Zuul路由前置调用限流服务的Java代码实现方案咨询
Zuul前置限流过滤器实现方案
首先修正你配置中的语法问题:新增的throttle路由缩进错误、key后缺少冒号,修正后配置如下:
routes: silver: path: "/silver/**" serviceId: "SILVER-MICO-SERVICE" stripPrefix: true gold: path: "/gold/**" serviceId: "GOLD-MICO-SERVICE" stripPrefix: false throttle: path: "/throttle/**" serviceId: "THROTTLE-MICO-SERVICE" stripPrefix: false
1. 自定义Zuul前置过滤器实现
你需要使用pre类型的Zuul过滤器,它会在请求转发到目标服务之前执行,优先级设置高于路由转发过滤器即可,代码示例如下:
import com.netflix.zuul.ZuulFilter; import com.netflix.zuul.context.RequestContext; import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; @Component public class ThrottleFilter extends ZuulFilter { // 注入支持负载均衡的RestTemplate @Resource private RestTemplate restTemplate; // 过滤器类型:pre代表前置执行 @Override public String filterType() { return FilterConstants.PRE_TYPE; } // 过滤器优先级,数值越小优先级越高,此处设置为高于路由转发过滤器 @Override public int filterOrder() { return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1; } // 过滤器生效规则:仅对silver相关请求生效 @Override public boolean shouldFilter() { RequestContext ctx = RequestContext.getCurrentContext(); String requestUri = ctx.getRequest().getRequestURI(); return requestUri.startsWith("/silver/"); } // 核心限流逻辑 @Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); // 获取请求特征信息传给限流服务,可根据业务需求扩展 String clientIp = getClientIp(request); String requestPath = request.getRequestURI(); try { // 通过服务名调用限流服务,依赖RestTemplate的负载均衡能力 Boolean passFlag = restTemplate.getForObject( "http://THROTTLE-MICO-SERVICE/throttle/check?ip={ip}&path={path}", Boolean.class, clientIp, requestPath ); // 限流不通过直接返回错误,不转发到目标服务 if (!Boolean.TRUE.equals(passFlag)) { ctx.setSendZuulResponse(false); ctx.setResponseStatusCode(429); ctx.getResponse().setContentType("application/json;charset=utf-8"); ctx.setResponseBody("{\"code\":429,\"msg\":\"请求过于频繁,请稍后再试\"}"); } } catch (Exception e) { // 限流服务调用异常的降级逻辑,可根据业务需求调整为放行/拦截 ctx.setSendZuulResponse(false); ctx.setResponseStatusCode(500); ctx.setResponseBody("{\"code\":500,\"msg\":\"系统繁忙,请稍后再试\"}"); } return null; } // 获取客户端真实IP工具方法 private String getClientIp(HttpServletRequest request) { String ip = request.getHeader("X-Forwarded-For"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } if (ip != null && ip.contains(",")) { ip = ip.split(",")[0].trim(); } return ip; } }
2. 配套配置
需要在Spring配置类中注入带负载均衡能力的RestTemplate:
import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class RestTemplateConfig { @Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); } }
其他说明
- 如果不需要对外暴露限流服务接口,可以直接删掉配置中的throttle路由,过滤器直接通过服务名调用即可,不会对外暴露限流服务端点
- 如果需要对gold接口也做限流,只需要修改
shouldFilter方法的判断逻辑即可 - 限流服务的入参、返回值可根据业务需求调整,比如新增用户ID、请求方法等判断维度
内容的提问来源于stack exchange,提问作者Tony
相关产品推荐
相关产品推荐

