网关作为流量的入口常用功能包括路由转发,权限校验,限流控制等
官网
当前gateway
https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.2.RELEASE/reference/html/
是什么源码架构
能干嘛
反向代理
鉴权
流量控制
熔断
日志监控
微服务架构中网关在哪里
有zuul了怎么又出来了gateway
我们为什么选择Gateway
SpringCloud Gateway具有如下特性Gateway模型
WebFlux是什么
Route(路由)
路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由
Predicate(断言)
参考java8的java.util.function.Predicate开发人员可以匹配HTTP请求中的所有内容(例如请求头和请求参数),如果请求与断言相匹配则进行路由
Filter(过滤)
指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或之后对请求进行修改
总体
官网总结核心逻辑
路由转发+执行过滤器链请求到网关首先会根据断言判断是否符合路由规则,如果符合就会按照路由规则路由到指定地方,在路由到指定地方之前会有一系列filter进行过滤
新建Module
wms-gateway-9527
POM
org.springframework.cloud spring-cloud-starter-gateway
wms com.hk.wms 1.0-SNAPSHOT 4.0.0 wms-gateway-9527 org.springframework.cloud spring-cloud-starter-gateway com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-devtools runtime true org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test 注意:spring-cloud-starter-gateway包含有spring-boot-starter-web,和spring-boot-starter-webflux,如果发生冲突会报错误
Consider defining a bean of type 'org.springframework.http.codec.ServerCodec
解决方案1,去除依赖 spring-boot-starter-web和spring-boot-starter-webflux
解决方案2:排除spring-cloud-starter-gateway的依赖,
org.springframework.cloud spring-cloud-starter-gateway org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-webflux 本例未使用 spring-boot-starter-webflux,所以不用排除
YML
server: port: 9527 spring: application: name: wms-gateway cloud: nacos: discovery: server-addr: 192.168.1.131:8848 #配置Nacos地址 #server-addr: 192.168.1.131:1111 #使用nginx负载均衡注册服务,1111是nginx设置的监听端口号 gateway: discovery: locator: enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由 routes: - id: service_route # 路由的id,没有规定规则但要求唯一,建议配合服务名 #匹配后提供服务的路由地址 uri: http://localhost:8401 predicates: - Path=/testE/** # 断言,路径相匹配的进行路由 #- After=2017-01-20T17:42:47.789-07:00[America/Denver] #- Before=2017-01-20T17:42:47.789-07:00[America/Denver] #- Cookie=username,zzyy #- Header=X-Request-Id, \d+ #请求头要有X-Request-Id属性,并且值为正数 #- Host=**.atguigu.com #- Method=GET #- Query=username, \d+ # 要有参数名username并且值还要是正整数才能路由 # 过滤 #filters: # - AddRequestHeader=X-Request-red, blue - id: service_route2 uri: http://localhost:8401 predicates: Path=/testF/** #断言,路径相匹配的进行路由 management: endpoints: web: exposure: include: '*'
业务类
无
主启动类package com.hk.wms.wmsgateway9527; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableDiscoveryClient public class WmsGateway9527Application { public static void main(String[] args) { SpringApplication.run(WmsGateway9527Application.class, args); } }
9527网关如何做路由映射呢?wms-sentinel-service8401 看看controller的访问地址
@GetMapping("/testE/{id}") @SentinelResource(value="testE",blockHandlerClass = CustomerBlockHandler.class,blockHandler = "customBlockHandler2",fallback = "handlerFallBack") public String getStrController(@PathVariable("id")int id){ String str=feignService.getString(id); if(id<0){ throw new IllegalArgumentException("IllegalArgument ,非法参数异常..."); } if(str==null){ throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常"); } return str; } @GetMapping("/testF/{id}") @SentinelResource(value="testF",blockHandlerClass = CustomerBlockHandler.class,blockHandler = "customBlockHandler1",fallback = "handlerFallBack") public String CircleBreakerController(@PathVariable("id")int id){ String str=template.getForObject(serverUrl+"/getStr/"+id,String.class); if(id<0){ throw new IllegalArgumentException("IllegalArgument ,非法参数异常..."); } if(str==null){ throw new NullPointerException("NullPointerException,该ID没有对应记录,空指针异常"); } return str; }
我们目前不想暴露8401端口,希望在8401外面套一层9527
YML新增网关配置spring: cloud: gateway: discovery: locator: enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由 routes: - id: service_route # 路由的id,没有规定规则但要求唯一,建议配合服务名 #匹配后提供服务的路由地址 uri: http://localhost:8401 predicates: - Path=/testE/** # 断言,路径相匹配的进行路由 #- After=2017-01-20T17:42:47.789-07:00[America/Denver] #- Before=2017-01-20T17:42:47.789-07:00[America/Denver] #- Cookie=username,zzyy #- Header=X-Request-Id, \d+ #请求头要有X-Request-Id属性,并且值为正数 #- Host=**.atguigu.com #- Method=GET #- Query=username, \d+ # 要有参数名username并且值还要是正整数才能路由 # 过滤 #filters: # - AddRequestHeader=X-Request-red, blue - id: service_route2 uri: http://localhost:8401 predicates: Path=/testF/** #断言,路径相匹配的进行路由
YML配置说明
Gateway网关路由有两种配置方式:
在配置文件yml中配置代码中注入RouteLocator的Bean
百度国内新闻网站,需要外网
通过9527网关访问到外网的百度新闻网址
编码package com.hk.wms.wmsgateway9527.config; import org.springframework.cloud.gateway.route.RouteLocator; import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class GatewayConfig { /** * 配置了一个id为route-name的路由规则 * 当访问地址 http://localhost:9527/guonei时会自动转发到地址: http://news.baidu.com/guonei * path是断言相当于在yml中配置 * predicates: * Path=/lady * * @param builder * @return */ @Bean public RouteLocator getRouterLocator(RouteLocatorBuilder builder){ RouteLocatorBuilder.Builder routes = builder.routes(); routes.route("path_route_eiletxie", r -> r.path("/guonei") .uri("http://news.baidu.com")).build(); return routes.build(); } @Bean public RouteLocator getRouteLocator2(RouteLocatorBuilder builder) { RouteLocatorBuilder.Builder routes = builder.routes(); routes.route("path_route_eiletxie2", r -> r.path("/guoji") .uri("http://news.baidu.com")).build(); return routes.build(); } }
相当于在yml中配置
spring: cloud: gateway: discovery: locator: enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由 routes: - id: path_route_eiletxie # 路由的id,没有规定规则但要求唯一,建议配合服务名 #匹配后提供服务的路由地址 uri: http://news.baidu.com predicates: - Path=/guonei # 断言,路径相匹配的进行路由 - id: path_route_eiletxie uri: http://news.baidu.com predicates: Path=/guoji #断言,路径相匹配的进行路由
默认情况下Gateway会根据注册中心的服务列表,以注册中心上微服务名为路径创建动态路由进行转发,从而实现动态路由的功能
YMLserver: port: 9527 spring: application: name: wms-gateway cloud: nacos: discovery: server-addr: 192.168.1.131:8848 #配置Nacos地址 #server-addr: 192.168.1.131:1111 #使用nginx负载均衡注册服务,1111是nginx设置的监听端口号 gateway: discovery: locator: enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由 routes: - id: service_route # 路由的id,没有规定规则但要求唯一,建议配合服务名 #匹配后提供服务的路由地址 uri: http://localhost:8401 predicates: - Path=/testE/** # 断言,路径相匹配的进行路由 #- After=2017-01-20T17:42:47.789-07:00[America/Denver] #- Before=2017-01-20T17:42:47.789-07:00[America/Denver] #- Cookie=username,zzyy #- Header=X-Request-Id, \d+ #请求头要有X-Request-Id属性,并且值为正数 #- Host=**.atguigu.com #- Method=GET #- Query=username, \d+ # 要有参数名username并且值还要是正整数才能路由 # 过滤 #filters: # - AddRequestHeader=X-Request-red, blue - id: service_route2 uri: http://localhost:8401 predicates: Path=/testF/** #断言,路径相匹配的进行路由 - id: service_route3 uri: lb://wms-provider predicates: - Path=/provider/** - id: service_route4 uri: lb://wms-provider predicates: - Path=/getStr/** - id: service_route4 uri: lb://wms-provider predicates: - Path=/getStr2/** management: endpoints: web: exposure: include: '*'
spring: cloud: gateway: discovery: locator: enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称进行路由 routes: - id: service_route # 路由的id,没有规定规则但要求唯一,建议配合服务名 #匹配后提供服务的路由地址 uri: http://localhost:8401 predicates: - Path=/testE/** # 断言,路径相匹配的进行路由 - id: service_route2 uri: http://localhost:8401 predicates: Path=/testF/** #断言,路径相匹配的进行路由 - id: service_route3 uri: lb://wms-provider predicates: - Path=/provider/** - id: service_route4 uri: lb://wms-provider predicates: - Path=/getStr/** - id: service_route4 uri: lb://wms-provider predicates: - Path=/getStr2/**
需要注意的是uri的协议为lb,表示启用Gateway的负载均衡功能
lb://serviceName是springcloud gateway在微服务中自动为我们创建的负载均衡urispring.cloud.gateway.discovery.locator.enabled=true:开启从注册中心动态创建路由的功能,利用微服务名称进行路由
是什么
启动我们的gateway9527
Route Predicate Factories 这个是什么东东?
常用的Route Predicate
1、After Route Predicate
YML- id: payment_route2 #uri: http://localhost:8001 uri: lb://cloud-payment-service predicates: - Path=/payment/lb/** #断言,路径相匹配的进行路由 - After=2020-03-12T16:44:15.064+08:00[Asia/Shanghai]
设置当前时间再加1小时进行测试,当测试请求的时间小于设定时间时就会报错,因为设置了 必须在 AFTER 某某时间以后请求才有效
2、Before Route Predicate
3、Between Route Predicate
YML
4、Cookie Route Predicate 匹配cookieyml
- id: payment_route2 #uri: http://localhost:8001 uri: lb://cloud-payment-service predicates: - Path=/payment/lb/** #断言,路径相匹配的进行路由 - After=2020-03-12T15:44:15.064+08:00[Asia/Shanghai] - Cookie=username,eiletxie #cookie的key是username,cookie的value是eiletxie - id: service_route3 uri: lb://wms-provider predicates: - Path=/provider/** - Cookie=username,yangdong #只有加上Cookie才能访问,Cookie的key是username,Cookie的value是yangd
不带cookies访问
curl http://localhost:9527/provider/1 报404错误C:\Users\hk>curl http://localhost:9527/provider/1 {"timestamp":"2020-05-21T05:39:14.625+0000","path":"/provider/1","status":404,"error":"Not Found","message":null,"requestId":"bf2a25f5","trace":"org.springframework.web.server.ResponseStatusException: 404 NOT_FOUND\r\n\tat org.springframework.web.reactive.resource.ResourceWebHandler.lambda$handle$0(ResourceWebHandler.java:325)\r\n\tSuppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: \nError has been observed at the following site(s):\n\t|_ checkpoint 鈬?org.springframework.cloud.gateway.filter.WeightCalculatorWebFilter [DefaultWebFilterChain]\n\t|_ checkpoint 鈬?org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain]\n\t|_ checkpoint 鈬?HTTP GET \"/provider/1\" [ExceptionHandlingWebHandler]\nStack trace:\r\n\t\tat org.springframework.web.reactive.resource.ResourceWebHandler.lambda$handle$0(ResourceWebHandler.java:325)\r\n\t\tat reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:44)\r\n\t\tat reactor.core.publisher.Mono.subscribe(Mono.java:4105)\r\n\t\tat reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onComplete(FluxSwitchIfEmpty.java:75)\r\n\t\tat reactor.core.publisher.MonoFlatMap$FlatMapMain.onComplete(MonoFlatMap.java:174)\r\n\t\tat reactor.core.publisher.MonoNext$NextSubscriber.onComplete(MonoNext.java:96)\r\n\t\tat reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:359)\r\n\t\tat reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onSubscribe(FluxConcatMap.java:211)\r\n\t\tat reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:139)\r\n\t\tat reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:63)\r\n\t\tat reactor.core.publisher.Mono.subscribe(Mono.java:4105)\r\n\t\tat reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.drain(MonoIgnoreThen.java:172)\r\n\t\tat reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:56)\r\n\t\tat reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:150)\r\n\t\tat reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:67)\r\n\t\tat reactor.core.publisher.MonoNext$NextSubscriber.onNext(MonoNext.java:76)\r\n\t\tat reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.innerNext(FluxConcatMap.java:274)\r\n\t\tat reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:851)\r\n\t\tat reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onNext(FluxMapFuseable.java:121)\r\n\t\tat reactor.core.publisher.Operators$ScalarSubscription.request(Operators.java:2186)\r\n\t\tat reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.request(FluxMapFuseable.java:162)\r\n\t\tat reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:1994)\r\n\t\tat reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:1868)\r\n\t\tat reactor.core.publisher.FluxMapFuseable$MapFuseableSubscriber.onSubscribe(FluxMapFuseable.java:90)\r\n\t\tat reactor.core.publisher.MonoJust.subscribe(MonoJust.java:54)\r\n\t\tat reactor.core.publisher.Mono.subscribe(Mono.java:4105)\r\n\t\tat reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:441)\r\n\t\tat reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onSubscribe(FluxConcatMap.java:211)\r\n\t\tat reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:139)\r\n\t\tat reactor.core.publisher.FluxIterable.subscribe(FluxIterable.java:63)\r\n\t\tat reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:55)\r\n\t\tat reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)\r\n\t\tat reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:55)\r\n\t\tat reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)\r\n\t\tat reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:55)\r\n\t\tat reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52)\r\n\t\tat reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:55)\r\n\t\tat reactor.cocurl: (23) Failed writing body (4096 != 7668)
带上cookies访问
curl http://localhost:9527/provider/1 --cookie "username=yangdong"C:\Users\hk>curl http://localhost:9527/provider/1 --cookie "username=yangdong" 9002:1 C:\Users\hk>curl http://localhost:9527/provider/1 --cookie "username=yangdong" 9003:1:this is wms-provider-dev.yaml C:\Users\hk>curl http://localhost:9527/provider/1 --cookie "username=yangdong" 9002:1 C:\Users\hk>curl http://localhost:9527/provider/1 --cookie "username=yangdong" 9003:1:this is wms-provider-dev.yaml
5、 Header Route Predicate 匹配请求头YML
- id: payment_route2 #uri: http://localhost:8001 uri: lb://cloud-payment-service predicates: - Path=/payment/lb/** #断言,路径相匹配的进行路由 - After=2020-03-12T15:44:15.064+08:00[Asia/Shanghai] #- Cookie=username,eiletxie #带Cookie,并且username的值为eiletxie - Header=X-Request-Id,\d+ #请求头要有 X-Request-Id属性并且值为整数的正则表达式
带上head请求头访问
curl http://localhost:9527/payment/lb -H "X-Request-Id:123"
6、 Host Route Predicate
7、 Method Route Predicate 匹配请求方式
9、 Query Route Predic 匹配请求路径上的参数ate- id: baidu uri: https://www.baidu.com predicates: - Query=url,baidu - id: qq uri: https://www.qq.com predicates: - Query=url,q.
http://localhost:9527/?url=qq访问腾讯主页
http://localhost:9527/?url=baidu 访问百度主页
10、 小总结
ALL
生命周期,
pre
post
种类
GatewayFilter
常用的GatewayFilter AddRequestParameter
YML
自定义过滤器
自定义全局GlobalFilter
implements GlobalFilter,Ordered说明网关的filter和servlet的filter无关package com.hk.wms.wmsgateway9527.config; import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.Ordered; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Mono; @Component public class MyLogGatewayFilter implements GlobalFilter, Ordered { Logger logger= LoggerFactory.getLogger(getClass()); @Override public Mono
filter(ServerWebExchange exchange, GatewayFilterChain chain) { logger.info("进入filter"); String uname = exchange.getRequest().getQueryParams().getFirst("uname"); if(StringUtils.isEmpty(uname)){ logger.info("*****用户名为null,非法用户,o(╥﹏╥)o"); exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } @Override /** * 加载过滤器的顺序,数字越小,优先级越高 */ public int getOrder() { return 0; } } 作用
全局日志记录
统一网关鉴权
C:\Users\hk>curl http://localhost:9527/provider/1 --cookie "username=yangdong" #没有添加参数 2020-05-21 14:54:12.328 INFO 4588 --- [ctor-http-nio-2] c.h.w.w.config.MyLogGatewayFilter : 进入filter 2020-05-21 14:54:12.330 INFO 4588 --- [ctor-http-nio-2] c.h.w.w.config.MyLogGatewayFilter : *****用户名为null,非法用户,o(╥﹏╥)o C:\Users\hk>curl http://localhost:9527/provider/1?uname=lily --cookie "username=yangdong" 9002:1 C:\Users\hk>curl http://localhost:9527/provider/1?uname=lily --cookie "username=yangdong" 9003:1:this is wms-provider-dev.yaml C:\Users\hk>curl http://localhost:9527/provider/1?uname=lily --cookie "username=yangdong" 9002:1 C:\Users\hk>curl http://localhost:9527/provider/1?uname=lily --cookie "username=yangdong" 9003:1:this is wms-provider-dev.yaml 2020-05-21 14:56:21.315 INFO 4588 --- [ctor-http-nio-5] c.h.w.w.config.MyLogGatewayFilter : 进入filter 2020-05-21 14:56:29.195 INFO 4588 --- [ctor-http-nio-6] c.h.w.w.config.MyLogGatewayFilter : 进入filter 2020-05-21 14:56:33.585 INFO 4588 --- [ctor-http-nio-7] c.h.w.w.config.MyLogGatewayFilter : 进入filter