【Spring Cloud系列】Spring Cloud-网关Zuul详解与实战

Spring Cloud - Zuul网关详解与实战

一、概述

Spring Cloud Zuul 是 Spring Cloud Netflix 子项目的核心组件之一,是netflix开源的一个API Gateway服务器,本质上有一个Web Servlet应用,可以作为微服务架构中的 API 网关使用,支持动态路由与过滤功能;网关为微服务提供统一的访问入口;网关的定义类似设计模式中的门面模式,相当于微服务中的门面,客户端访问微服务都是通过它进行路由及过滤。它提供请求路由,负载均衡、校验过滤、服务容错、服务聚合等功能。

二、 Zuul功能和作用

Zuul可以通过加载动态过滤机制,从而实现以下各项功能:

  • 验证与安全保障: 识别面向各类资源的验证要求并拒绝那些与要求不符的请求。
  • 审查与监控: 在边缘位置追踪有意义数据及统计结果,从而为我们带来准确的生产状态结论。
  • 路由转发: 以动态方式根据需要将请求路由至不同后端集群处。
  • 压力测试: 逐渐增加指向集群的负载流量,从而计算性能水平。
  • 负载均衡: 为每一种负载类型分配对应容量,并弃用超出限定值的请求。
  • 静态响应处理: 在边缘位置直接建立部分响应,从而避免其流入内部集群。
  • 多区域弹性: 跨越AWS区域进行请求路由,旨在实现ELB使用多样化并保证边缘位置与使用者尽可能接近。

除以上功能之外,Netflix公司还利用Zuul的功能通过金丝雀版本实现精确路由与压力测试。

在Spring Cloud中,可用通过添加Zuul的依赖和配置文件,快速搭建一个网关,方便统一管理和维护各个微服务,实现更好的服务治理。

三、Zuul网关工作原理

3.1 网关主要组件:

Zuul网关主要由一下几个组件构成:

  • Filter:过滤器,可以在请求被路由前或者之后添加一些处理逻辑。
  • Route:路由,将请求路由到不同的后端服务上。
  • Ribbon:负载均衡器,Zuul 默认使用 Ribbon 进行负载均衡。
  • Hystrix:容错处理器,可以实现限流和熔断机制。

Zuul 的过滤器链是整个网关的核心部分,它由多个过滤器构成,每个过滤器都负责不同的处理逻辑,比如请求的鉴权、转发等操作。过滤器链在处理请求的过程中,会依次执行这些过滤器,从而实现对请求的全生命周期管理,具体流程如下图所示。

在图中,我们可以看到 Filter Chain 主要由三部分组成:过滤器、生成路由并发送给后端服务、处理路由响应。下面我们将详细介绍每个部分的处理逻辑。

3.2 Zuul过滤器

过滤器是 Zuul 中最重要的组件之一,它可以拦截和修改请求和响应的内容,实现各种功能。在 Spring Cloud 中,所有的过滤器都必须继承抽象类 ZuulFilter,并实现其中的四个方法:

  • filterType()方法:返回过滤器类型,包括 pre、post、route 和 error 四种类型。

  • filterOrder()方法:返回过滤器执行的顺序,值越小越先执行。

  • shouldFilter()方法:判断过滤器是否需要执行,默认返回 true,表示全部需要执行。

  • run()方法:过滤器的主要业务逻辑,实现具体的过滤逻辑

Filter 类型

Zuul 中不同类型 filter 的执行逻辑核心在 com.netflix.zuul.http.ZuulServlet 类中定义,该类相关代码如下:

@Override
public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse) throws ServletException, IOException {
    
    
    try {
    
    
        init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
        // Marks this request as having passed through the "Zuul engine", as opposed to servlets
        // explicitly bound in web.xml, for which requests will not have the same data attached
        RequestContext context = RequestContext.getCurrentContext();
        context.setZuulEngineRan();
        try {
    
    
            preRoute();
        } catch (ZuulException e) {
    
    
            error(e);
            postRoute();
            return;
        }
        try {
    
    
            route();
        } catch (ZuulException e) {
    
    
            error(e);
            postRoute();
            return;
        }
        try {
    
    
            postRoute();
        } catch (ZuulException e) {
    
    
            error(e);
            return;
        }
    } catch (Throwable e) {
    
    
        error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
    } finally {
    
    
        RequestContext.getCurrentContext().unset();
    }
}

在这里插入图片描述

这张经典的官方流程图有些问题,其中 post Filter 抛错之后进入 error Filter,然后再进入 post Filter 是有失偏颇的。实际上 post Filter 抛错分两种情况:

  1. 在 post Filter 抛错之前,pre、route Filter 没有抛错,此时会进入 ZuulException 的逻辑,打印堆栈信息,然后再返回 status = 500 的 ERROR 信息。
  2. 在 post Filter 抛错之前,pre、route Filter 已有抛错,此时不会打印堆栈信息,直接返回status = 500 的 ERROR 信息。

