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

本文涉及的产品
应用型负载均衡 ALB,每月750个小时 15LCU
传统型负载均衡 CLB,每月750个小时 15LCU
网络型负载均衡 NLB,每月750个小时 15LCU
简介: 笔记

一、概述


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();
    }
}

3.jpeg

这张经典的官方流程图有些问题,其中 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:8081 和 http://localhost:8082 这两个地址上。其中 stripPrefix(1) 表示去掉 URL 中第一个斜杠之后的内容。


4.4 配置路由规则

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

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

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


四、总结


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


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


相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
目录
相关文章
|
25天前
|
负载均衡 Java 开发者
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
深入探索Spring Cloud与Spring Boot:构建微服务架构的实践经验
83 5
|
2月前
|
自然语言处理 Java API
Spring Boot 接入大模型实战:通义千问赋能智能应用快速构建
【10月更文挑战第23天】在人工智能(AI)技术飞速发展的今天,大模型如通义千问(阿里云推出的生成式对话引擎)等已成为推动智能应用创新的重要力量。然而,对于许多开发者而言,如何高效、便捷地接入这些大模型并构建出功能丰富的智能应用仍是一个挑战。
267 6
|
2月前
|
缓存 NoSQL Java
Spring Boot与Redis:整合与实战
【10月更文挑战第15天】本文介绍了如何在Spring Boot项目中整合Redis,通过一个电商商品推荐系统的案例,详细展示了从添加依赖、配置连接信息到创建配置类的具体步骤。实战部分演示了如何利用Redis缓存提高系统响应速度,减少数据库访问压力,从而提升用户体验。
144 2
|
2月前
|
XML Java 数据格式
如何使用 Spring Cloud 实现网关
如何使用 Spring Cloud 实现网关
50 3
|
2月前
|
Java 数据库连接 Spring
【2021Spring编程实战笔记】Spring开发分享~(下)
【2021Spring编程实战笔记】Spring开发分享~(下)
34 1
|
3月前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
171 5
|
2月前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
80 0
|
2月前
|
XML Java 数据库连接
【2020Spring编程实战笔记】Spring开发分享~(上)
【2020Spring编程实战笔记】Spring开发分享~
59 0
|
2月前
|
负载均衡 Java API
【Spring Cloud生态】Spring Cloud Gateway基本配置
【Spring Cloud生态】Spring Cloud Gateway基本配置
62 0
|
2月前
|
安全 5G 网络性能优化