java springCloud总结-gateway

时间:2025-08-27 12:36:01来源:互联网

下面小编就为大家分享一篇java springCloud总结-gateway,具有很好的参考价值,希望对大家有所帮助。

简介

SpringCloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,目标是替代 Zuul

可以与Spring Cloud Discovery Client(如Eureka)、Ribbon、Hystrix等组件配合使用,实现路由转发、负载均衡、熔断、鉴权、路径重写、日志监控等
Gateway还内置了限流过滤器,实现了限流的功能。

是有 WebFlux+Netty+Reactor实现的响应式API网关

官网:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-starter

网关简介

网关为微服务架构的系统提供简单、有效且统一的API路由管理,作为系统的统一入口,提供内部服务的路由中转,给客户端提供统一的服务,可以实现一些和业务没有耦合的公用逻辑,主要功能包含认证、鉴权、路由转发、安全策略、防刷、流量控制、监控日志等。

网关在微服务架构中的位置:、

为什么需要网关服务:

对于微服务而言,微服务之间调用繁多,如果通过配置服务地址和接口来进行调用比较杂乱,采用网关后微服务都与网关进行交互,网关再找到对应服务及接口

流量网关和微服务网关

流量网关(如Nignx)是指提供全局性的、与后端业务应用无关的策略,例如 HTTPS证书卸载、Web防火墙、全局流量监控等。

微服务网关(如Spring Cloud Gateway)是指与业务紧耦合的、提供单个业务域级别的策略,如服务治理、身份认证等。

核心概念

路由(route):路由是构建网关的基本模块,它由ID,目标URI,断言集合和过滤器集合组成,如果断言为true,则匹配该路由。

断言(Predicate):参考Java8的java.util.function.Predicate,用来匹配HTTP请求中的所有内容,例如请求头或请求参数,如果请求与断言匹配结果返回一个布尔值。

过滤(filter):Spring框架中GatewayFilter的实例,使用过滤器,可以载请求被路由前或者后对请求进行处理。

断言说明

Spring Cloud Gataway 帮我们内置了很多 Predicates 功能

After:匹配在指定日期时间之后发生的请求。
Before:匹配在指定日期之前发生的请求。
Between:需要指定两个日期参数,设定一个时间区间,匹配此时间区间内的请求。
Cookie:需要指定两个参数,分别为name和regexp(正则表达式),也可以理解Key和Value,匹配具有给定名称且其值与正则表达式匹配的Cookie。
Header:需要两个参数header和regexp(正则表达式),也可以理解为Key和Value,匹配请求携带信息。
Host:匹配当前请求是否来自于设置的主机。
Method:可以设置一个或多个参数,匹配HTTP请求,比如GET、POST
Path:匹配指定路径下的请求,可以是多个用逗号分隔
Query:需要指定一个或者多个参数,一个必须参数和一个可选的正则表达式,匹配请求中是否包含第一个参数,如果有两个参数,则匹配请求中第一个参数的值是否符合正则表达式。
RemoteAddr:匹配指定IP或IP段,符合条件转发。
Weight:需要两个参数group和weight(int),实现了路由权重功能,按照路由权重选择同一个分组中的路由

 

- After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #这个时间之后的请求为true,否则为false
- Before=2022-06-11T15:30:40.785+08:00[Asia/Shanghai] #这个时间之前的请求为true,否则为false
- Between=2022-06-11T15:30:40.785+08:00[Asia/Shanghai],2022-06-11T16:30:40.785+08:00[Asia/Shanghai]
- Cookie=myCookieKey,[a-z]+ # 匹配Cookie的key和value(正则表达式)
- Header=myHeaderKey, d+ # 一个 header 中属性名称和一个正则表达式
- Host=**.muxiaonong.com #匹配当前的主机地址发出的请求
- Method=POST,GET #多个用逗号分割

