Gateway网关组件

Gateway(网关)组件

官网:https://docs.spring.io/spring-cloud-gateway/docs/4.0.x/reference/html/

springcloud中提供了一个组件Gateway,用于搭建微服务的网关。
网关处于客户端(前端)和微服务端之间,作为前端请求的统一入口去访问微服务:
通常对于各个微服务的地址信息需要屏蔽(不会直接对外公开),公开的是网关的地址。
网关可以将请求转发(负载均衡)到目标微服务
网关可以实现一些公共的业务逻辑,例如:统一的身份/权限校验,统一的跨域处理,统一的日志记录…

image-20241020155331309

网关功能:身份认证和权限校验、服务路由、负载均衡、请求限流

三大核心概念

Route(路由)

路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如果断言为true则匹配该路由

Predicate(断言)

参考的是java8的java.util.function.Predicate开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由。

Filter(过滤)

指的是Spring框架中GatewayFilter的实例,**使用过滤器,可以在请求被路由前或者之后对请求进行修改。****

工作流程

image-20241020160039262

  • 客户端向Spring Cloud Gateway发出请求。然后在Gateway Handler Mapping中找到与请求匹配的路由,将其发送到Gateway Web Handler.
  • Handler再通过指定的过滤器链来将请求发送给我们实际的服务执行业务逻辑,然后返回。
  • 过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前("pre")或之后("post")执行业务逻辑。
  • Filter在"pre"类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在"post"类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量控制等有着非常重要的作用

搭建网关服务

1、创建一个独立的普通Maven项目,只用来实现网关配置

image-20241020161706983

2、引入如下依赖

<dependencies>
  <!--网关为啥需要添加nacos的依赖?-->
  <!--网关需要将请求负载均衡到目标微服务集群,网关需要从nacos上找到目标服务的实例列表从而进行负载均衡-->
  <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
   </dependency>

   <!--实现负载均衡的组件-->
   <dependency>
       <groupId>org.springframework.cloud</groupId>
       <artifactId>spring-cloud-starter-loadbalancer</artifactId>
    </dependency>

    <!--网关本身的核心依赖-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
     </dependency>
</dependencies>

3、编写启动类

@SpringBootApplication
public class GatewayApplication {
	public static void main(String[] args) {
		SpringApplication.run(GatewayApplication.class, args);
	}
}

4、在application.yml配置文件中编写基础配置和路由规则

server:
  port: 9000  # 网关服务器端口号
