Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,取代Zuul网关。网关作为流量的,在微服务系统中有着非常作用,网关常见的功能有路由转发、权限校验、限流控制等作用。

  Spring Cloud Gateway是Spring官方最新推出的一款基于Spring Framework 5,Project Reactor和Spring Boot 2之上开发的网关。与zuul1.0不同的是,gateway是异步非阻塞的(netty+webflux实现),zuul1.0是同步阻塞请求的。gateway的数据是封装在ServerWebExchange中,zuul是存放在RequestContext里的(这里是重点,圈起来!)Gateway相对于Zuul来说,在路由的配置上更加多样化,配置更加简便。

  官方文档 : https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#gateway-starter

Gateway 核心概念:

  1. Route 路由,它是网关的基础元素,包含ID、目标URI、断言、过滤器组成,当前请求到达网关时,会通过Gateway Handler Mapping,基于断言进行路由匹配,当断言为true时,匹配到路由进行转发

  2. Predicate,断言,学过java8的同学应该知道这个函数,它可以允许开发人员去匹配HTTP请求中的元素,一旦匹配为true,则表示匹配到合适的路由进行转发

  3. Filter,过滤器,可以在请求发出的前后进行一些业务上的处理,比如授权、埋点、限流等。

Gateway 工作模型:

  其中,predicate就是我们的匹配条件;而filter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了。客户端向 Spring Cloud Gateway 发出请求,如果请求与网关程序定义的路由匹配,则该请求就会被发送到网关 Web 处理程序,此时处理程序运行特定的请求过滤器链。过滤器之间用虚线分开的原因是过滤器可能会在发送代理请求的前后执行逻辑。所有 pre 过滤器逻辑先执行,然后执行代理请求;代理请求完成后,执行 post 过滤器逻辑。

Predicate 路由断言:

  Spring Cloud Gateway将路由匹配为Spring WebFlux HandlerMapping基础设施的一部分。Spring Cloud Gateway包括许多内置的路由谓词工厂。所有这些谓词都匹配HTTP请求的不同属性。您可以使用逻辑和语句组合多个路由谓词工厂。

  Gateway 的路由断言机制以 AbstractRoutePredicateFactory 为基础,实现了如下多种方式。再官方文档内提供了配置说明

Gateway 网关的实现:

  要在项目中引入Spring Cloud Gateway,引入相关依赖,然后只需要一些简单的配置即可构建好一个 Gateway 网关服务。

1.引入依赖(基于 spring-cloud-dependencies  Hoxton.SR4 版本)

  1. <dependency>
  2. <groupId>org.springframework.cloud</groupId>
  3. <artifactId>spring-cloud-starter-gateway</artifactId>
  4. </dependency>

