1. 概述

本文将深入探讨 Zuul 与 Eureka 协同实现负载均衡的机制。我们将通过 Zuul 代理,将请求路由到由 Spring Cloud Eureka 发现的 REST 服务。

2. 初始准备

需要预先搭建好 Eureka 服务端/客户端,具体步骤可参考 Spring Cloud Netflix-Eureka 一文(此处不展开基础配置)。

3. 配置 Zuul

Zuul 的核心功能之一是从 Eureka 获取服务位置并执行服务端负载均衡。

3.1. Maven 依赖配置

首先在 pom.xml 中添加 Zuul ServerEureka 依赖

<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>

3.2. 与 Eureka 通信

在 Zuul 的 application.properties 中添加关键配置:

server.port=8762
spring.application.name=zuul-server
eureka.instance.preferIpAddress=true
eureka.client.registerWithEureka=true
eureka.client.fetchRegistry=true
eureka.client.serviceUrl.defaultZone=${EUREKA_URI:http://localhost:8761/eureka}

✅ 此配置让 Zuul 在 Eureka 中注册为服务,并运行在 8762 端口。

接下来实现主类,使用 @EnableZuulProxy@EnableDiscoveryClient 注解:

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

访问 http://localhost:8762/routes 可查看所有由 Eureka 发现的 Zuul 路由:

{"/spring-cloud-eureka-client/**":"spring-cloud-eureka-client"}

现在通过 Zuul 代理路由访问 Eureka 客户端:http://localhost:8762/spring-cloud-eureka-client/greeting,将得到类似响应:

Hello from 'SPRING-CLOUD-EUREKA-CLIENT with Port Number 8081'!

4. Zuul 负载均衡实现

当 Zuul 收到请求时,会从可用服务实例中选择一个并转发请求。服务实例位置缓存和请求转发过程完全开箱即用,无需额外配置。

下图展示了 Zuul 如何封装同一服务的三个不同实例:

Zuul 负载均衡示意图

内部实现上,Zuul 使用 Netflix Ribbon 从服务发现中心(Eureka Server)查询所有服务实例。

4.1. 注册多实例

启动两个服务实例(端口 8081 和 8082)。观察日志可见实例位置已注册到 DynamicServerListLoadBalancer,路由映射到负责转发的 Zuul Controller

Mapped URL path [/spring-cloud-eureka-client/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
Client:spring-cloud-eureka-client instantiated a LoadBalancer:
  DynamicServerListLoadBalancer:{NFLoadBalancer:name=spring-cloud-eureka-client,
  current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
Using serverListUpdater PollingServerListUpdater
DynamicServerListLoadBalancer for client spring-cloud-eureka-client initialized: 
  DynamicServerListLoadBalancer:{NFLoadBalancer:name=spring-cloud-eureka-client,
  current list of Servers=[0.0.0.0:8081, 0.0.0.0:8082],
  Load balancer stats=Zone stats: {defaultzone=[Zone:defaultzone;    Instance count:2;    
  Active connections count: 0;    Circuit breaker tripped count: 0;    
  Active connections per server: 0.0;]},
  Server stats: 
    [[Server:0.0.0.0:8080;    Zone:defaultZone;......],
    [Server:0.0.0.0:8081;    Zone:defaultZone; ......],

⚠️ 日志已格式化以便阅读

4.2. 负载均衡演示

多次访问 http://localhost:8762/spring-cloud-eureka-client/greeting,将看到轮询分配的结果:

Hello from 'SPRING-CLOUD-EUREKA-CLIENT with Port Number 8081'!
Hello from 'SPRING-CLOUD-EUREKA-CLIENT with Port Number 8082'!
Hello from 'SPRING-CLOUD-EUREKA-CLIENT with Port Number 8081'!

Zuul 收到的每个请求都会以轮询方式转发到不同实例。

启动新实例(如端口 8083)并注册到 Eureka 后,Zuul 自动将其加入负载均衡池:

Hello from 'SPRING-CLOUD-EUREKA-CLIENT with Port Number 8083'!

⚠️ 可通过 Ribbon 配置更改负载均衡策略(详见 Ribbon 文章)。

5. 总结

实践表明,Zuul 为 REST 服务的所有实例提供统一入口,并通过轮询方式实现负载均衡。核心优势包括:

  • ✅ 服务自动发现与注册
  • ✅ 零配置负载均衡
  • ✅ 动态实例感知

完整代码示例请查阅 GitHub 仓库


原始标题:An Example of Load Balancing with Zuul and Eureka