spring-cloud架构的几个概念理解
ribbon的重试功能
当A服务部署在N台服务器上,ribbon路由到A(n1)…的过程中,如果A(n1)实例异常关闭,则通过配置重试机制可以向后搜索可用的实例,配置项如下:
zuul.retryable=true
ribbon.eager-load.enabled=true
ribbon.restclient.enabled=true
ribbon.ReadTimeout=3000
ribbon.ConnectTimeout=3000
ribbon.MaxAutoRetries=0
ribbon.MaxAutoRetriesNextServer=3
ribbon.okToRetryOnAllOperations=true
结合redis实现限流功能
限流有三种实现算法:计数器,漏桶,令牌桶。计数器结合redis的实现如下:
zuul.routes.demo1.path=/demo1/**
zuul.routes.demo1.serviceId=demo1
zuul.routes.demo1.sensitive-headers=
zuul.routes.demo1.custom-sensitive-headers=true
zuul.routes.demo1.strip-prefix=true
zuul.routes.demo1.qps=10
@Override
public Object run() throws ZuulException {
RequestContext ctx = RequestContext.getCurrentContext();
String key = ctx.getRequest().getRequestURI();
String serviceId = key.substring(0, key.indexOf("/", 1));
synchronized (this){
String prop = String.format("zuul.routes.%s.qps", serviceId);
if (environment.containsProperty(prop)) {
String s = environment.getProperty(prop);
long limit = Objects.nonNull(s) ? Long.parseLong(s) : Integer.MAX_VALUE;
long v = redisService.increment(key, 1, 1);
if (v > limit) {
throw new ZuulException(ZuulEnums.STATUS_BUSY_SERVICE_TEXT.getValue(), ZuulEnums.STATUS_BUSY_SERVICE.getIntValue(), "");
}
}
}
return null;
}
当所有的服务An都不可用时,尝试降级
当所有服务不可用时进行降级,降级处理仍然是不能正常提供服务的,只是一种异常的特殊响应。使用FallbackProvider实现降级的代码如下:
public class ZuulFallback implements FallbackProvider {
private String route;
public ZuulFallback(String route){
this.route = route;
}
@Override
public String getRoute() {
return this.route;
}
@Override
public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
return new ClientHttpResponse() {
@Override
public HttpStatus getStatusCode() throws IOException {
return HttpStatus.OK;
}
@Override
public int getRawStatusCode() throws IOException {
return HttpStatus.OK.value();
}
@Override
public String getStatusText() throws IOException {
return HttpStatus.OK.getReasonPhrase();
}
@Override
public void close() {
}
@SuppressWarnings("unchecked")
@Override
public InputStream getBody() throws IOException {
BaseResult baseResult = new BaseResult();
baseResult.setStatus(ZuulEnums.STATUS_DOWN_SERVER.getValue());
baseResult.setMessage(ZuulEnums.STATUS_DOWN_SERVER_TEXT.getValue());
baseResult.setSuccess(false);
baseResult.setData(new JSONObject());
baseResult.setTimestamp(System.currentTimeMillis());
return new ByteArrayInputStream(JSONObject.toJSONBytes(baseResult, SerializerFeature.WriteMapNullValue));
}
@Override
public HttpHeaders getHeaders() {
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8);
return httpHeaders;
}
};
}
}
亲测经历
启动的项目有:
注册中心:http://localhost:10010/
网关服务:http://localhost:10012/
接口地址:/demo1/index/test
demo1项目部署两台服务,端口号分别为10013、20013;使用睡眠的方式让/index/test睡眠200ms,目的是让接口平均响应速度在200ms出头一点
启动所有服务,执行测试脚本:
ab -n 10000 -c 1 -p d:/pdata.json -T 'application/json' http://localhost:10012/demo1/index/test
过程中,关闭端口号20013的服务(直接关闭进程,非shutdown的方式),ribbon重试正常,没有异常响应。
由于接口调用差不多需要215ms左右,所以按每秒处理不到5个请求的来算,如果把线程数调整为3(4*3=12>10),因此理论上会报限流异常信息。执行测试脚本:
ab -n 10000 -c 3 -p d:/pdata.json -T 'application/json' http://localhost:10012/demo1/index/test
过程中,限流异常正常响应,观察处理正常的请求数量确实每秒10个。
现在为了观察降级的响应,手动将10013和20013服务都关闭掉,可以观察接口响应的内容确实为降级响应的内容
或者,将限流QPS调整为99999,同时将测试脚本的线程数故意加大,比如100个甚至更多,此时也可以观察到由于demo1单体服务能力的不足导致接口返回降级响应的内容