服务网关
Zuul
面试题:
zuul和gateway的区别
tomcat线程池 连接个数是有限的
zuul1.0使用了阻塞模型
zuul2未集成spring cloud
Gateway使用了异步的非阻塞模型,使用到了spring5的webflux异步非阻塞框架
Gateway
Gateway属于Spring Cloud开发的网关组件,Gateway本来就是服务网关的意思,不仅仅在spring cloud中。
Spring Cloud Gateway使用的Webflux中的reactor-netty响应式编程组件,底层使用了netty通讯框架
Spring Cloud自制作的Gateway服务网关组件集成了Spring boot2.x+Spring5 Web Flux+Project Reactor非阻塞异步模型。
Servlet3.1 非阻塞异步模型
能干嘛
反向代理
鉴权
流量控制
熔断
日志监控
微服务架构中的网关在哪里
Gateway的三大核心概念
Route路由
路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由
Predicate断言
参考的是java8的java.util.function.Predicate开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
Filter过滤
指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改
Gateway的工作流程
例如:使用了服务网关之后,我们就可以统一的管理客户端访问,不论是负载均衡还是对访问客户端之前做一些操作,比如说对其进行身份验证等等,拦截各种请求,然后将其进行更细精度的转发。这就是网关的作用,也是网关的三个核心要素:路由、断言和过滤。
总结起来就是一句话:核心的逻辑就是路由转发+执行过滤器链
使用
- 建项目
改pom
gateway的核心依赖就是spring -cloud - starter-gateway,spring cloud gateway不是netflix公司的,是spring cloud社区自己出的
<!--新增gateway--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency>
写yml
server: port: 9527 spring: application: name: cloud-gateway cloud: # 网关配置 gateway: #路由配置:转发的规则 routes: - id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名 uri: http://localhost:8001 #匹配后提供服务的路由地址 转发的地址 predicates: - Path=/payment/get/** #断言,路径相匹配的进行路由 - id: payment_routh2 uri: http://localhost:8001 predicates: - Path=/payment/lb/** #断言,路径相匹配的进行路由
eureka: instance: hostname: cloud-gateway-service client: service-url: register-with-eureka: true fetch-registry: true defaultZone: http://eureka7001.com:7001/eureka
- 主启动
```java
@EnableEurekaClient
@SpringBootApplication
public class Gateway9527 {
public static void main(String[] args) {
SpringApplication.run(Gateway9527.class, args);
}
}
路由的配置作用就是当前访问http://localhost:9527/payment/lb/ 下的任意路径和http://localhost:9527/payment/get/ 下的任意路径就将其转发路由到对应的 8001端口,这时就可以达到进地铁之前要过安检一样,只有过了匹配到了才能转发到对应的目的地。
测试正常
bug
如果在网关服务模块启动时候报错,那么请移除掉模板中的web和actor监控服务的依赖
能不能通过9517访问到8001,然后因为这时判断8001下面有没有/payment/get/断言匹配到了这个匹配规则这时为true,就访问到当前对应的8001中的接口,在8001外面套了一层外壳。
Gateway网关路由配置的两种方式
- 在配置文件yml文件中配置
代码中注入RouteLocator的Bean
需求分析:根据配置访问到百度
@Configuration public class GatewayConfig { @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { //这个routes 就是我们配置中的routes 可以配置多个 uri 跟 断言规则 RouteLocatorBuilder.Builder routes = builder.routes(); //两个参数 id,然后一个四大函数型接口 function接口 有输入和输出 routes.route("path_route_custom", //这个意思就是访问 http://localhost:9527/guonei 然后跳转到了百度的新闻页面 r -> r.path("/guonei") .uri("http://news.baidu.com/guonei))")).build(); return routes.build(); } }
如果在网关中出现有错误:
这个意思就是路由中没有相关的匹配规则
通过微服务名称实现动态路由
此时对外暴露得就是网关服务了
现在使用网关进行负载均衡
默认情况下Gateway会根据注册中心注册得服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能。
开启两个服务提供者8001、8002
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
# 静态路由配置
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
# # 动态路由配置
- id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service # 服务名称动态配置路由
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service # 服务名称动态配置路由
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
eureka:
instance:
hostname: cloud-gateway-service
client:
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
微服务名称配置
# 微服务名称配置
discovery:
locator:
enabled: true # 设置为true 请求路径前可以添加微服务名称
lower-case-service-id: true # 允许为小写
一般使用服务名称进行访问,在访问的路径前加上微服务名称
好像只能是小写了
Predicate的使用
是啥
查看启动控制台
配置发现routs,路由的配置是多个,每一个id就是一个路由配置,但是predicates 也是多个,而且我们只使用到了-Path 这个路由的配置。其中这个RouteLocator还有很多种 精确度匹配
这个请求的条件的作用一般用于系统升级:在凌晨的几点之后,After之后将某个请求切换到一个新的路由上去新的访问路径,这时就完成了旧系统到新系统的升级过程。
这个factory的实现类有很多个
cloud:
gateway:
# 静态路由配置
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
lower-case-service-id: true # 允许为小写
routes:
# # 动态路由配置
- id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service # 服务名称动态配置路由
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service # 服务名称动态配置路由
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
# 在哪个时间之后这个路由匹配生效
- After=2020-11-25T16:35:13.702+08:00[Asia/Shanghai]
public class ZonedDateTimeTest {
public static void main(String[] args) {
//默认时区
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println(zonedDateTime);
//使用指定的时区来获取到当前时间
ZonedDateTime time = ZonedDateTime.now(ZoneId.of("America/New_York"));
System.out.println(time);
//2020-11-25T16:37:18.284+08:00[Asia/Shanghai]
// 2020-11-25T03:37:18.289-05:00[America/New_York]
}
}
这里是获取到默认的时区,java中的新特性
用于项目迭代
无非就是加上一些条件,这些条件都匹配之后才可以访问到这个路径。这就是断言的作用。
- After=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
- Before=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
- Between=2020-03-08T10:59:34.102+08:00[Asia/Shanghai] , 2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
加上了curl访问中文乱码的问题解决:https://blog.csdn.net/leedee/article/details/82685636
Cookie=username, zhangsan #并且Cookie是username=zhangsan 才能访问
- Header=X-Request-Id, \d+ #请求头中要有X-Request-Id属性并且值为整数的正则表达式
- Host=**.xxxx.com
- Method=GET
- Query=username, \d+ #要有参数名称并且是正整数才能路由
server:
port: 9527
spring:
application:
name: cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
routes:
- id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/payment/get/** #断言,路径相匹配的进行路由
- id: payment_routh2
#uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
#- After=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
#- Cookie=username,zhangshuai #并且Cookie是username=zhangshuai才能访问
#- Header=X-Request-Id, \d+ #请求头中要有X-Request-Id属性并且值为整数的正则表达式
#- Host=**.xxx.com
#- Method=GET
#- Query=username, \d+ #要有参数名称并且是正整数才能路由
eureka:
instance:
hostname: cloud-gateway-service
client:
service-url:
register-with-eureka: true
fetch-registry: true
defaultZone: http://eureka7001.com:7001/eureka
说白了,Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理
设置在xx时间后才可以路由到
- id: payment_routh2
# uri: http://localhost:8001 #匹配后提供服务的路由地址
uri: lb://cloud-payment-service # 服务名称动态配置路由
predicates:
- Path=/payment/lb/** #断言,路径相匹配的进行路由
# 在哪个时间之后这个路由匹配生效
- After=2020-11-25T18:10:00.702+08:00[Asia/Shanghai]
好像没有乱用啊
Filter的使用
过滤器
Spring Cloud Gateway的Filter
生命周期,Only Two
- pre 之前
- post 之后
种类,Only Two
- Gateway Filter 单一的 31种
- GlobalFilter 全局的 10多种
常用的Gateway Filter
自定义过滤器
主要使用两个接口
@Configuration
@Slf4j
public class MyLogGatewayFilter implements GlobalFilter, Ordered {
//一个是全局的配置 一个是优先级 Order
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info(" come in MyLogGatewayFilter: " + new Date());
String uname = exchange.getRequest().getQueryParams().getFirst("uname");
if (uname==null) {
log.info(" 用户名为null,非法用户!");
//给它响应结果一个返回值 HttpStatus.NO_CONTENT
exchange.getResponse().setStatusCode(HttpStatus.NO_CONTENT);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
// 加载顺序 一般数据越小,过滤器的优先级越高
@Override
public int getOrder() {
return 0;
}
}
查看请求中是否有uname 用户名,如果不带就不然你进来。
过滤器身份验证
public Result login(String phone,String password){
//调用业务层完成用户登录
User user = userService.login(phone, password);
if(user==null){
return new Result(false,"手机号或密码错误");
}
//生成身份令牌 JWT
String token = UUID.randomUUID().toString().replaceAll("-","");
//将token信息保存到redis 7天后过期
redisTemplate.boundValueOps(token).set(1,7, TimeUnit.DAYS);
return new Result(true,"登录成功",token);
}