Spring Cloud Gateway
一、介绍
1,概念
SpringCloud Gateway是在Spring生态系统之上构建的API网关服务,基于Spring5、Spring Boot2和Project Reactor等技术。SpringCloud Gateway作为Spring Cloud生态系统中的网关,目标是替代Zuul。SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则是使用了高性能的Reactor模型通信框架Netty。
SpringCloud Gateway的目标是提供统一的路由方式且基于Filter链的方式提供网关基本的功能,例如:安全、监控/指标和限流。
2,核心
Route(路由)
网关配置的基本组成模块,和Zuul的路由配置模块类似。一个Route模块由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配,目标URI会被访问。
Predicate(断言)
这是一个 Java 8 的 Predicate,可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。断言的输入类型是一个 ServerWebExchange。
Filter(过滤)
和Zuul的过滤器在概念上类似,可以使用它拦截和修改请求,并且对上游的响应,进行二次处理。过滤器为org.springframework.cloud.gateway.filter.GatewayFilter类的实例。
二、案例使用
通用pom
<!--新增gateway--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> <version>2.2.1.RELEASE</version> </dependency>
启动类
@SpringBootApplication @EnableEurekaClient public class GateWayMain9527 { public static void main(String[] args) { SpringApplication.run( GateWayMain9527.class,args); } }
1,采用yaml配置
server: port: 9527 spring: application: name: cloud-gateway cloud: gateway: routes: - id: payment_routh #路由的ID,没有固定规则但要求唯一,建议配合服务名 uri: http://localhost:8001 #匹配后提供服务的路由地址 predicates: - Path=/payment/get1/** #断言,路径相匹配的进行路由 - id: payment_routh2 uri: http://localhost:8001 predicates: - Path=/payment/lb/** #断言,路径相匹配的进行路由 #注入到eureka eureka: instance: hostname: cloud-gateway-service client: service-url: register-with-eureka: true fetch-registry: true defaultZone: http://eureka7001.com:7001/eureka
测试:
http://localhost:8001/payment/get/1 直接访问
http://localhost:9527/payment/get/1 通过gateway路由访问
2,代码(注入RouteLocator)
@Configuration public class GatewayConfig { @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) { RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes(); //routeId:path_rote_guonei http://localhost:9527/guonei ==> http://news.baidu.com/guoji routes.route("path_rote_guonei", r -> r.path("/guonei").uri("http://news.baidu.com/guoji")) .route("path_rote_guoji", r -> r.path("/guoji").uri("http://news.baidu.com/guoji")).build(); return routes.build(); } }
3,动态路由
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 #对应eureka中微服务的示例名称 predicates: - Path=/payment/get/** #断言,路径相匹配的进行路由 # filters: # - PrefixPath=/payment #去掉指定的前缀 例如:原始访问 http://localhost:9527/payment/get/1 现在只需要输入网址 http://localhost:9527/get/1 # - StripPrefix=1 #去掉路径中的一段 当为1是去掉1个 例如:原始访问 http://localhost:9527/payment/get/1 现在需要输入网址(/123会被去除) http://localhost:9527/123/payment/get/1 - id: payment_routh2 #uri: http://localhost:8001 #匹配后提供服务的路由地址 uri: lb://cloud-payment-service predicates: - Path=/payment/lb/** #断言,路径相匹配的进行路由
三、Predicate与Filter
1,Predicate
predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理。
spring: application: name: cloud-gateway cloud: gateway: discovery: locator: enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由 routes: - 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] #After在什么时间之后 Before在什么时间之前 #- Between=2020-03-08T10:59:34.102+08:00[Asia/Shanghai] , 2020-03-08T10:59:34.102+08:00[Asia/Shanghai] #在什么时间之间 #- Cookie=username,xcc #并且Cookie是username=xcc才能访问 #- Header=X-Request-Id, \d+ #请求头中要有X-Request-Id属性并且值为整数的正则表达式 #- Host=**.xcc.com #- Method=GET #- Query=username, \d+ #要有参数名称并且是正整数才能路由
2,Filter
/** * 自定义filter,过滤掉非法的请求 */ @Slf4j @Component public class MyLogGateWayFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { log.info("***************com in MyLogGateWayFilter: "+ LocalDateTime.now()); String uname = exchange.getRequest().getQueryParams().getFirst("uname"); if (StrUtil.isEmpty(uname)) { log.info("**************用户名为Null,非法用户****************"); exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE); //设置状态码 return exchange.getResponse().setComplete(); } return chain.filter(exchange); } @Override public int getOrder() { return 0; //order值越小则优先级越高 ; 值越大则优先级越低 } }
测试
错误请求: http://localhost:9527/payment/get/1 #***************com in MyLogGateWayFilter: 2020-10-12T17:06:00.169 #**************用户名为Null,非法用户**************** 正确请求: http://localhost:9527/payment/get/1?uname=xcc