• Spring Cloud Gateway(网关)


    一、什么是API网关

    API网关作用就是把各个服务对外提供的API汇聚起来,让外界看起来是一个统一的接口。同时也可在网关中提供额外的功能。

    总结:网关就是所有项目的一个统一入口。

    二、基本使用

    1.准备Eureka注册中心

    2.准备一个微服务工程

    3.搭建Gateway网关微服务

    (1)导入依赖


       
            org.springframework.cloud
            spring-cloud-starter-gateway
       

       
            org.springframework.cloud
            spring-cloud-starter-netflix-eureka-client
       

    (2)编写配置文件

    server:
      port: 9999
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:8761/eureka/
    # 自动发现工具的本地路由规则是:
    # 请求路径 - http://网关IP:网关端口/微服务的服务名/要访问的具体地址
    # gateway自动解析,把请求地址中的'微服务的服务名'截取,从Eureka Client发现的服务列表中查看,如果有同名服务,则开始转发。
    spring:
      application:
        name: cloud-gateway
      cloud: # spring cloud相关配置的常用前缀
        gateway: # 网关技术配置前缀
          discovery: # 自动发现工具
            locator: # 本地逻辑
              enabled: true # 开启自动发现工具的本地路由逻辑
              lower-case-service-id: true # 把从EurekaServer上发现的服务名称,转换成全小写

    (3)编写启动类

    1. @SpringBootApplication
    2. public class GatewayApplication {
    3. public static void main(String[] args) {
    4. SpringApplication.run(GatewayApplication.class,args);
    5. }
    6. }

     三、谓词

    谓词:当满足条件在进行路由转发。

    在Spring Cloud Gateway中谓词实现GatewayPredicate接口。其中类名符合:XXXRoutePredicateFactory,其中XXX就是在配置文件中谓词名称。

    所有的谓词都设置在predicates属性中,当设置多个谓词时取逻辑与条件,且一个谓词只能设置一组条件,如果需要有个多条件,添加多个相同谓词。

    (1)Path

    用于匹配路由地址规则的谓词。

    spring:
      application:
        name: cloud-gateway
      cloud: 
        gateway:
          discovery:
            locator:
              enabled: false # 关闭自动发现工具的本地路由逻辑
              lower-case-service-id: true 
          routes:  # 配置多路由策略的属性,类型是List。配置方案是:回车 + 缩进 + - + 空格。集合中的每个对象的属性,对齐多行配置
            - id: application-service # 路由的唯一名称
              uri: lb://application-service # 规则满足后,转发到的地址。lb是spring cloud gateway支持的一种协议名
              predicates: # 谓词
                - Path=/service/**  # 路由地址规则
              filters:  # 过滤器,先使用,后续课程细致讲解。后续案例配置统一,文档中省略
                - StripPrefix=1

    (2)Query

    用于校验请求中是否包含指定的请求参数,同时可也校验请求参数值是否符合要求。

    - id: application-service
      uri: lb://application-service 
      predicates: # 谓词
        - Path=/service/**  
        - Query=name # 请求参数必须包含name

    下述配置代表,请求中必须包含命名为name和age的参数,且参数值必须已'bjsxt'开头。下述配置中bjsxt.*是一个正则表达式。在正则表达式中点(.)表示匹配任意一个字符。所以当请求参数name=bjsxt或name=bjsxtAdmin等都能满足谓词条件。

    如果设定请求中必须包含多个参数及值。则设置多个Query。在此处演示多个相同谓词配置,其他谓词中就不在强调如何配置多个谓词。

    - id: application-service 
      uri: lb://application-service 
      predicates:
        - Path=/service/**  
        - Query=name,bjsxt.* # 请求参数必须包含name,请求参数的值必须以 bjsxt 开头
        - Query=age # 请求参数必须包含age

    (3)Header

    用于校验请求中是否包含指定的请求头,同时可也校验请求头数值是否符合要求。配置方式和Query类似。

    - id: application-service
      uri: lb://application-service
      predicates:
        - Path=/service/**  
        - Query=name,bjsxt.* 
        - Query=age
        - Header=Host,.* # 请求头必须有Host,值为任意字符串

    (4)Method

    Method表示请求方式。支持多个值,使用逗号分隔,多个值之间为or条件。

    - id: application-service
      uri: lb://application-service
      predicates:
        - Path=/service/** 
        - Method=GET,POST # 请求方式必须是GET或POST

    (5)RemoteAddr

    允许访问的客户端地址。

    - id: application-service
      uri: lb://application-service
      predicates:
        - Path=/service/** 
        - RemoteAddr=192.168.41.252 # 客户端IP必须是192.168.41.252

    (6)Host

    匹配请求中Host请求头的值。满足Ant模式(之前在Spring Security中学习过)可以使用:

    • ? 匹配一个字符

    • * 匹配0个或多个字符

    • ** 匹配0个或多个目录

    - id: application-service
      uri: lb://application-service
      predicates:
        - Path=/service/** 
        - Host=127.0.0.1:9999 # 请求头Host值必须是127.0.0.1:9999

    (7)Cookie

    要求请求中包含指定Cookie名和满足特定正则要求的值。

    Cookie必须有两个值,第一个Cookie包含的参数名,第二个表示参数对应的值,正则表达式。不支持一个参数写法。

    - id: application-service
      uri: lb://application-service
      predicates:
        - Path=/service/** 
        - Cookie=name,bjsxt.* # 请求必须包含名称是name,值符合bjsxt开头的cookie。

    (8)Before

    在指定时间点之前。

    - id: application-service
      uri: lb://application-service
      predicates:
        - Path=/service/** 
        - Before=2022-10-01T18:00:00.000+08:00[Asia/Shanghai] # 2022-10-01晚18点前可以访问

    (9)After

    在指定时间点之后。

    - id: application-service
      uri: lb://application-service
      predicates:
        - Path=/service/** 
        - After=2020-10-01T08:00:00.000+08:00[Asia/Shanghai] # 2020-10-01早8点后可以访问

    (10)Between

    请求时必须在设定的时间范围内,才进行路由转发。

    - id: application-service
      uri: lb://application-service
      predicates:
        - Path=/service/** 
        - Between=2020-10-01T08:00:00.000+08:00[Asia/Shanghai],2022-10-01T18:00:00.000+08:00[Asia/Shanghai] # 2020-10-01早8点后,2022-10-01晚18点前可以访问

    (11)Weight

    多版本服务发布的时候,偶尔使用。

    如v1.0+v1.1两个版本同时发布服务。内容一致,实现机制不同。发布两个不同命名的服务集群。使用Gateway做负载均衡并设置权重。 ​ 代表同一个组中URI进行负载均衡。语法:Weight=组名,负载均衡权重 ​ 在Eureka中注册两个服务,这个服务(项目)是相同的,应用程序名分别叫做application-service1和application-service2。 ​ Gateway在路由匹配时application-service1将占20%,application-service2将占80%。

    - id: application-service1
      uri: lb://application-service1
      predicates:
        - Path=/service/**
        - Weight=group1,2
    - id: application-service2
      uri: lb://application-service2
      predicates:
        - Path=/service/**
        - Weight=group1,8

    四、过滤器-Filter

    在路由转发到代理服务之前和代理服务返回结果之后额外做的事情。Filter是在路由转发之后,被代理的服务执行前后运行的。只要Filter执行了,说一定满足了谓词条件。 ​ 在Spring Cloud Gateway的路由中Filter分为:

    • 路由过滤器:框架内置的Filter实现都是路由过滤器,都是GatewayFilter实现类型。本章节所有案例都是路由过滤器

    • 全局过滤器:框架未内置全局过滤器实现,需自定义。全局过滤器需实现接口GlobalFilter。

    (1)StripPrefix

    跳过路由uri中前几段后发送给下游。

    spring:
      application:
        name: cloud-gateway
      cloud: 
        gateway:
          discovery:
            locator:
              enabled: false
              lower-case-service-id: true 
          routes:  
            - id: application-service 
              uri: lb://application-service
              predicates: 
                - Path=/service/**  
              filters:  # 过滤器
                - StripPrefix=1 # 跳过路由uri中前1段后发送给下游。

    (2)AddRequestHeader

    添加请求头参数,参数名和值之间使用逗号分隔。

    filters:
      - StripPrefix=1
      - AddRequestHeader=company,bjsxt

    (3)AddRequestParameter

    添加请求表单参数,多个参数需要有多个过滤器。

    filters:
      - StripPrefix=1
      - AddRequestParameter=name,bjsxt
      - AddRequestParameter=age,18

    (4)AddResponseHeader

    添加响应头。

    filters:
      - StripPrefix=1
      - AddResponseHeader=company,bjsxt··

    (5)DedupeResponseHeader

    对指定响应头去重复。配置语法:DedupeResponseHeader=响应头参数 或 DedupeResponseHeader=响应头参数,strategy。

    去重策略strategy可选值:

    • RETAIN_FIRST :默认值,保留第一个

    • RETAIN_LAST 保留最后一个

    • RETAIN_UNIQUE 保留唯一的,出现重复的属性值,会保留一个。例如有两个My:bbb的属性,最后会只留一个。

    filters:
      - StripPrefix=1
      - DedupeResponseHeader=MyHeader,RETAIN_UNIQUE 

    (6)CircuitBreaker

    实现熔断时使用,支持CircuitBreaker和Hystrix两种。

    (7)FallbackHeaders

    可以添加降级时的异常信息。

    (8)PrefixPath

    给符合规则替换后的URI地址添加统一的前缀地址。

    (9)RequestRateLimiter

    限流过滤器。

    (10)RedirectTo

    重定向。有两个参数,status和url。其中status应该300系列重定向状态码。

    (11)RemoveRequestHeader

    删除请求头参数。

    (12)RemoveResponseHeader

    删除响应头参数。

    (13)RemoveRequestParameter

    删除请求参数。

    (14)RewritePath

    重写请求路径。

    (15)RewriteResponseHeader

    重写响应头参数。
    (16)SaveSession

    如果项目中使用Spring Security和Spring Session整合时,此属性特别重要。

    (17)SecureHeaders

    具有权限验证时,建议的头信息内容。

    (18)SetPath

    功能和StripPrefix有点类似。语法更贴近restful。

    - id: setpath_route 
      uri: https://example.org 
      predicates: 
        - Path=/api/{segment} 
      filters: 
        - SetPath=/{segment}

    (19)SetRequestHeader

    替换请求参数头数。不是添加

    (20)SetResponseHeader

    替换响应头参数。

     (21)SetStatus

    设置响应状态码。

    (22)Retry

    设置重试次数。

    (23)RequestSize

    请求最大大小。包含maxSize参数,单位包括“KB”或“MB”等。默认为“B”。

    (24)ModifyRequestBody

    修改请求体内容。

    (25)ModifyResponseBody

    修改响应体

    五、使用Gateway实现限流

    令牌桶算法

    令牌桶算法可以说是对漏桶算法的一种改进。

    在桶中放令牌,请求获取令牌后才能继续执行。如果桶中没有令牌,请求可以选择进行等待或者直接拒绝。

    由于桶中令牌是按照一定速率放置的,所以可以一定程度解决突发访问。如果桶中令牌最多有100个,QPS最大为100。

    (1)导入依赖


        org.springframework.boot
        spring-boot-starter-data-redis

    (2)新建Key解析器

    1. /**
    2. * 限流过滤器配置必要的类型。
    3. * 令牌桶算法中令牌工厂的组件之一,用于生成一个个与客户端对应的令牌key。 提供一个能对应客户端的数据。
    4. * 如:IP,用户登录名等。
    5. *
    6. * 当前类型对象,需要Spring容器管理。
    7. */
    8. @Component
    9. public class MyKeyResolver implements KeyResolver {
    10. @Override
    11. public Mono resolve(ServerWebExchange exchange) {
    12. String ip = exchange.getRequest() // 获取请求对象
    13. .getRemoteAddress() // 获取客户端地址对象 InetSocketAddress
    14. .getAddress() // 获取客户端地址对象 InetAddress
    15. .getHostAddress(); // 获取客户端的主机地址(IP或唯一的主机名)
    16. return Mono.just(ip); // 创建返回结果对象
    17. }
    18. }

    (3)编写配置文件

    server:
      port: 9999
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:8761/eureka/
    spring:
      application:
        name: cloud-gateway
      cloud: 
        gateway: 
          discovery: 
            locator: 
              enabled: false 
              lower-case-service-id: true
          routes:
            - id: rateLimiter
              uri: lb://application-service
              predicates:
                - Path=/limiter/**
              filters:
                - StripPrefix=1
                - name: RequestRateLimiter
                  args:
                    keyResolver: '#{@myKeyResolver}' # 表达式, #{} 从容器找对象, @beanId
                    # redis-rate-limiter是用于做令牌校验,和令牌生成的类型。gateway框架提供了基于Redis的实现。
                    redis-rate-limiter.replenishRate: 1 # 每秒令牌生成速率
                    redis-rate-limiter.burstCapacity: 2 # 令牌桶容量上限

    六、使用Gateway实现服务降级

     (1)导入依赖


        org.springframework.cloud
        spring-cloud-starter-netflix-hystrix

    (2)编辑降级逻辑控制器

    1. /**
    2. * 服务降级控制器
    3. */
    4. @Controller
    5. public class FallbackController {
    6. /**
    7. * 服务降级处理方法。
    8. * 当通过gateway转发请求的服务,不可用时,当前方法执行。返回降级数据。
    9. * @return
    10. */
    11. @RequestMapping(value = "/fallback", produces = {"text/html; charset=UTF-8"})
    12. @ResponseBody
    13. public String fallback(){
    14. return "
      服务器忙,请稍后重试!
      "
      ;
    15. }
    16. }

    (3)配置文件

    - id: hystrix
      uri: lb://application-service
      predicates:
        - Path=/hystrix/**
      filters:
        - StripPrefix=1
        - name: Hystrix
          args:
        name: fallback # 随意定义的名称。相当于@HystrixCommand注解中的commandKey属性。
        fallbackUri: forward:/fallback # 如果转发的服务不可用,请求转发到当前系统的哪一个路径上。

    七、自定义全局过滤器

    编辑全局过滤器

    1. /**
    2. * 自定义全局过滤器。
    3. * 必须实现接口GlobalFilter
    4. * 当前类型的对象,必须被spring容器管理。
    5. * 无须配置,所有路由都生效。
    6. *
    7. * 执行顺序:
    8. * 先执行网关过滤器,后执行全局过滤器
    9. * 多个全局过滤器,执行顺序由Spring boot扫描管理当前对象的顺序决定。
    10. * 每个过滤器,都是完整执行后,才执行下一个过滤器。
    11. */
    12. @Component
    13. public class MyGlobalFilter implements GlobalFilter {
    14. /**
    15. * 过滤方法。
    16. * 实现上,只有唯一的要求。必须调用方法chain.filter(exchange),并把方法的返回值,返回。
    17. * @param exchange
    18. * @param chain
    19. * @return
    20. */
    21. @Override
    22. public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    23. System.out.println("前置全局过滤");
    24. Mono result = chain.filter(exchange);
    25. System.out.println("后置全局过滤");
    26. return result;
    27. }
    28. }

    八、自定义路由过滤器

    定义针对于Router的Filter。必须经由配置才能生效。 注意:

    1. 类名必须定义为XxxGatewayFilterFactory。注入到Spring容器后使用时的名称就叫做xxx。

    2. 建议继承AbstractGatewayFilterFactory。如果不继承,则必须实现接口GatewayFilterFactory,这种方式开发成本高。

    3. 所有需要传递进来的参数都配置到当前类的静态内部类Config中。

     (1)编辑路由过滤器

    1. /**
    2. * 自定义网关过滤器(路由过滤器),必须经过配置使用才能生效的过滤器。
    3. * 要求当前类型的对象必须被spring容器管理。
    4. * 要求必须实现接口 GatewayFilterFactory, 建议继承AbstractGatewayFilterFactory
    5. *
    6. * 多个网关过滤器执行顺序:
    7. * 按照配置文件中,过滤器的配置顺序,依次运行。每个过滤器完整运行结束后,执行下一个过滤规则。
    8. */
    9. @Component
    10. public class LoggerFilterGatewayFilterFactory
    11. extends AbstractGatewayFilterFactory {
    12. /**
    13. * 建议提供2个构造方法。一个无参数。一个有参数,参数类型就是当前类型中的Config静态内部类的类对象类型。
    14. * 父类型,可以帮助解析配置文件,并创建Config对象。
    15. */
    16. public LoggerFilterGatewayFilterFactory(){
    17. this(Config.class);
    18. }
    19. public LoggerFilterGatewayFilterFactory(Class configClass){
    20. super(configClass);
    21. }
    22. /**
    23. * 如果需要简化配置方案。提供方法shortcutFieldOrder
    24. * 有当前方法,配置文件使用,可以简化配置为 LoggerFilter=abc
    25. * 没有当前方法,配置文件完整编写,内容是:
    26. * name: LoggerFilter
    27. * args:
    28. * remark: abc
    29. */
    30. @Override
    31. public List shortcutFieldOrder() {
    32. return Arrays.asList("remark");
    33. }
    34. /**
    35. * 创建网关过滤器的方法。
    36. * @param config 就是配置文件中的内容,就是当前类型中的静态内部类对象。
    37. * @return 一般使用匿名内部类,创建GatewayFilter接口的实现对象。
    38. */
    39. @Override
    40. public GatewayFilter apply(Config config) {
    41. return new GatewayFilter() {
    42. /**
    43. * 过滤方法。要求必须调用chain.filter(exchange),并返回方法的返回结果
    44. * @param exchange
    45. * @param chain
    46. * @return
    47. */
    48. @Override
    49. public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    50. System.out.println("前置 - 日志过滤器 - config.remark = " + config.getRemark());
    51. Mono result = chain.filter(exchange);
    52. System.out.println("后置 - 日志过滤器 - config.remark = " + config.getRemark());
    53. return result;
    54. }
    55. };
    56. }
    57. /**
    58. * 定义静态内部类,作为配置对象
    59. * 定义的每个属性,都是用于在配置文件中配置的对应属性。
    60. * 必须提供getter和setter方法。
    61. */
    62. public static class Config{
    63. private String remark;
    64. public String getRemark() {
    65. return remark;
    66. }
    67. public void setRemark(String remark) {
    68. this.remark = remark;
    69. }
    70. }
    71. }

    (2)编辑配置文件

    - id: logger
      uri: lb://application-service
      predicates:
        - Path=/logger/**
      filters:
        - StripPrefix=1
        - LoggerFilter=simpleTestGatewayFilter
        - name: LoggerFilter
            args:
              remark: fullyTestGatewayFilter

  • 相关阅读:
    阿里云高校计划学生和教师完成认证领取优惠权益
    Matlab论文插图绘制模板第44期—二元直方图(Histogram2)
    自动驾驶系统激光雷达传感器反射率标定板
    【打卡】21天学习挑战赛—RK3399平台开发入门到精通-day10
    用YOLOv5模型对流媒体和网络视频做推理的时候发生了报错——OSError: [WinError 123] 文件名、目录名或卷标语法不正确。:'runs\train\\exp'
    Spring源码解析——@Transactional注解的声明式事物介绍
    从C语言到C++:C++入门知识(2)
    戴尔PowerEdge R650服务器荣获国家级实验室5项证书
    〖全域运营实战白宝书 - 运营角色认知篇②〗- 什么是运营?
    java常见面试题(未整理,后续可能一直更新)
  • 原文地址:https://blog.csdn.net/weixin_53455615/article/details/128163208