14.服务网关Zuul和Gateway
zuul分为两个版本,zuul1已经提更,zuul2还没开发好!
重点学习gateway!
cloud全家桶中有个很重要的组件就是网管,在1.x版本中采用的时Zuul网关
但在2.x版本中,zuul的升级一致跳票,SpringCloud最后自己研发了一个网关代替Zuul
这就是Spring Cloud Gateway,一句话:gateway时原zuul1.x版的替代!
gateway是基于异步非阻塞模型上进行开发的,性能方面不需要担心!
zuul1是基于Servlet2.5使用阻塞架构上开发的,它不支持任何长连接(如WebSocket)
GateWay的三大核心概念:
1.Route(路由):路由是构建网关的基本模块,它是由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由
2.Predicate(断言):开发人员可以匹配HTTP请求中的所有内容(例如请求头或者请求参数),如果请求和断言相匹配则进行路由
3.Filter(过滤):指的是spring框架中GatewayFiletr实例,使用过滤器,可以在请求被路由前或者后对请求进行修改!
Gateway的核心逻辑就是:路由转发和执行过滤器链
代码构建:
gateway是一个单独的项目,去给其他cloud项目访问加上一层网关
1.pom文件中的内容:
<dependencies>
重点:gateway的jar包
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--eureka client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--一般基础通用配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cn.com.springcloud</groupId>
<artifactId>common</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
2.springboot启动类:
@SpringBootApplication
@EnableEurekaClient
public class SpringBootGateway9527 {
public static void main(String[] args) {
SpringApplication.run(SpringBootGateway9527.class,args);
}
}
3.springboot的配置文件写法:
server:
port: 9527
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#单机版
#defaultZone: http://localhost:7001/eureka
# 集群版
defaultZone: http://eureka7001:7001/eureka,http://eureka7002:7002/eureka
instance:
instance-id: cloud-gateway-service9527
prefer-ip-address: true
spring:
application:
name: cloud-gateway-service
#重点
cloud:
gateway:
routes:
- id: producer_queryUserById #路由的id,没有固定规则但要求唯一,建议符合服务名称
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
- Path=/producer/queryUserById/** #断言,服务端路径匹配的进行路由
- id: producer_getCurrentThread
uri: http://localhost:8001
predicates:
- Path=/producer/getCurrentThread/**
上述配置的意思是:
当访问的是gateway:http://localhost:9527/producer/queryUserById/7时,
满足上述的断言,此时会路由到http://localhost:8001//producer/queryUserById/7(路径拼接)
如果嫌需要给服务项目的每个请求方法都加上网关,可以这么写:
spring:
application:
name: cloud-gateway-service
cloud:
gateway:
routes:
- id: producer_queryUserById #路由的id,没有固定规则但要求唯一,建议符合服务名称
uri: http://localhost:8001 #匹配后提供服务的路由地址
predicates:
重点:服务项目的每个请求前都设置一个公共的路径/producer,然后根据此进行配置,这样就不需要单独每个方法都配置了!
- Path=/producer/** #断言,服务端路径匹配的进行路由
发现上述有问题:
1.服务提供端一般都是以集群形式部署,在配置文件中写死ip和端口不好
2.如何在网管处实现负载均衡呢?
在application.yam中配置如下:
server:
port: 9527
eureka:
client:
#表示是否将自己注册进EurekaServer默认为true。
register-with-eureka: true
#是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
fetchRegistry: true
service-url:
#单机版
#defaultZone: http://localhost:7001/eureka
# 集群版
defaultZone: http://eureka7001:7001/eureka,http://eureka7002:7002/eureka
instance:
instance-id: cloud-gateway-service9527
prefer-ip-address: true
spring:
application:
name: cloud-gateway-service
cloud:
gateway:
discovery:
locator:
#重点:加上该配置
enabled: true #开启从注册中心动态创建路由的功能,利用微服务名称进行路由
routes:
- id: producer_queryUserById #路由的id,没有固定规则但要求唯一,建议符合服务名称
#重点2:这里本身写服务提供端的地址,此时可以写服务注册在eureka注册中心的名称,lb:loadbalance(轮训的负载均衡)
uri: lb://CLOUD-PROVIDER #匹配后提供服务的路由地址
predicates:
- Path=/getPerson/** #断言,服务端路径匹配的进行路由
- id: producer_getCurrentThread
uri: lb://CLOUD-PROVIDER
predicates:
- Path=/producer/timesleep/**
测试:
此时若通过http://localhost:9527/getPerson/10007去访问时,发现是轮训的去访问服务集群!
predicates讲解
参考地址:https://blog.csdn.net/u012367513/article/details/86356708
predicates(断言):可以加很多种规则,目的就是让请求过来找到合适的Route进行处理
1. After Route Predicate Factory:使用的是时间作为匹配规则,只要当前时间大于设定时间,路由才会匹配请求。
spring:
cloud:
gateway:
routes:
- id: after_route
uri: http://www.google.com
predicates:
- After=2018-12-25T14:33:47.789+08:00
这个路由规则会在东8区的2018-12-25 14:33:47后,将请求都转跳到google。
2.Before Route Predicate Factory:使用时间作为匹配规则,只要当前时间小于设定时间,路由才会匹配请求。
spring:
cloud:
gateway:
routes:
- id: before_route
uri: http://www.google.com
predicates:
- Before=2018-12-25T14:33:47.789+08:00
这个路由规则会在东8区的2018-12-25 14:33:47前,将请求都转跳到google。
3.Between Route Predicate Factory:使用两个时间作为匹配规则,只要当前时间大于第一个设定时间,并小于第二个设定时间,路由才会匹配请求。
spring:
cloud:
gateway:
routes:
- id: between_route
uri: http://www.google.com
predicates:
- Between=2018-12-25T14:33:47.789+08:00, 2018-12-26T14:33:47.789+08:00
这个路由规则会在东8区的2018-12-25 14:33:47到2018-12-26 14:33:47之间,将请求都转跳到google。
4. Cookie Route Predicate Factory:使用的是cookie名字和正则表达式的value作为两个输入参数,请求的cookie需要匹配cookie名和符合其中value的正则。
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://www.google.com
predicates:
- Cookie=cookiename, cookievalue
路由匹配请求存在cookie名为cookiename,cookie内容匹配cookievalue的,将请求转发到google。
5.Header Route Predicate Factory:与Cookie Route Predicate Factory类似,也是两个参数,一个header的name,一个是正则匹配的value。
spring:
cloud:
gateway:
routes:
- id: header_route
uri: http://www.google.com
predicates:
- Header=X-Request-Id, \d+
路由匹配存在名为X-Request-Id,内容为数字的header的请求,将请求转发到google。
6. Host Route Predicate Factory:使用的是host的列表作为参数,host使用Ant style匹配。
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://www.google.com
predicates:
- Host=**.somehost.org,**.anotherhost.org
路由会匹配Host诸如:www.somehost.org 或 beta.somehost.org或www.anotherhost.org等请求。
7. Method Route Predicate Factory:是通过HTTP的method来匹配路由。
spring:
cloud:
gateway:
routes:
- id: method_route
uri: http://www.google.com
predicates:
- Method=GET
路由会匹配到所有GET方法的请求。
8.Path Route Predicate Factory:使用的是path列表作为参数,使用Spring的PathMatcher匹配path,可以设置可选变量。
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://www.google.com
predicates:
- Path=/foo/{segment},/bar/{segment}
上面路由可以匹配诸如:/foo/1 或 /foo/bar 或 /bar/baz等
其中的segment变量可以通过下面方式获取:
PathMatchInfo variables = exchange.getAttribute(URI_TEMPLATE_VARIABLES_ATTRIBUTE);
Map<String, String> uriVariables = variables.getUriVariables();
String segment = uriVariables.get("segment");
在后续的GatewayFilter Factories就可以做对应的操作了。
9.Query Route Predicate Factory:可以通过一个或两个参数来匹配路由,一个是查询的name,一个是查询的正则value。
spring:
cloud:
gateway:
routes:
- id: query_route
uri: http://www.google.com
predicates:
- Query=baz
路由会匹配所有包含baz查询参数的请求。
spring:
cloud:
gateway:
routes:
- id: query_route
uri: http://www.google.com
predicates:
- Query=foo, ba.
路由会匹配所有包含baz,并且baz的内容为诸如:bar或baz等符合ba.正则规则的请求。
10.RemoteAddr Route Predicate Factory:通过无类别域间路由(IPv4 or IPv6)列表匹配路由。
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: http://www.google.com
predicates:
- RemoteAddr=192.168.1.1/24
上面路由就会匹配RemoteAddr诸如192.168.1.10等请求。
11.Modifying the way remote addresses are resolved
Filter:过滤器
分类:
1.GatewayFilter
2.GlobalFilter
3.上述两种的配置种太多,不如自己去实现代码来的方便
自定义的过滤器:
1.两个主要的接口:
2.能干嘛;
2.1全局日志记录
2.2统一通关鉴权
样例:创建自己的过滤器,加上@Component注解将其加载到容器中
@Component
@Slf4j
重点1:实现两个接口:GlobalFilter和Ordered(用来控制该过滤器的执行顺序,数值越小,越先执行!)
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
重点2:hutool工具包的使用
log.info("*****gateWay的自定义过滤器:时间:"+ DateUtil.formatDateTime(new Date()));
重点3:获取请求中的uname属性值
String uanme=exchange.getRequest().getQueryParams().getFirst("uname");
if(uanme == null){
log.info("********用户名为null,非法用户,/(ㄒoㄒ)/~~");
重点4:设置返回的状态码:406
exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
重点5:表示消息处理完毕,可以进行结束
return exchange.getResponse().setComplete();
}
重点6:该该条过滤的ServerWebExchange 对象传入到过滤链中的下一个过滤环节!
return chain.filter(exchange);
}
重点7:过滤的执行顺序,数字越小,越先执行!
@Override
public int getOrder() {
return 0;
}
}