这样就比较直观地描述了 Zuul 关于 Filter 的请求生命周期。Zuul 中一共有四种不同生命周期的 Filter,分别是:

  1. pre:在 Zuul 按照规则路由到下级服务之前执行。如果需要对请求进行预处理,比如鉴权、限流等,都应考虑在此类 Filter 实现。
  2. route:这类 Filter 是 Zuul 路由动作的执行者,是 Apache Http Client 或 Netflix Ribbon 构建和发送原始 HTTP 请求的地方,目前已支持 Okhttp。
  3. post:这类 Filter 是在源服务返回结果或者异常信息发生后执行的,如果需要对返回信息做一些处理,则在此类 Filter 进行处理。
  4. error:在整个生命周期内如果发生异常,则会进入 error Filter,可做全局异常处理。

实际项目中,需要自实现以上类型的 Filter 来对请求链路进行处理,根据业务的需求,选取相应生命周期的 Filter 来达成目的。在 Filter 之间,通过 com.netflix.zuul.context.RequestContext 类来进行通信,内部采用 ThreadLocal 保存每个请求的一些信息,包括请求路由、错误信息、HttpServletRequest、HttpServletResponse,这使得一些操作是十分可靠的,它还扩展了 ConcurrentHashMap,目的是为了在处理过程中保存任何形式的信息。

3.3 生成路由并发送给后端服务

在Zuul中,可以通过配置ZuulProperties和RouteLocator实现路由转发的功能。其中ZuulProperties主要用于配置Zuul服务的一些相关属性,比如缓存时间、URL的前缀和后缀等;而RouteLocator则用于定义多个路由规则,将请求映射到不同的后端服务上。

当请求进入Zuul网关之后,首先会经过一系列过滤器处理,然后根据路由规则将请求转发到对应的后端服务上,最终返回响应结果。在转发请求时,Zuul可以自动地根据负载均衡策略选择相应的服务器,实现负载均衡的功能。

3.4 处理路由响应

在Zuul中,如果后端服务响应异常或者错误,那么 Zuul 会将这个异常封装成一个 ZuulException 对象,并交给其它的过滤器进行处理。当所有过滤器执行完毕之后,Zuul 会根据 ZuulException 中的状态码和错误消息,返回相应的响应结果。

四、Zuul 网关配置过程

在 Spring Cloud 中,我们可以通过添加一些依赖和配置文件,快速地创建一个 Zuul 网关服务。下面我们将详细介绍如何进行配置。

4.1 添加pom.xml依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

4.2 创建配置类

创建一个配置类,用于配置 Zuul 网关服务的相关属性。

@Configuration
@EnableZuulProxy
public class ZuulConfig {
    
    
    // 配置 Zuul 网关服务的相关属性
}

其中注解 @EnableZuulProxy 表示开启 Zuul 的代理功能,可以自动注册到 Eureka 服务中心,并集成 Ribbon 和 Hystrix 等组件。

4.3 配置路由规则

接下来我们需要配置 Zuul 的路由规则,将请求转发到不同的后端服务上。

@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
    
    
    return builder.routes()
        .route("service-a", r -> r.path("/service-a/**")
            .filters(f -> f.stripPrefix(1))
            .uri("http://localhost:6060"))
        .route("service-b", r -> r.path("/service-b/**")
            .filters(f -> f.stripPrefix(1))
            .uri("http://localhost:6061"))
        .build();
}

在上面的代码中,我们定义了两个路由规则,分别将请求转发到 http://localhost:8081http://localhost:8082 这两个地址上。其中 stripPrefix(1) 表示去掉 URL 中第一个斜杠之后的内容。

4.4 配置路由规则

最后我们可以添加一些过滤器,实现不同的功能。

@Bean
public MyFilter myFilter() {
    
    
    return new MyFilter();
}

其中 MyFilter 是我们自定义的过滤器类,用于实现一些特定的功能。

四、总结

本文从 Zuul 网关的原理、使用场景和配置过程三个方面详细介绍了 Zuul 网关的相关知识。可以看出,Zuul 的过滤器链是整个网关的核心部分,通过添加不同的过滤器,可以实现不同的功能,比如鉴权、转发、限流等。同时,通过合理地配置路由规则,可以将请求快速地转发到相应的后端服务中,实现负载均衡和服务治理的功能。

在实际开发中,我们可以根据不同的需求,灵活地运用 Zuul 网关服务,构建高可用、高并发的分布式应用系统。

猜你喜欢

转载自blog.csdn.net/songjianlong/article/details/133267519