- Path=/foo/{segment} #匹配路径返回true,否则返回false
- Query=id,.+ # 一个是属性名一个为属性值,属性值可以是正则表达式;这里如果需要匹配多个参数,可以写多个- Query=;如果只写属性名,只要入参有该属性就返回true
- RemoteAddr=192.168.1.1/24 #支持通过设置某个 ip 区间号段的请求才会路由,(其中 192.168.1.1 是 IP 地址,24  是子网掩码)。
- Weight=groupName,8 #两个参数group和weight(int)权重数值,实现了路由权重功能

 

完整定义和快捷定义

完整定义:

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://imdony.org
        predicates:
        - name: Cookie
          args:
            name: mycookie
            regexp: mycookievalue

快捷定义

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - Cookie=mycookie,mycookievalue

过滤说明

内置的Filter生命周期有两种:pre(业务逻辑之前)、post(业务逻辑之后)
自带的Filter分为两种: GateWayFilter(局部)、GlobalFilter(全局)
官网:
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#global-filters

局部过滤器需要在路由中配置才能生效

全局过滤器应用全部路由上,当然除了内置的全局过滤器,实际工作中还需要定制过滤器,全局过滤器不必在路由上配置,注入到IOC容器中即可全局生效。

 

自定义全局过滤器

@Slf4j
@Component
@Order(value = Integer.MIN_VALUE)
public class AccessLogGlobalFilter implements GlobalFilter {
 
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //filter的前置处理
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getPath().pathWithinApplication().value();
        InetSocketAddress remoteAddress = request.getRemoteAddress();
        return chain
                //继续调用filter
                .filter(exchange)
                //filter的后置处理
                .then(Mono.fromRunnable(() -> {
            ServerHttpResponse response = exchange.getResponse();
            HttpStatus statusCode = response.getStatusCode();
            log.info("请求路径:{},远程IP地址:{},响应码:{}", path, remoteAddress, statusCode);
        }));
    }
}

@Order指定优先级,值越小,优先级越高

由于过滤器有 pre 和 post 两种类型,pre 类型过滤器如果 order 值越小,那么它就应该在pre过滤器链的顶层,post 类型过滤器如果 order 值越小,那么它就应该在 post 过滤器链的底层。

 

监控和指标功能

management:
  endpoints:
    web:
      exposure:
        include: gateway, metrics
  endpoint:
    gateway:
      enabled: true
  metrics:
    export:
      prometheus:
        enabled: true

在这个示例中:
- management.endpoints.web.exposure.include配置了暴露的端点,包括gateway和metrics端点。
- management.endpoint.gateway.enabled启用了Gateway端点,用于查看Gateway的路由信息和状态。
- management.metrics.export.prometheus.enabled启用了Prometheus指标导出,可以将Gateway的指标数据导出到Prometheus监控系统中。

通过这样的配置,可以实现对Gateway的监控和指标功能,方便管理员监控和分析Gateway的运行状态和性能指标。

Gateway配置的监控功能可以监控以下内容:

1. 路由信息:可以查看当前配置的路由信息,包括路由规则、目标服务、匹配条件等。
2. 请求统计:可以监控请求的数量、响应时间、成功率等指标,帮助分析系统的负载情况和性能表现。
3. 错误信息:可以查看请求中出现的错误信息,帮助排查问题和进行故障诊断。
4. 流量控制:可以监控流量的分布情况,帮助进行流量控制和负载均衡。
5. 熔断器状态:可以查看熔断器的状态和触发情况,帮助监控系统的稳定性和可用性。

 

流程

当用户发出请求达到 GateWay 之后,会通过一些匹配条件,定位到真正的服务节点,并且在这个转发过程前后,进行一些细粒度的控制,其中 Predicate(断言) 是我们的匹配条件,Filter 是一个拦截器,有了这两点,再加上URL,就可以实现一个具体的路由,核心思想:路由转发+执行过滤器链

 

熔断降级

