Zuul 是 Netflix 开源的 API 网关组件,主要用于微服务架构中的路由和过滤。在 Spring Cloud 中,Zuul 提供了一种动态路由机制,它将所有外部请求统一路由到具体的微服务中,同时可以对请求进行过滤、限流、鉴权等操作。Zuul 已经进入维护模式,但在一些项目中仍然广泛使用。它的替代者是 Spring Cloud Gateway。
在 Spring Boot 项目中集成 Zuul 需要在 pom.xml
中添加相关的依赖。Zuul 和 Eureka 通常结合使用,因此我们也需要引入 Eureka 的依赖:
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-zuulartifactId>
dependency>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
dependencies>
在 Spring Boot 主应用类中,使用 @EnableZuulProxy
注解来启用 Zuul 代理功能,同时使用 @EnableEurekaClient
来启用 Eureka 客户端。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
@SpringBootApplication
@EnableZuulProxy
@EnableDiscoveryClient
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
@EnableZuulProxy
:启用 Zuul 的 API 网关功能。@EnableDiscoveryClient
:启用 Eureka 的服务发现功能。在 application.yml
文件中,我们需要对 Zuul 和 Eureka 进行一些基础配置:
server:
port: 8080 # Zuul 网关服务运行的端口
spring:
application:
name: zuul-gateway # Zuul 网关服务名称
cloud:
zuul:
routes:
service1:
path: /service1/**
service-id: SERVICE1 # 通过服务名称进行路由
service2:
path: /service2/**
service-id: SERVICE2
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/ # Eureka 服务器地址
在这个配置文件中:
server.port
定义了 Zuul 网关服务的运行端口。spring.cloud.zuul.routes
配置了不同的路由规则。比如,当请求路径为 /service1/**
时,Zuul 会将请求路由到 SERVICE1
微服务。eureka.client.service-url.defaultZone
定义了 Eureka 服务器地址,Zuul 通过 Eureka 动态发现微服务。如果需要使用 Eureka 来进行服务发现,我们需要一个 Eureka 服务端,它可以是一个独立的服务。Eureka 服务端的基本配置如下:
server:
port: 8761
eureka:
client:
register-with-eureka: false # 不注册自己作为客户端
fetch-registry: false # 不从其他Eureka服务器获取服务注册信息
server:
enable-self-preservation: false
spring:
application:
name: eureka-server
当 Zuul 与 Eureka 集成时,它会自动从 Eureka 注册中心获取可用的服务实例,并动态地将请求路由到相应的服务。例如,当我们访问 http://localhost:8080/service1/some-api
时,Zuul 会自动将该请求路由到 Eureka 中注册的 SERVICE1
微服务的实例。
假设 SERVICE1
微服务暴露了一个 API http://localhost:8081/api/hello
,我们可以通过 Zuul 路由访问该服务。
SERVICE1
的 API 定义:import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/api/hello")
public String hello() {
return "Hello from SERVICE1";
}
}
现在我们可以通过 Zuul 网关访问该服务的 API,地址为 http://localhost:8080/service1/api/hello
,Zuul 会将请求路由到 SERVICE1
服务的 /api/hello
。
Zuul 提供了一套过滤器机制,允许开发者在请求的不同阶段(路由前、路由后)对请求进行拦截和处理。常见的过滤器类型有:
我们可以通过继承 ZuulFilter
类来创建自定义过滤器,下面是一个 pre
类型的过滤器示例,它会在请求被路由之前执行:
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Component
public class PreRequestLogFilter extends ZuulFilter {
@Override
public String filterType() {
return "pre"; // 定义过滤器类型为 pre
}
@Override
public int filterOrder() {
return 1; // 定义过滤器的优先级,数字越小优先级越高
}
@Override
public boolean shouldFilter() {
return true; // 是否启用该过滤器
}
@Override
public Object run() {
// 获取当前请求上下文
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
// 记录请求日志
System.out.println("Request Method: " + request.getMethod() + " Request URL: " + request.getRequestURL().toString());
return null;
}
}
在这个过滤器中:
filterType()
:指定过滤器的类型为 pre
,表示在请求路由之前执行。filterOrder()
:定义过滤器的执行顺序。shouldFilter()
:返回 true
,表示启用该过滤器。run()
:过滤器的核心逻辑。在这里,我们记录了每个请求的日志。post
过滤器下面是一个 post
类型的过滤器示例,它会在请求被路由之后执行,并可以对响应进行处理:
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.springframework.stereotype.Component;
@Component
public class PostRequestFilter extends ZuulFilter {
@Override
public String filterType() {
return "post"; // 定义过滤器类型为 post
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
// 可以修改响应内容或添加头信息
ctx.addZuulResponseHeader("X-Response-Header", "PostFilter");
System.out.println("Response Post Filter executed");
return null;
}
}
Zuul 可以与 Hystrix 和 Ribbon 集成,提供限流与熔断功能。当微服务不可用或响应缓慢时,Zuul 可以通过 Hystrix 自动进行熔断和降级处理。
在 application.yml
中启用 Hystrix 支持:
zuul:
ribbon:
eager-load:
enabled: true
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 5000 # 请求超时时间
当某个路由的服务不可用或超时时,Hystrix 会自动触发熔断,避免影响其他服务。
Zuul 的核心功能:Zuul 作为 API 网关,提供了路由和过滤功能,将外部请求转发到内部微服务,并支持动态路由、负载均衡、限流和熔断。
与 Eureka 集成:Zuul 可以与 Eureka 集成,自动发现服务,并将请求路由到可用的服务实例。
自定义过滤器:通过自定义 Zuul 过滤器,可以对请求进行预处理或后处理,常用于日志记录、认证、限流等功能。
限流与熔断:通过与 Ribbon 和 Hystrix 集成,Zuul 可以实现限流、熔断和自动降级功能。
虽然 Zuul 已经进入维护模式,但它仍然是很多项目中稳定且可靠的 API 网关解决方案。对于新项目,推荐使用 Spring Cloud Gateway 作为替代,它在性能和扩展性方面有更好的表现。