spring:
  application:
    name: service-gateway  # 将网关服务器注册到nacos中
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848  # 将网关服务器注册到nacos中
    gateway:
    #配置路由(可以配置多组路由) 每组路由包含的核心参数配置:id(路由的编号,保持唯一即可) + uri + predicate + filter
      routes:
        - id: service-order
          uri: lb://chs-cloud-order # 固定的写法: lb://目标服务nacos中的名称
          predicates: #断言(就是一个判断条件),用于判断当前客户端的请求要匹配到网关中的那一组路由
            - Path=/*/order/** # 路径匹配
        
        - id: service-user
          uri: lb://chs-cloud-user
          predicates:
            - Path=/*/user/** # 路径匹配

5、访问测试:

重启网关,访问http://localhost:9000/api/user/findUserByUserId/1时,符合/api/user/**规则,

请求转发到uri:http://chs-cloud-user/api/user/findUserByUserId/1,得到了结果:

image-20241020165426588

Predicate(断言)的使用

我们在配置文件中只是配置了一个访问路径的规则,怎么就可以实现路由呢?

底层原理:框架底层会自动读取配置文件中的内容,然后通过制定的路由工厂将其转换成对应的判断条件,然后进行判断。在Gateway中提供了很多的路由工厂如下所示:https://docs.spring.io/spring-cloud-gateway/docs/4.0.6/reference/html/#gateway-request-predicates-factories

image-20241020170021852

大致有12个,每一种路由工厂的使用Spring Cloud的官网都给出了具体的示例代码

gateway:
	#配置路由(可以配置多组路由) 每组路由包含的核心参数配置:id(路由的编号,保持唯一即可) + uri + predicate + filter
      routes: 
        - id: service-order
          uri: lb://service-order # 固定的写法: lb://目标服务的id
          
          predicates: #断言(就是一个判断条件),用于判断当前客户端的请求要匹配到网关中的那一组路由
          - Path=/api/order/** # -Path 使用当前客户端的http请求的路径作为判断条件
          - Header=token,123 #当前请求的请求头中必须包含一个token字段,值也得等于123
          - Method=GET,POST #当前请求必须是get或者post类型
          - Query=green,red #当前请求中必须包含这两个请求参数,url?green=red

总结

Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapper基础框架的一部分。

Spring Cloud Gateway包括许多内置的Route Predicate工厂。所有这些Predicate都与HTTP请求的不同属性匹配。多个Route Predicate工厂可以进行组合

Spring Cloud Gateway创建Route对象时,使用RoutePredicateFactory创建Predicate对象,Predicate对象可以赋值给 Route。Spring Cloud Gateway包含许多内置的Route Predicate Factories。所有这些谓词都匹配HTTP请求的不同属性。多种谓词工厂可以组合,并通过逻辑and 。

Filter(过滤器)

在gateway中要实现其他的功能:权限控制、流量监控、统一日志处理等。就需要使用到gateway中所提供的过滤器了。过滤器,可以对进入网关的请求和微服务返回的响应做处理:

image-20230624164230054

内置的过滤器

spring gateway提供了30多种不同的过滤器。

官网地址:https://docs.spring.io/spring-cloud-gateway/docs/4.0.x/reference/html/#gatewayfilter-factories

名称 说明
AddRequestHeader 给当前请求添加一个请求头
RemoveRequestHeader 移除请求中的一个请求头
AddResponseHeader 给响应结果中添加一个响应头
RemoveResponseHeader 从响应结果中移除有一个响应头
RequestRateLimiter 限制请求的流量
RewritePath 修改请求的路径
Timeout 设置请求的超时时间
Retry 对失败的请求进行重试
CircuitBreaker 实现熔断器功能,提供服务熔断
StripPrefix 用于去除请求路径中的指定数量的前缀段
PrefixPath 用于为请求路径添加指定的前缀

在Gateway中提供了三种级别的类型的过滤器:

1、路由过滤器:只针对当前路由有效

2、默认过滤器:针对所有的路由都有效

3、全局过滤器:针对所有的路由都有效,需要进行自定义

路由过滤器

需求:给所有进入chs-cloud-user的请求添加一个请求头:Truth=1234

1、修改gateway服务的application.yml文件,添加路由过滤

spring:
  cloud:
    gateway:
      routes:
        - id: chs-cloud-user
          uri: lb://chs-cloud-user
          predicates:
            - Path=/api/user/** 
          filters:
            - AddRequestHeader=Truth, 1234	# 配置路由基本的过滤器,给访问user微服务的所有接口添加Truth请求头

当前过滤器写在chs-cloud-user路由下,因此仅仅对访问chs-cloud-user的请求有效

2、在chs-cloud-user的接口方法中读取请求头数据,进行测试

@GetMapping(value = "/findUserByUserId/{userId}")
public User findUserByUserId(@PathVariable(value = "userId") Long userId , @RequestHeader(name = "Truth")String header) {
    log.info("UserController...findUserByUserId方法执行了... ,header: {} " , header);
    return userService.findUserByUserId(userId) ;
}

默认过滤器

如果要对所有的路由都生效,则可以将过滤器工厂写到default下。

spring:
  cloud:
    gateway:
      routes:
        - id: chs-cloud-user
          uri: lb://chs-cloud-user
          predicates:
            - Path=/api/user/**
      default-filters:
        - AddRequestHeader=Truth,1234

全局过滤器

上述的过滤器是gateway中提供的默认的过滤器,每一个过滤器的功能都是固定的。

但是如果我们希望拦截请求,做自己的业务逻辑,默认的过滤器就没办法实现。

此时就需求使用全局过滤器,全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。区别在于GatewayFilter通过配置定义,处理逻辑是固定的;而GlobalFilter的逻辑需要自己写代码实现。

需求:定义全局过滤器,拦截请求:请求参数中是否有username,如果同时满足则放行,否则拦截

1、定义一个类实现GlobalFilter接口

2、重写filter方法

3、将该类纳入到spring容器中

4、实现Ordered接口定义该过滤器的顺序

@Component // 将该类标记为Spring的一个组件,使其被自动检测和注册  
public class AuthorizationFilter implements GlobalFilter, Ordered {  

    // 实现过滤器逻辑  
    @Override  
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {  
        // 获取当前请求对象  
        ServerHttpRequest request = exchange.getRequest();  
        
        // 从请求参数中获取"username"参数  
        String username = request.getQueryParams().getFirst("username");  
        
        // 判断"username"是否存在且不为空  
        if (!StringUtils.hasText(username)) {  
            // 获取当前响应对象  
            ServerHttpResponse response = exchange.getResponse();  
            
            // 设置响应状态码为401 Unauthorized(未经授权)  
            response.setStatusCode(HttpStatus.UNAUTHORIZED);  
            
            // 完成响应,不再继续过滤链中的后续处理  
            return response.setComplete();  
        }  
        
        // 继续执行过滤器链,调用下一个过滤器  
        return chain.filter(exchange);  
    }  

    // 定义该过滤器的顺序,值越小优先级越高  
    @Override  
    public int getOrder() {  
        return 0; // 优先执行该过滤器  
    }  
}  

过滤器执行顺序

请求进入网关会碰到三类过滤器:当前路由的过滤器、DefaultFilter、GlobalFilter

请求路由后,会将当前路由过滤器和DefaultFilter、GlobalFilter,合并到一个过滤器链(集合)中,排序后依次执行每个过滤器:

image-20230624170925571

排序的规则是什么呢?

1、按照order的值进行排序,order的值越小,优先级越高,执行顺序越靠前

2、路由过滤器和默认过滤器会按照order的值进行排序,这个值由spring进行指定,默认是按照声明顺序从1递增

3、当过滤器的order值一样时,会按照 globalFilter(全局) > defaultFilter(默认) > 路由过滤器的顺序执行

核心源码分析:org.springframework.cloud.gateway.handler.FilteringWebHandler#handle方法会加载全局过滤器,与前面的过滤器合并后根据order排序,组织过滤器链

public Mono<Void> handle(ServerWebExchange exchange) {
    Route route = (Route)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
    
    // 获取路由级别的过滤器和默认过滤器的集合
    List<GatewayFilter> gatewayFilters = route.getFilters();
    
    // 获取全局过滤器的集合
    List<GatewayFilter> combined = new ArrayList(this.globalFilters);
    
    // 将取路由级别的过滤器和默认过滤器的集合中的元素添加到全局过滤器的集合中
    combined.addAll(gatewayFilters);
    
    // 进行排序
    AnnotationAwareOrderComparator.sort(combined);
    if (logger.isDebugEnabled()) {
        logger.debug("Sorted gatewayFilterFactories: " + combined);
    }

    // 调用过滤器链中的filter方法
    return (new DefaultGatewayFilterChain(combined)).filter(exchange);
}
posted @   CH_song  阅读(80)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
点击右上角即可分享
微信分享提示