在 Spring Cloud Gray 由 spring-cloud-gray-client(角色灰度客户端),spring-cloud-gray-client-netflix 和 spring-cloud-tray-server,spring-cloud-gray-webui 组成。
spring-cloud-gray-client 定义了一套灰度路由决策模型,灰度信息追踪模型,以及和 spring-cloud-gray-server 的根本通信性能。
spring-cloud-gray-client-netflix 在 spring-cloud-gray-client 的根底上集成了微服务注册核心 eureka,扩大 ribbon 的负载平衡规定,提供了对 zuul,feign,RestTemplate 的灰度路由能力,并且无缝反对 hystrix 线程池隔离。
spring-cloud-gray-server 负责灰度决策、灰度追踪等信息的治理以及长久化。
注册核心 负责服务的注册和发现。
灰度客户端 灰度的客户端是指依赖了 spring-cloud-gray-client 的服务,个别是指服务生产方。
灰度管控端 负责灰度信息的治理、长久化等保护工作。
灰度客户端会从灰度管控端拉取一份灰度信息的清单,并在内存中保护这份清单信息,清单中蕴含服务,服务实例,灰度策略,灰度追踪字段等。当申请达到网关时,网关就会在灰度追踪中将须要透传的信息记录下来,并将传递给转发的服务实例,前面的接口调用也会依照同样的逻辑将追踪信息透传下去,从而保障所有一个申请在微服务调用链中的灰度路由。
public class CustomMetadataRule extends ZoneAvoidanceRule {
// 检测灰度开关是否启动
private HttpResult checkGraySwitch() {
String url = "http://127.0.0.1:6015/eureka/apps/switch";
HttpResult result = new HttpResult();
result.statusCode = 500;
try {result = HttpClient.get(url, null);
} catch (Exception e1) {e1.printStackTrace();
}
return result;
}
@Override
public Server choose(Object key) {
// 获取是否存在存活的服务可调用
List<Server> serverList = this.getPredicate().getEligibleServers(this.getLoadBalancer().getAllServers());
// 获取不到服务
if (CollectionUtils.isEmpty(serverList)) {return null;}
// 获取灰度开关是否启动
HttpResult result = checkGraySwitch();
// 灰度开关被设置成敞开状态, 默认走空 metadata 或者是特定标识是失常的服务, 轮询拜访
Boolean isOpen = Boolean.parseBoolean(JSONObject.parseObject(result.content).getString("errorMsg"));
if (result.statusCode == 200 && !isOpen) {
isOpen = true;
return RoundRobinRuleBySelf.getInstance().choose(this.getLoadBalancer(), key,isOpen);
}
// 灰度公布启动状态, 未被设置成灰度对象, 默认走空 metadata 或者是特定标识是失常的服务, 轮询拜访
if (StringUtils.isEmpty(CoreHeaderInterceptor.label.get())) {
isOpen = false;
return RoundRobinRuleBySelf.getInstance().choose(this.getLoadBalancer(), key,isOpen);
}
// 灰度公布启动状态, 被设置成灰度对象, 走空特定标识的服务, 轮询拜访
return RoundRobinRuleBySelf.getInstance().choose(this.getLoadBalancer(), key,!isOpen);
}
}
feignClient 调用 flag 位透传的问题
public class CoreFeignRequestInterceptor implements RequestInterceptor {
private static final Logger logger = LoggerFactory.getLogger(CoreHttpRequestInterceptor.class);
@Override
public void apply(RequestTemplate template) {String header = StringUtils.collectionToDelimitedString(CoreHeaderInterceptor.label.get(),CoreHeaderInterceptor.HEADER_LABEL_SPLIT);
String tag = CoreHeaderInterceptor.tag.get();
template.header(CoreHeaderInterceptor.HEADER_LABEL, header).header(CoreHeaderInterceptor.HEADER_TAG, tag);
logger.info("label:" + header + "tag :" + tag);
}
}
HttpRequest 调用 flag 位透传的问题
public class CoreHttpRequestInterceptor implements ClientHttpRequestInterceptor {
private static final Logger logger = LoggerFactory.getLogger(CoreHttpRequestInterceptor.class);
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {HttpRequestWrapper requestWrapper = new HttpRequestWrapper(request);
String header = StringUtils.collectionToDelimitedString(CoreHeaderInterceptor.label.get(), CoreHeaderInterceptor.HEADER_LABEL_SPLIT);
String tag = CoreHeaderInterceptor.tag.get();
logger.info("label:"+header + "tag :" + tag);
HttpHeaders headers = requestWrapper.getHeaders();
headers.add(CoreHeaderInterceptor.HEADER_LABEL, header);
headers.add(CoreHeaderInterceptor.HEADER_TAG, tag);
return execution.execute(requestWrapper, body);
}
}