2. 以 PathRoutePredicateFactory 路由断言为例子演示,做以下配置:

  1. server:
  2. port: 9544
  3.  
  4. spring:
  5. application:
  6. name: gateway-service
  7. cloud:
  8. gateway:
  9. enabled: true
  10. discovery:
  11. locator:
  12. enabled: false #gateway开启服务注册和发现的功能
  13. lowerCaseServiceId: true #请求路径上的服务名配置为小写
  14. routes:
  15. - id: ribbon-server
  16. uri: lb://RIBBON-SERVER #uri以lb://开头(lb代表从注册中心获取服务),后面接的就是你需要转发到的服务名称
  17. predicates:
  18. - Path=/demo/**
  19. filters:
  20. - StripPrefix=1 # 代表 Path 的值中将第一段舍弃,本例子就是转发的时候为 /** 将/demo 去除。
  21.  
  22. eureka:
  23. client:
  24. service-url:
  25. defaultZone: http://localhost:7001/eureka/
  26. instance:
  27. instance-id: gateway-service

3.启动服务访问相关的路径即可看到效果。

  自定义Predicate 路由断言的实现:

  创建一个类,继承 AbstractRoutePredicateFactory 类,实现对应方法:

  1. @Component
  2. public class AuthRoutePredicateFactory extends AbstractRoutePredicateFactory<AuthRoutePredicateFactory.Config> {
  3.  
  4. public AuthRoutePredicateFactory() {
  5. super(Config.class);
  6. }
  7.  
  8. private static final String NAME_KEY = "name";
  9. private static final String VALUE_KEY = "value";
  10.  
  11. @Override
  12. public List<String> shortcutFieldOrder() {
  13. //属性进行匹配对应
  14. return Arrays.asList(NAME_KEY, VALUE_KEY);
  15. }
  16.  
  17. @Override
  18. public Predicate<ServerWebExchange> apply(Config config) {
  19. //Header中携带了某个值,进行header的判断
  20. return (exchange -> {
  21. HttpHeaders headers = exchange.getRequest().getHeaders();
  22. List<String> headerList = headers.get(config.getName());
  23. return headerList.size() > 0;
  24. });
  25. }
  26.  
  27. public static class Config {
  28. private String name;
  29. private String value;
  30.  
  31. public String getName() {
  32. return name;
  33. }
  34.  
  35. public void setName(String name) {
  36. this.name = name;
  37. }
  38.  
  39. public String getValue() {
  40. return value;
  41. }
  42.  
  43. public void setValue(String value) {
  44. this.value = value;
  45. }
  46. }
  47. }

  yml 配置如下:

  1. spring:
  2. application:
  3. name: gateway-service
  4. cloud:
  5. gateway:
  6. enabled: true
  7. discovery:
  8. locator:
  9. enabled: false #gateway开启服务注册和发现的功能
  10. lowerCaseServiceId: true #请求路径上的服务名配置为小写
  11. routes:
  12. - id: cookie_route
  13. predicates:
  14. - Auth=Authorization,token
  15. filters:
  16. - StripPrefix=1
  17. uri: lb://RIBBON-SERVER

  其中 Authorization 为 name,token 为 value,打个断点看看就行了。

Filter 请求过滤器:

  Filter分为全局过滤器和路由过滤器,当请求与路由匹配时,过滤Web处理程序会将的所有实例GlobalFilter和所有特定GatewayFilter于路由的实例添加到过滤器链中。该组合的过滤器链按org.springframework.core.Ordered接口排序,您可以通过实现该getOrder()方法进行设置。

  需要获取到更详细的信息可以惨开官网 :https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#gatewayfilter-factories

  这里演示一下自定义的过滤器(全局、路由)

1.全局过滤器只需要实现接口  org.springframework.cloud.gateway.filter.GlobalFilter,而后无需任何配置,即可生效。

  1. @Component
  2. public class CustomGlobalFilter implements GlobalFilter, Ordered {
  3.  
  4. Logger logger= LoggerFactory.getLogger(CustomGlobalFilter.class);
  5.  
  6. @Override
  7. public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
  8. logger.info("custom global filter");
  9. return chain.filter(exchange);
  10. }
  11.  
  12. @Override
  13. public int getOrder() {
  14. return -1;
  15. }
  16. }

2.路由过滤器 需要继承  org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory

每个过滤器工厂都对应一个实现类,并且这些类的名称必须以 GatewayFilterFactory 结尾,这是Spring Cloud Gateway的一个约定,例如 AddRequestHeader 对应的实现类为 AddRequestHeaderGatewayFilterFactory

  1. @Component
  2. public class WuzzDefineGatewayFilterFactory extends AbstractGatewayFilterFactory<WuzzDefineGatewayFilterFactory.WuzzConfig>{
  3.  
  4. private static final String NAME_KEY="name";
  5.  
  6. Logger logger= LoggerFactory.getLogger(WuzzDefineGatewayFilterFactory.class);
  7.  
  8. public WuzzDefineGatewayFilterFactory() {
  9. super(WuzzConfig.class);
  10. }
  11.  
  12. @Override
  13. public List<String> shortcutFieldOrder() {
  14. return Arrays.asList(NAME_KEY);
  15. }
  16.  
  17. @Override
  18. public GatewayFilter apply(WuzzConfig config) {
  19. //Filter pre post
  20. return ((exchange,chain)->{
  21. logger.info("[pre] Filter Request, name:"+config.getName());
  22. //TODO
  23. return chain.filter(exchange).then(Mono.fromRunnable(()->{
  24. //TODO
  25. logger.info("[post]: Response Filter");
  26. }));
  27. });
  28. }
  29.  
  30. public static class WuzzConfig{
  31. private String name;
  32.  
  33. public String getName() {
  34. return name;
  35. }
  36.  
  37. public void setName(String name) {
  38. this.name = name;
  39. }
  40. }
  41. }

3. 配置路由规则

  1. spring:
  2. application:
  3. name: gateway-service
  4. cloud:
  5. gateway:
  6. enabled: true
  7. discovery:
  8. locator:
  9. enabled: false #gateway\u5F00\u542F\u670D\u52A1\u6CE8\u518C\u548C\u53D1\u73B0\u7684\u529F\u80FD
  10. lowerCaseServiceId: true #\u8BF7\u6C42\u8DEF\u5F84\u4E0A\u7684\u670D\u52A1\u540D\u914D\u7F6E\u4E3A\u5C0F\u5199
  11. routes:
  12. - id: config_route
  13. predicates:
  14. - Path=/demo/**
  15. filters:
  16. - StripPrefix=1
  17. - WuzzDefine=Hello Wuzz
  18. uri: lb://RIBBON-SERVER

4. 启动测试,发送一个请求,可以在控制台看到如下信息,说明过滤器均生效:

  使用自带的限流过滤器 :

  1. spring:
  2. application:
  3. name: gateway-service
  4. cloud:
  5. gateway:
  6. enabled: true
  7. discovery:
  8. locator:
  9. enabled: false #gateway\u5F00\u542F\u670D\u52A1\u6CE8\u518C\u548C\u53D1\u73B0\u7684\u529F\u80FD
  10. lowerCaseServiceId: true #\u8BF7\u6C42\u8DEF\u5F84\u4E0A\u7684\u670D\u52A1\u540D\u914D\u7F6E\u4E3A\u5C0F\u5199
  11. routes:
  12. - id: config_route
  13. predicates:
  14. - Path=/demo/**
  15. filters:
  16. - StripPrefix=1
  17. - WuzzDefine=Hello Wuzz
  18. uri: lb://RIBBON-SERVER
  19. - id: ratelimiter_route
  20. predicates:
  21. - Path=/ratelimiter/**
  22. filters:
  23. - StripPrefix=1
  24. - name: RequestRateLimiter
  25. args:
  26. deny-empty-key: true
  27. keyResolver: '#{@ipAddressKeyResolver}'
  28. redis-rate-limiter.replenishRate: 1
  29. redis-rate-limiter.burstCapacity: 2
  30. uri: lb://RIBBON-SERVER

  以上配置了限流过滤器,

  1. replenishRate:令牌桶中令牌的填充速度,代表允许每秒执行的请求数。
  2. burstCapacity:令牌桶的容量,也就是令牌桶最多能够容纳的令牌数。表示每秒用户最大能够执行的请求数量。

  其中还需要配置一个   keyResolver:

  1. @Component
  2. public class IpAddressKeyResolver implements KeyResolver{
  3.  
  4. @Override
  5. public Mono<String> resolve(ServerWebExchange exchange) {
  6. return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
  7. }
  8. }

  然后启动测试,迅速刷新页面访问接口,会限流:

动态路由:

  Spring Cloud Gateway 提供了 Endpoint 端点,暴露路由信息,有获取所有路由、刷新路由、查看单个路由、删除路由等方法,源码在 org.springframework.cloud.gateway.actuate.GatewayControllerEndpoint 中,想访问端点中的方法需要添加 spring-boot-starter-actuator 依赖,并在配置文件中暴露所有端点

  1. management:
  2. endpoints:
  3. web:
  4. exposure:
  5. include: "*"

  列举几个常用的操作API 。详细信息及配置查看官方文档:https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#actuator-api

  • /actuator/gateway/routes GET 获取路由列表
  • /actuator/gateway/globalfilters  GET 全局过滤器列表
  • /actuator/gateway/routefilters  GET 路由过滤器列表
  • /actuator/gateway/refresh   POST  刷新新增的路由
  • /gateway/routes/{id_route_to_create}   POST/DELETS  新增或者删除
  • ......

  新增路由示例 :

  然后调用一下 刷新的接口,即可生效。但是默认情况下,我们新增的路由只是保存在内存中,万一服务重启,则配置信息丢失,这个时候就需要将路由信息持久化。

  路由持久化--基于redis

1. 引入 redis 依赖:

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-data-redis</artifactId>
  4. </dependency>
  5. <dependency>
  6. <groupId>com.alibaba</groupId>
  7. <artifactId>fastjson</artifactId>
  8. </dependency>

2. 实现 org.springframework.cloud.gateway.route.RouteDefinitionRepository 接口,重写路由的相关操作方法

  1. @Component
  2. public class RedisRouteDefinitionRepository implements RouteDefinitionRepository {
  3.  
  4. private final static String GATEWAY_ROUTE_KEY="gateway_dynamic_route";
  5.  
  6. @Autowired
  7. RedisTemplate<String,String> redisTemplate;
  8.  
  9. @Override
  10. public Flux<RouteDefinition> getRouteDefinitions() {
  11. List<RouteDefinition> routeDefinitionList=new ArrayList<>();
  12. redisTemplate.opsForHash().values(GATEWAY_ROUTE_KEY).stream().forEach(route->{
  13. routeDefinitionList.add(JSON.parseObject(route.toString(),RouteDefinition.class));
  14. });
  15. return Flux.fromIterable(routeDefinitionList);
  16. }
  17.  
  18. @Override
  19. public Mono<Void> save(Mono<RouteDefinition> route) {
  20. return route.flatMap(routeDefinition -> {
  21. redisTemplate.opsForHash().put(GATEWAY_ROUTE_KEY,routeDefinition.getId(),JSON.toJSONString(routeDefinition));
  22. return Mono.empty();
  23. });
  24. }
  25.  
  26. @Override
  27. public Mono<Void> delete(Mono<String> routeId) {
  28. return routeId.flatMap(id->{
  29. if(redisTemplate.opsForHash().hasKey(GATEWAY_ROUTE_KEY,id)){
  30. redisTemplate.opsForHash().delete(GATEWAY_ROUTE_KEY,id);
  31. return Mono.empty();
  32. }
  33. return Mono.defer(()->Mono.error(new Exception("routeDefinition not found:"+routeId)));
  34. });
  35. }
  36. }

3.配置路由

  1. spring:
  2. application:
  3. name: gateway-service
  4. cloud:
  5. gateway:
  6. enabled: true
  7. discovery:
  8. locator:
  9. enabled: false #gateway\u5F00\u542F\u670D\u52A1\u6CE8\u518C\u548C\u53D1\u73B0\u7684\u529F\u80FD
  10. lowerCaseServiceId: true #\u8BF7\u6C42\u8DEF\u5F84\u4E0A\u7684\u670D\u52A1\u540D\u914D\u7F6E\u4E3A\u5C0F\u5199
  11. routes:
  12. - id: config_route
  13. predicates:
  14. - Path=/demo/**
  15. filters:
  16. - StripPrefix=1
  17. - WuzzDefine=Hello Wuzz
  18. uri: lb://RIBBON-SERVER

4. 测试,启动后按照之前一样创建一个路由,这个时候发现 redis里面已经保存了这个路由配置信息

  也可以通过配置中心,比如 config 、Nacos 实现动态的配置。

spring-cloud-gateway 服务网关的更多相关文章

  1. Spring Cloud Gateway 服务网关快速上手

    Spring Cloud Gateway 服务网关 API 主流网关有NGINX.ZUUL.Spring Cloud Gateway.Linkerd等:Spring Cloud Gateway构建于 ...

  2. Spring Cloud Gateway服务网关

    原文:https://www.cnblogs.com/ityouknow/p/10141740.html Spring 官方最终还是按捺不住推出了自己的网关组件:Spring Cloud Gatewa ...

  3. spring cloud:服务网关 Spring Cloud GateWay 入门

    Spring 官方最终还是按捺不住推出了自己的网关组件:Spring Cloud Gateway ,相比之前我们使用的 Zuul(1.x) 它有哪些优势呢?Zuul(1.x) 基于 Servlet,使 ...

  4. Spring Cloud (14) 服务网关-过滤器

    Spring Cloud Zuul作为网关所具备的最基本的功能:路由,还具备另外一个核心的功能:过滤器. 过滤器 通过Spring Cloud Zuul实现的路由功能,我们的微服务可以通过统一的API ...

  5. Spring Cloud (12) 服务网关-基础

    通过前几篇介绍,已经可以构建一个简单的微服务架构了,如下图: 通过eureka实现服务注册中心以及服务注册发现,通过ribbon或feign实现服务的消费以及负载均衡,通过spring cloud c ...

  6. Spring Cloud 之 服务网关

    在微服务架构体系中,使用API 服务网关后的系统架构图如下: API服务网关的主要作用如下: 服务访问的统一入口 服务访问的负载均衡功能 服务访问的路由功能 在SpringCloud中,基于Netfl ...

  7. Spring Cloud (13) 服务网关-路由配置

    传统路由配置 所谓传统路由配置方式就是在不依赖于服务发现机制情况下,通过在配置文件中具体制定每个路由表达式与服务实例的映射关系来实现API网关对外部请求的路由.没有Eureka服务治理框架帮助的时候, ...

  8. spring Cloud网关之Spring Cloud Gateway

    Spring Cloud Gateway是什么?(官网地址:https://cloud.spring.io/spring-cloud-gateway/reference/html/) Spring C ...

  9. 熬夜肝了这篇Spring Cloud Gateway的功能及综合使用

    前言 SpringCloud 是微服务中的翘楚,最佳的落地方案. Spring Cloud Gateway 是 Spring Cloud 新推出的网关框架,之前是 Netflix Zuul.网关通常在 ...

  10. Spring Cloud微服务中网关服务是如何实现的?(Zuul篇)

    导读 我们知道在基于Spring Cloud的微服务体系中,各个微服务除了在内部提供服务外,有些服务接口还需要直接提供给客户端,如Andirod.IOS.H5等等. 而一个很尴尬的境地是,如果直接将提 ...

随机推荐

  1. Kyle Tedford :人,一定要懂得转弯

    每个人都渴望成功,但成功之路不仅仅只有一条. 有的时候,有一条路人满为患,每个人都挤破脑袋想要过去,然而能过去者,却寥寥无几.但有的人,却知道适时转弯,在新的道路上,摸索前进,最终通往成功. 最近,星 ...

  2. 一次"内存泄漏"引发的血案

    本文转载自一次"内存泄漏"引发的血案 导语 2017年末,手Q春节红包项目期间,为保障活动期间服务正常稳定,我对性能不佳的Ark Server进行了改造和重写.重编发布一段时间后, ...

  3. 国际标准时间格式ISO8601

    日期表示法 年由4位数字组成YYYY,或者带正负号的四或五位数字表示±YYYYY.以公历公元1年为0001年,以公元前1年为0000年,公元前2年为-0001年,其他以此类推.应用其他纪年法要换算成公 ...

  4. MapReduce原理及简单实现

    MapReduce是Google在2004年发表的论文<MapReduce: Simplified Data Processing on Large Clusters>中提出的一个用于分布 ...

  5. 手把手教你使用IDEA2020创建SpringBoot项目

    一.New Project 二.如图选择Spring Initalizr,选择jdk版本,然后点击Next(注意:SpringBoot2开始至少使用JDK1.8) 三.如图根据自己需要修改,然后点击N ...

  6. 运行maven遇到的坑,差点崩溃了。

    参考链接1:https://blog.csdn.net/lch_cn/article/details/8225448/ 参考链接2:https://jingyan.baidu.com/article/ ...

  7. JS判断年份是否为闰年

    //闰年能被4整除且不能被100整除,或能被400整除.function year(){ if(year%4==0&&year%100!=0||year%400==0){        ...

  8. 使用PowerDesigner进行数据库设计并直接把设计好的表导出相应的建表语句

    Power Designer:数据库表设计工具 PowerDesigner是Sybase公司的一款软件,使用它可以方便地对系统进行分析设计,他几乎包括了数据库模型设计的全过程.利用PowerDesig ...

  9. Vuex入门、同步异步存取值进阶版

    关注公众号查看原文: 1. vueX介&绍安装 Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式.它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方 ...

  10. Java基础语法:JavaDoc

    一.简介 JavaDoc是一种将注释生成HTML文档的技术,它从程序源代码中抽取类.方法.成员等注释形成一个和源代码配套的API帮助文档. 也就是说,只要在编写程序时以一套特定的标签作注释,在程序编写 ...