整合Spring Cloud Gateway编码实践

接下来就通过实际的编码,把微服务网关整合到项目中,顺便体验一下 Spring Cloud Gateway 的功能。

编码整合Spring Cloud Gateway

笔者将结合实际的编码来讲解如何构建一个网关服务。本节代码是在 spring-cloud-alibaba-multi-service-demo 模板项目的基础上修改的,具体步骤如下。

  1. 修改项目名称。

    修改项目名称为 spring-cloud-alibaba-gateway-demo,并把各个模块中 pom.xml 文件的 artifactId 修改为 spring-cloud-alibaba-gateway-demo

  2. 新建网关模块并引入 Spring Cloud Gateway 依赖。

    新建一个模块,并命名为 gateway-demoJava 代码的包名为 ltd.newbee.cloud。在该模块的 pom.xml 配置文件中增加 parent 标签,与上层 Maven 建立好关系。

    打开 gateway-demo 项目中的 pom.xml 文件,在 dependencies 标签下引入 Spring CloudGateway 的依赖文件,新增代码如下:

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
  3. 增加网关配置。

    打开 gateway-demo 项目中的 application.properties 文件,主要配置项目端口及路由,配置项为 spring.cloud.gateway.routes.*,最终的配置文件内容如下:

    server.port=8127
    
    spring.cloud.gateway.routes[0].id=goods-demo-route
    spring.cloud.gateway.routes[0].uri=http://localhost:8120
    spring.cloud.gateway.routes[0].order=1
    spring.cloud.gateway.routes[0].predicates[0]=Path=/goods/**
    
    spring.cloud.gateway.routes[1].id=shopcart-demo-route
    spring.cloud.gateway.routes[1].uri=http://localhost:8122
    spring.cloud.gateway.routes[1].order=1
    spring.cloud.gateway.routes[1].predicates[0]=Path=/shop-cart/**

    这里主要配置 gateway-demogoods-service-demoshopcart-service-demo 的路由信息。如果访问网关项目的路径是以 /goods 开头的,就路由到 goods-service-demo,该项目的 URLhttp://localhost:8120 ;如果访问网关项目的路径是以 /shop-cart 开头的,就路由到 shopcart-service-demo,该项目的 URLhttp://localhost:8122

    另外,在 goods-service-demoshopcart-service-demo 中分别新增两个接口用于测试。

    NewBeeCloudGoodsAPI 类中新增如下代码:

    @GetMapping("/goods/page/{pageNum}")
    public String goodsList(@PathVariable("pageNum") int pageNum) {
        // 返回信息给调用端
        return "请求 goodsList, 当前服务的端口号为" + applicationServerPort;
    }

    NewBeeCloudShopCartAPI 类中新增如下代码:

    @GetMapping("/shop-cart/page/{pageNum}")
    public String cartItemList(@PathVariable("pageNum") int pageNum) throws InterruptedException {
        // 返回信息给调用端
        return "请求 cartItemList, 当前服务的端口号为" + applicationServerPort;
    }

依次启动 gateway-demogoods-service-demoshopcart-service-demo 这三个项目。启动成功后,打开浏览器验证网关的功能,在地址栏中依次输入如下地址进行测试:

http://localhost:8127/goods?goodsId=2025
http://localhost:8127/goods/page/2
http://localhost:8127/shop-cart?cartId=2035
http://localhost:8127/shop-cart/page/3

四次访问的最终结果如图 9-7 所示。

image 2025 04 16 18 01 24 428
Figure 1. 图9-7 网关整合后的访问结果

Spring Cloud Gateway 网关整合成功!

另外,有一个知识点需要注意:不要在网关模块中加入 spring-boot-starter-web 依赖,否则网关项目是无法正常启动的,报错内容如下:

****************************
APPLICATION FAILED TO START
****************************

Description:

Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway.

Spring 官方文档中也做了重点提示,内容如下:

Spring Cloud Gateway requires the Netty runtime provided by Spring Boot and
Spring Webflux. It does not work in a traditional Servlet Container or when
built as a WAR.

在使用 Spring Cloud Gateway 组件时,不能使用传统的 Servlet 容器,也不能打包成 WAR 包。

将网关服务整合到服务中心

前文中只是简单地整合网关和功能验证,并没有整合服务中心,即网关模块并没有被真正纳入微服务架构中。在配置文件中定义的路由地址是 “写死的”,这种做法在真实项目中肯定是不推荐的。接下来要做的就是把 Spring Cloud Gateway 注册到服务中心,通过服务中心(本书中的技术选型为 Nacos)把请求路由到对应的服务实例中。

新建一个模块,命名为 gateway-demo2Java 代码的包名为 ltd.newbee.cloud。在该模块的 pom.xml 配置文件中增加 parent 标签,与上层 Maven 建立好关系。打开 gateway-demo2 项目中的 pom.xml 文件,在 dependencies 标签下引入 Spring Cloud Gateway 和服务发现的依赖文件,最终代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                             https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>ltd.newbee.cloud</groupId>
    <artifactId>gateway-demo2</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>gateway-demo2</name>
    <description>Spring Cloud Alibaba Gateway Demo</description>
    <parent>
        <groupId>ltd.newbee.cloud</groupId>
        <artifactId>spring-cloud-alibaba-gateway-demo</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>
</project>

下面修改 gateway-demo2 项目中的 application.properties 配置文件,增加服务发现的配置项和路由配置项,代码如下:

server.port=8129
# 应用名称
spring.application.name=newbee-cloud-gateway-service
# 注册中心 Nacos 的访问地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
# 登录名(默认为 nacos,可自行修改)
spring.cloud.nacos.username=nacos
# 密码(默认为 nacos,可自行修改)
spring.cloud.nacos.password=nacos

spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lower-case-service-id=true

spring.cloud.gateway.routes[0].id=goods-service-route
spring.cloud.gateway.routes[0].uri=lb://newbee-cloud-goods-service
spring.cloud.gateway.routes[0].order=1
spring.cloud.gateway.routes[0].predicates[0]=Path=/goods/**

spring.cloud.gateway.routes[1].id=shopcart-service-route
spring.cloud.gateway.routes[1].uri=lb://newbee-cloud-shopcart-service
spring.cloud.gateway.routes[1].order=1
spring.cloud.gateway.routes[1].predicates[0]=Path=/shop-cart/**

路由配置与前文中介绍的路由配置区别不大,只是将 “写死” 的地址修改为对应服务的地址,这样就能通过服务发现机制路由到对应的服务实例中了。另外,地址前缀由 http 改为了 lb,表示启用负载均衡功能。微服务中负载均衡这个知识点的编码和源码,笔者都已经讲解过。

在本书中,Spring Boot 项目的配置文件都是 .properties 格式的。如果平时开发时习惯使用 .yml 格式的配置文件,可自行修改。

编码完成后,最终 spring-cloud-alibaba-gateway-demo 项目的目录结构如图 9-8 所示。

image 2025 04 16 18 07 30 262
Figure 2. 图9-8 spring-cloud-alibaba-gateway-demo 项目的目录结构

编码完成后进行功能验证,需要启动 Nacos Server,之后依次启动 gateway-demo2goods-service-demoshopcart-service-demo 这三个项目。如果未能成功启动,则开发人员需要查看控制台中的日志是否报错,并及时确认问题和修复。启动成功后进入 Nacos 控制台,单击 “服务管理” 中的服务列表,可以看到列表中已经存在这三个服务的服务信息,如图 9-9 所示。

打开浏览器验证网关的功能,在地址栏中依次输入如下地址进行测试:

http://localhost:8129/goods?goodsId=2025
http://localhost:8129/goods/page/2
http://localhost:8129/shop-cart?cartId=2035
http://localhost:8129/shop-cart/page/3
image 2025 04 16 18 09 14 023
Figure 3. 图9-9 Nacos 控制台中的服务列表

整合Spring Cloud Gateway报错503的问题解决方法

此时页面中显示的结果如下:

Whitelabel Error Page

This application has no configured error view, so you are seeing this as a
fallback.

Mon Jun 5 16:28:12 CST 2023

[0223c41b-3]There was an unexpected error(type=Service
Unavailable, status=503).

并未获取正确的结果,而是一个 503 的报错提示。

报错主要是因为 Spring Cloud Gateway 的相关依赖中没有负载均衡器,因此无法正确将请求路由到对应的服务中。修改方式比较简单,在 Gateway 项目的依赖文件中加上负载均衡器的依赖就可以了,新增如下代码:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-loadbalancer</artifactId>
</dependency>

添加代码后记得刷新一下 Maven 依赖,之后重新启动这个项目,在浏览器中就能够获取正确的数据了。

为什么要单独讲一下这个问题呢?主要是因为浏览器中报了这个错误,但是在网关项目的日志文件中并没有这个错误的异常信息栈,这就导致开发人员在看到这个问题后,无法判断出现这个问题的具体原因,进而不知道如何处理它。

现在,服务网关也整合到项目中了。不过,仅仅整合和简单的配置是远远不够的,接下来笔者将介绍 Spring Cloud Gateway 组件中的重要知识——Predicate(断言)和 Filter(过滤器)。