如何基于Zuul与Eureka实现按请求头路由至指定版本微服务实例
实现基于请求头Version的Zuul路由规则
这需求其实很典型,咱们可以通过自定义Zuul过滤器来实现——核心就是在路由阶段拦截请求,根据请求头里的version值,从Eureka注册中心的test服务实例中筛选出metadata-map.version匹配的实例,然后指定路由到该实例。下面给你一步步来:
步骤1:确保依赖配置正确
首先得保证你的项目已经引入了Zuul和Eureka Client的Spring Boot Starter依赖,比如Maven项目的pom.xml:
<dependencies> <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> </dependencies>
步骤2:自定义Zuul路由过滤器
创建一个类继承ZuulFilter,注入DiscoveryClient来获取Eureka中的服务实例列表,在run()方法里实现版本匹配的核心逻辑:
import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import com.netflix.zuul.exception.ZuulException; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import javax.servlet.http.HttpServletRequest; import java.util.List; import java.util.Optional; @Component public class VersionRouteFilter extends ZuulFilter { private final DiscoveryClient discoveryClient; // 构造注入DiscoveryClient public VersionRouteFilter(DiscoveryClient discoveryClient) { this.discoveryClient = discoveryClient; } @Override public String filterType() { // 指定在路由阶段执行,对应Zuul的Route处理阶段 return FilterConstants.ROUTE_TYPE; } @Override public int filterOrder() { // 设置执行顺序,比默认路由过滤器优先级更高(默认是10) return 1; } @Override public boolean shouldFilter() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); // 只对test服务的请求生效,且请求头携带version时才执行 String serviceId = (String) ctx.get(FilterConstants.SERVICE_ID_KEY); return "test".equals(serviceId) && StringUtils.hasText(request.getHeader("version")); } @Override public Object run() throws ZuulException { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); String targetVersion = request.getHeader("version"); // 从Eureka获取test服务的所有健康实例 List<org.springframework.cloud.client.ServiceInstance> instances = discoveryClient.getInstances("test"); if (instances.isEmpty()) { throw new ZuulException("No available test service instance", 503, "Service Unavailable"); } // 筛选出metadata中version匹配的实例 Optional<org.springframework.cloud.client.ServiceInstance> targetInstance = instances.stream() .filter(instance -> targetVersion.equals(instance.getMetadata().get("version"))) .findFirst(); if (targetInstance.isPresent()) { org.springframework.cloud.client.ServiceInstance instance = targetInstance.get(); // 构造目标实例的访问地址,替换Zuul默认路由地址 String targetUrl = String.format("http://%s:%d", instance.getHost(), instance.getPort()); ctx.setRouteHost(java.net.URI.create(targetUrl)); } else { // 无匹配实例时的降级处理 ctx.setResponseStatusCode(404); ctx.setSendZuulResponse(false); ctx.setResponseBody(String.format("No test service instance with version %s found", targetVersion)); } return null; } }
步骤3:配置Zuul基础路由规则
在网关的application.yml里添加基础路由配置,把/test/**路径的请求映射到test服务:
spring: application: name: zuul-gateway eureka: client: service-url: defaultZone: http://localhost:8761/eureka/ # 替换成你的Eureka注册中心地址 zuul: routes: test: path: /test/** service-id: test
测试验证
- 发送带
version:1的请求:curl -H "version:1" http://localhost:网关端口/test/你的接口路径,会被路由到7878端口的实例 - 发送带
version:2的请求:curl -H "version:2" http://localhost:网关端口/test/你的接口路径,会被路由到7879端口的实例
额外说明
- 如果请求头没带
version,可以在shouldFilter()里返回false,让Zuul走默认的轮询路由;也可以在run()里加默认逻辑,比如路由到版本1的实例 DiscoveryClient默认只会返回健康的实例,不用担心路由到已下线的服务
内容的提问来源于stack exchange,提问作者Bhushan
相关产品推荐
相关产品推荐

