Spring Cloud Gateway + 注册服务 ( Consul ) 实现负载均衡

Posted by aclyyx on 12-02,2020

前言

在微服务( SpringBoot )的使用过程中,一直在用 Zuul 作为网关服务程序,但很久很久以前,就传出 Spring Cloud Zuul 已经停止维护,并且使用 Spring Cloud Gateway 代替;但一直没有在实际项目中替换使用 Gateway 的想法(懒)。

最近,由于疫情的影响公司似乎是要凉凉,所以工作的事情比较少。就打算对服务中的网关程序下手,并看看是不是可以解决之前在 Zuul 的历史遗留问题。(最后还是没有解决...)

想一想

接触 Spring 微服务以来,两年时间里一直使用 SpringBoot + Zuul + Consul 的组合;通过注册服务( Consul )来集合和管理各个微服务程序,网关( Zuul )通过配置的关键字跳转到不同的微服务程序。

springcloud_gateway_20201201_1

这一次,只替换网关部分的程序,用 Spring Cloud Gateway 替换 Zuul 。

Gateway 的配置与 Zuul 类似,替换难度不大;这里主要说说项目的 Gateway 项目的搭建,并与测试项目联调。

工具和版本说明

  • 集成开发工具:IDEA 2020.2.3
  • 编译管理工具:Maven 3.6.3
  • Spring Cloud 版本:2020.0.0-M5
  • Spring Boot 版本:2.4.0

动手做

使用 Spring Initializr 在 IDEA 中创建两个 Module,一个是 Gateway 程序,一个是用户测试的微服务程序。

Maven 引入

Gateway 项目中包含的库:

spring-cloud-starter-gateway

测试项目中包含的库:

spring-boot-starter-web

两个项目中都需要包含的库:

spring-cloud-starter-consul-discovery
spring-boot-starter-actuator
spring-boot-devtools
spring-boot-configuration-processor

yaml 配置

Gateway 程序的 yaml 配置文件,使用最简配置

spring:
  application:
    name: gateway #定义应用名称
  cloud:
    consul:
      host: 127.0.0.1 # Consul 服务的地址
      discovery:
        ip-address: ${spring.cloud.client.ip-address}
        prefer-ip-address: true # 使用 IP 地址作为调用地址,而不使用主机名
        healthCheckInterval: 1s # 健康检查间隔
    gateway:
      routes:
        - id: test              # 应用ID(名称)
          uri: lb://test        # URI,lb表示从发现服务进行服务的路由
          predicates:
            - Path=/test/**     # 路径调整条件
          filters:
            - StripPrefix=1     # 截取路径的层级数 /t1/** 填 1, /t1/t2/** 填 2,以此类推

测试程序的 yaml 配置文件,因为要启动多个实例以达到负载均衡的效果,所以创建三个配置信息,其中主要的配置是一样的,是有服务端口变化。

第一个配置文件也是主要的配置信息。

server:
  port: 8081

spring:
  application:
    name: test
  cloud:
    consul:
      host: 127.0.0.1
      discovery:
        ip-address: ${spring.cloud.client.ip-address}
        prefer-ip-address: true
        healthCheckInterval: 1s

其他两个只是定义服务端口。

文件 application-test1.yaml

server:
  port: 8082

文件 application-test2.yaml

server:
  port: 8083

测试用 Controller 类

在测试程序项目中添加 TestController ,测试用 Controller 类。

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping("hi")
public class TestController {

    @Value(value = "${server.port}")
    private Integer port;

    @GetMapping("get")
    @ResponseBody
    public String get() {
        // 每个被访问的 Controller 输出各自服务的端口号加以区分
        return "Test is OK. Server port : " + port;
    }
}

启动 Gateway 服务

springcloud_gateway_20201202_1

启动 Test 程序

springcloud_gateway_20201202_2

复制 Test 程序启动配置

下图中选择Copy Configuration...
springcloud_gateway_20201202_3

复制后修改名称和启动参数,以达到启动器调用不同的配置文件,启动不同的服务端口。
springcloud_gateway_20201202_4

启动后各个服务程序的状态

IDEA 中 Services 界面:
springcloud_gateway_20201202_5

Consul Web UI 界面:
springcloud_gateway_20201202_6

springcloud_gateway_20201202_7

启动浏览器看看效果

启动浏览器,访问地址http://127.0.0.1:8080/test/hi/get并不断刷新,可以看到页面的内容会在 3 个测试服务中依次显示

Test is OK. Server port : 8081
Test is OK. Server port : 8082
Test is OK. Server port : 8083

遗留问题

当测试程序中的其中一个节点停止后,网关( Gateway 和 Zuul )并不会立即停止对给服务程序的访问,短暂的还是会有跳转到已经关闭的服务程序中去。

参考

Spring Cloud Gateway替代zuul作为API网关(一)
SpringCloud组件的停更和替换说明
Spring boot之@Value注解的使用总结
Spring Cloud Consul 注册服务failing,但是可以访问
SpringCloud实战十三:Gateway之 Spring Cloud Gateway 动态路由
SpringCloud gateway (史上最全)
SpringCloud Gateway 修改请求路径的过滤器(StripPrefix Filter和PrefixPath Filter)