微服务项目在使用zuul作为网关时,由于zuul的线程模型,对下游接口必须要设置超时时间和熔断机制,本文主要是叙述设置路由超时的方式。
zuul的超时配置有两种方式:
- 直接使用url配置的路由,是基于httpclient来发送请求,可以直接设置socket的连接时间
- 使用ribbon的轮训机制,可以配置ribbon超时时间,也可以配置hystrix超时时间,两者取配置最小者
直接使用url路由配置事例:
- 适用于ApacheHttpClient,如果是okhttp无效。每个服务的http客户端连接池最大连接,默认是200.
zuul.host.max-total-connections=1000
- 适用于ApacheHttpClient,如果是okhttp无效。每个route可用的最大连接数,默认值是20。
zuul.host.max-per-route-connections=500
zuul.host.socket-timeout-millis=3000
zuul.host.connect-timeout-millis=500
- 使用这种方式时,最好增加一个error的zuulFilter,以便处理异常,返回响应的文案:
public Object run() {
try {
RequestContext ctx = RequestContext.getCurrentContext();
ctx.setSendZuulResponse(false);
ctx.remove("throwable");
ctx.remove("error.status_code");
ctx.getResponse().setContentType("text/html;charset=UTF-8");
ctx.getResponse().setCharacterEncoding("UTF-8");
InputStream inputStream = ctx.getResponseDataStream();
Map<String, Object> returnMap = RequsetReturnValueUtils.createReturnMap(
1, "服务器异常", new JSONObject());
String returnJson = JSON.toJSONString(returnMap);
InputStream inputStream ;
inputStream = new ByteArrayInputStream(returnJson.getBytes("utf-8"));
ctx.setResponseDataStream(inputStream);
ctx.setResponseStatusCode(200);
ctx.setResponseGZipped(false);
} catch (Exception ex) {
logger.error("CurrencyErrorFilter run(){}" ,ExceptionUtils.getFullStackTrace(ex));
}
return null;
}
重点说下使用ribbon的配置
基于ribbon的serviceid的配置,可以用到hystrix等的降级、熔断等功能,hystrix按照serviceid 设置,同默认设置的优先级,由低到高:
1. hystrix.command.default.XXX
2. hystrix.command.[HystrixCommandKey].XXX
3. Hystrix代码内置属性参数值
具体配置代码如下:
zuul.routes.api-v1-config.path=/gw-passenger/config/**
api-v1-config.ribbon.listOfServers=http://test-inside-mp.***.com
zuul.routes.api-v1-config.sensitiveHeaders="*"
hystrix.command.api-v1-config.execution.isolation.thread.timeoutInMilliseconds=1000
hystrix.command.api-v1-config.execution.isolation.semaphore.maxConcurrentRequests=100 单独配置
最好增加fallback的实现,以便在超时时增加自定义处理逻辑
/**
* 熔断fallback
*/
@Component
public class MyZuulFallbackProvider implements ZuulFallbackProvider {
private static Logger logger = LoggerFactory.getLogger(MyZuulFallbackProvider.class);
@Override
public String getRoute() {
return "*";//所有路由
}
@Override
public ClientHttpResponse fallbackResponse() {
logger.error("route fallback");
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return 200;
}
@Override
public String getStatusText() throws IOException {
return "OK";
}
@Override
public void close() {
}
@Override
public InputStream getBody() throws IOException {
JSONObject returnObject = new JSONObject();
returnObject.put("code", 1);
returnObject.put("msg", "接口降级");
returnObject.put("time", System.currentTimeMillis());
return new ByteArrayInputStream(returnObject.toJSONString().getBytes("utf-8"));
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
return headers;
}
};
}
}
要使用hystrix的超时fallback,必须设置:
hystrix.command.default.execution.timeout.enabled=true 默认开启
hystrix相关配置
设置隔离方式
execution.isolation.strategy= THREAD|SEMAPHOREzuul.semaphore.max-semaphores
是一个绝对值,无时间窗口,相当于亚毫秒级的。当请求达到或超过该设置值后,其余请求会被拒绝。默认100,这个值最好是根据每个后端服务的访问情况,单独设置。execution.isolation.thread.timeoutInMilliseconds
用来设置thread和semaphore两种隔离策略的超时时间,默认值是1000。可以针对service-id单独设置。
单独设置时,要根据所对应的业务和服务器所能承受的负载来设置,一般是大于平均响应时间的20%~100%,最好是根据压力测试结果来评估,这个值设置太大,会导致线程不够用而会导致太多的任务被fallback;设置太小,一些特殊的慢业务失败率提升,甚至会造成这个业务一直无法成功,在重试机制存在的情况下,反而会加重后端服务压力。execution.isolation.semaphore.maxConcurrentRequests
指任意时间点允许的并发数。当请求达到或超过该设置值后,其其余就会被拒绝。默认值是100。execution.timeout.enabled
要使用hystrix的超时fallback,必须设置,默认开启execution.isolation.thread.interruptOnTimeout
发生超时是是否中断线程,默认是true。execution.isolation.thread.interruptOnCancel
取消时是否中断线程,默认是false。
*hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests
设置fallback的线程数,默认是10,这个值在大量触发fallback逻辑时要注意调整。
总结:
spring cloud zuul 使用起来还是非常方便的,通过hystirx也能达到一些降级和熔断的功能,其pre、route、post三阶段机制很大程度上方便了我们做扩展,但是在路由配置方面不大方便,可以结合spring cloud config 或者直接通过统一配置来添加路由。