网关作为流量的入口,因此会有大量的请求进入网关,向其他服务发起调用,其他服务不可避免的会出现调用失败(超时、异常),失败时不能让请求堆积在网关上,需要快速失败并返回给客户端,想要实现这个要求,就必须在网关上做熔断、降级操作。

spring:
  application:
    name: gateway
  cloud:
    gateway:
      routes:
        - id: rateLimit_route
          uri: http://localhost:8000
          order: 0
          predicates:
            - Path=/test/**
          filters:
            - StripPrefix=1
            - name: Hystrix
              args:
                name: fallbackCmdA
                fallbackUri: forward:/fallbackA
 
  hystrix.command.fallbackCmdA.execution.isolation.thread.timeoutInMilliseconds: 5000

过滤器Hystrix,作用是通过Hystrix进行熔断降级
当上游的请求,进入了Hystrix熔断降级机制时,就会调用fallbackUri配置的降级地址。需要注意的是,还需要单独设置Hystrix的commandKey的超时时间
fallbackUri配置的降级地址的代码如下:

@RestController
public class FallbackController {
 
    @GetMapping("/fallbackA")
    public Response fallbackA() {
        Response response = new Response();
        response.setCode("100");
        response.setMessage("服务暂时不可用");
        return response;
    }
}

 

gateway项目搭建

添加依赖,依赖的版本根据自己的springboot的版本确定

<!-- 引入gateway网关 -->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-gateway</artifactId>
	<exclusions>
        <exclusion>
			<groupId>org.springframework.boot</groupId>
        	<artifactId>spring-boot-starter-web</artifactId>
        </exclusion>
    </exclusions>
</dependency>

配置文件

server:
  port: 8888
  servlet:
    context-path: /gateway-demo
spring:
  application:
    name: demo
  cloud:
    gateway:
      #路由规则
      routes:
        - id: xgss1 #路由标识,要求唯一,名称任意
          uri: http://localhost:8080 #请求被路由到的地址
          order: 1 #优先级,数字越小优先级越高
          #设置断言
          predicates:
            - Path=/gateway/xgss/** #满足该路径的才会被路由到uri地址
            - Weight=group1,8 #同一分组按照权重进行分配流量,这里分配了80%,第一个group1是分组名,第二个参数是权重
          filters:
            - StripPrefix=1 # StripPrefix:去除原始请求路径中的前1级路径,即/gateway
        - id: xgss2
          uri: http://localhost:8081
          predicates:
            - Path=/gateway/xgss/**
            - Weight=group1,2 #这里分配权重20%
          filters:
            - StripPrefix=1
logging:
  file:
    path: logs/
  level:
    com.xgss.demo: info

启动类跟普通springBoot项目的启动类一样

启动日志如下

当访问:localhost:8888/gateway/xgss/test 时,就会有80%的请求会路由到 8080, 20%的请求会路由到 8081,并且路径上去掉了gateway

 

配置文件说明

- spring.cloud.gateway.routes:定义路由规则的列表。
- spring.cloud.gateway.routes.id:路由的唯一标识符。
- spring.cloud.gateway.routes.uri:目标服务的URI。
- spring.cloud.gateway.routes.predicates:定义路由的匹配条件(断言),例如请求路径、请求方法等。
- spring.cloud.gateway.routes.filters:定义路由的过滤器,用于对请求进行预处理和后处理。

实例:

   spring:
     cloud:
       gateway:
         routes:
           - id: example_route
             uri: http://example.com
             predicates:
               - Path=/example/**
             filters:
               - AddRequestHeader=X-ExampleHeader, example-value

断言配置:

After:匹配在指定日期时间之后发生的请求。
Before:匹配在指定日期之前发生的请求。
Between:需要指定两个日期参数,设定一个时间区间,匹配此时间区间内的请求。
Cookie:需要指定两个参数,分别为name和regexp(正则表达式),也可以理解Key和Value,匹配具有给定名称且其值与正则表达式匹配的Cookie。
Header:需要两个参数header和regexp(正则表达式),也可以理解为Key和Value,匹配请求携带信息。
Host:匹配当前请求是否来自于设置的主机。
Method:可以设置一个或多个参数,匹配HTTP请求,比如GET、POST
Path:匹配指定路径下的请求,可以是多个用逗号分隔
Query:需要指定一个或者多个参数,一个必须参数和一个可选的正则表达式,匹配请求中是否包含第一个参数,如果有两个参数,则匹配请求中第一个参数的值是否符合正则表达式。
RemoteAddr:匹配指定IP或IP段,符合条件转发。
Weight:需要两个参数group和weight(int),实现了路由权重功能,按照路由权重选择同一个分组中的路由

匹配所有以/api/开头的请求路径。可以使用Ant风格的路径模式(如/api/**)或正则表达式来进行匹配。

     predicates:
       - Path=/api/**

匹配所有GET请求方法的请求。可以使用HTTP请求方法(如GET、POST、PUT等)进行匹配。

     predicates:
       - Method=GET

该配置将匹配具有X-Custom-Header请求头且值为custom-value的请求。

     predicates:
       - Header=X-Custom-Header, custom-value

匹配具有名为param1且值为value1的查询参数的请求。

     predicates:
       - Query=param1, value1

匹配具有名为cookie1且值为value1的Cookie的请求。

     predicates:
       - Cookie=cookie1, value1

过滤器配置:
可以使用过滤器对请求进行预处理和后处理。常用的内置过滤器包括:
- AddRequestHeader:添加请求头。
- AddResponseHeader:添加响应头。
- RewritePath:重写请求路径。
- Hystrix:添加熔断器功能。
- RateLimiter:添加限流功能。

   spring:
     cloud:
       gateway:
         routes:
           - id: example_route
             uri: http://example.com
             predicates:
               - Path=/example/**
             filters:
               - AddRequestHeader=X-ExampleHeader, example-value
               - RewritePath=/example/(?<segment>.*), /${segment}

/example/(?<segment>.*)是一个正则表达式,用于匹配以/example/开头的请求路径,并捕获路径中的内容作为一个命名组(named group),命名为segment。

/${segment}是一个替换字符串,用于将捕获的路径内容作为替换的一部分。用于转义$符号,使其被解释为普通字符而不是正则表达式的特殊符号,${segment}表示引用捕获的命名组segment的值。

综合起来,这个表达式的意思是将以/example/开头的请求路径进行重写,将路径中的内容作为替换的一部分。例如,对于请求路径/example/foo,经过这个表达式的处理后,将被重写为/foo。
 

 

除了内置过滤器,还可以自定义过滤器来满足特定的需求。需要实现GatewayFilter接口,并注册到Spring容器

   @Component
   public class CustomFilter implements GatewayFilter {

       @Override
       public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
           // 自定义过滤器逻辑
           return chain.filter(exchange);
       }
   }
   spring:
     cloud:
       gateway:
         routes:
           - id: example_route
             uri: http://example.com
             predicates:
               - Path=/example/**
             filters:
               - name: CustomFilter

gateway集成nacos注册中心

上述 demo 中并没有集成注册中心,每次路由配置都是指定固定的服务uri
缺点:
网关服务需要知道所有服务的域名或IP地址
一旦服务的域名或IP地址发生修改,路由配置中的 uri 就必须修改
无法实现负载均衡

那么此时我们可以集成的注册中心,使得网关能够从注册中心自动获取uri,并实现负载均衡

使用nacos作为注册中心:

增加依赖:

<!--nacos注册中心-->
<dependency>
	<groupId>com.alibaba.cloud</groupId>
	<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

 启动类添加 @EnableDiscoveryClient 注解开启注册中心功能

配置文件配置 nacos 注册中心的地址:

nacos:
  namespace: 856a40d7-6548-4494-bdb9-c44491865f63
  url: 120.76.129.106:80
spring:
  cloud:
    nacos:
      discovery:
      	server-addr: ${nacos.url}
        namespace: ${nacos.namespace}
        register-enabled: true

微服务网关路由配置:

spring:
  cloud:
    gateway:
      routes:
        - id: gateway-provider_1
          ## 使用了lb形式,从注册中心负载均衡的获取uri
          uri: lb://gateway-provider
          ## 配置断言
          predicates:
            - Path=/gateway/provider/**
          filters:
            - AddResponseHeader=X-Response-Foo, Bar

路由配置中唯一不同的就是路由的 uri,格式:lb://service-name,这是固定写法:

在Spring Cloud Gateway中,lb:是用于定义路由的URI的一种特殊前缀,表示使用负载均衡的方式进行服务路由。
具体来说,当使用lb:前缀时,Spring Cloud Gateway会将请求路由到注册在服务注册中心的服务实例上,以实现负载均衡的效果。

使用lb:前缀进行负载均衡路由时,需要确保已经配置了服务注册中心,并且目标服务已经注册到服务注册中心中。


lb:固定格式,指的是从注册中心按照名称获取微服务,并遵循负载均衡策略
service-name:注册中心的服务名称,这里并不是IP地址形式的

 为什么指定了 lb 就可以开启负载均衡,全局过滤器 LoadBalancerClientFilter 就是负责路由寻址和负载均衡的,可以看到如下源码:

开启 gateway 自动路由配置:
我们的系统架构不断地发展,系统中微服务的数量越来越多,不可能每添加一个服务,就在网关配置一个新的路由规则,这样的维护成本很大;我们在请求路径中会携带一个路由标识方便进行转发,而这个路由标识一般都是服务在注册中心中的服务名,因此这是我们就可以开启 spring cloud gateway 的自动路由功能,网关自动根据注册中心的服务名为每个服务创建一个router,将以服务名开头的请求路径转发到对应的服务,配置如下:

# enabled:默认为false,设置为true表明spring cloud gateway开启服务发现和路由的功能,网关自动根据注册中心的服务名为每个服务创建一个router,将以服务名开头的请求路径转发到对应的服务
spring.cloud.gateway.discovery.locator.enabled = true
# lowerCaseServiceId:启动 locator.enabled=true 自动路由时,路由的路径默认会使用大写ID,若想要使用小写ID,可将lowerCaseServiceId设置为true
spring.cloud.gateway.discovery.locator.lower-case-service-id = true

由于我们的网关项目配置了 server.servlet.context-path 属性,这会导致自动路由失败的问题,因此我们需要做如下两个修改:

# 重写过滤链,解决项目设置了 server.servlet.context-path 导致 locator.enabled=true 默认路由策略404的问题
spring.cloud.gateway.discovery.locator.filters[0] = PreserveHostHeader
@Configuration
public class GatewayConfig
{
    @Value ("${server.servlet.context-path}")
    private String prefix;
 
    /**
     * 过滤 server.servlet.context-path 属性配置的项目路径,防止对后续路由策略产生影响,因为 gateway 网关不支持 servlet
     */
    @Bean
    @Order (-1)
    public WebFilter apiPrefixFilter()
    {
        return (exchange, chain) ->
        {
            ServerHttpRequest request = exchange.getRequest();
            String path = request.getURI().getRawPath();
 
            path = path.startsWith(prefix) ? path.replaceFirst(prefix, "") : path;
            ServerHttpRequest newRequest = request.mutate().path(path).build();
 
            return chain.filter(exchange.mutate().request(newRequest).build());
        };
    }
}

网关自动根据注册中心的服务名为每个服务创建一个router,将以服务名开头的请求路径转发到对应的服务。假设我们的服务提供者在 nacos 注册中心的服务名为 “gateway-provider”,这时我们只需要访问 “http://localhost:9023/gateway/gateway-provider/test”,就可以将请求成功转发过去了

本站部分内容转载自互联网,如果有网站内容侵犯了您的权益,可直接联系我们删除,感谢支持!