小知识,大挑战!本文正在参与“程序员必备小知识”创作活动
使用Ribbon完成应用灰度发布
定义RibbonRequestContext
public class RibbonRequestContext {
private final Map<String, String> attr = new HashMap<>();
public String put(String key, String value) {
return attr.put(key, value);
}
public String remove(String key) {
return attr.remove(key);
}
public String get(String key) {
return attr.get(key);
}
}
维护一个map,用于透传解析的请求信息。
RibbonRequestContextHolder:
public class RibbonRequestContextHolder {
private static ThreadLocal<RibbonRequestContext> holder = new ThreadLocal<RibbonRequestContext>() {
@Override
protected RibbonRequestContext initialValue() {
return new RibbonRequestContext();
}
};
public static RibbonRequestContext getCurrentContext() {
return holder.get();
}
public static void setCurrentContext(RibbonRequestContext context) {
holder.set(context);
}
public static void clearContext() {
holder.remove();
}
}
持有自定义的RibbonRequestContext
然后定义GrayRequestInterceptor
public class GrayInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
if (request.getHeaders().containsKey("Gray")) {
String value = request.getHeaders().getFirst("Gray");
if (value.equals("true")) {
RibbonRequestContextHolder.getCurrentContext().put("Gray", Boolean.TRUE.toString());
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
attributes.setAttribute("Gray", Boolean.TRUE.toString(), 0);
}
}
return execution.execute(request, body);
}
}
GrayRequestInterceptor加入到RestTemplate
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getInterceptors().add(new GrayInterceptor());
return restTemplate;
}
@Bean
public IRule myRule() {
return new GrayRule();
}
自定义IRule:
ublic class GrayRule extends AbstractLoadBalancerRule {
private Random random = new Random();
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
@Override
public Server choose(Object key) {
boolean grayInvocation = false;
try {
String grayTag = RibbonRequestContextHolder.getCurrentContext().get("Gray");
if(!StringUtils.isEmpty(grayTag) && grayTag.equals(Boolean.TRUE.toString())) {
grayInvocation = true;
}
List<Server> serverList = this.getLoadBalancer().getReachableServers();
List<Server> grayServerList = new ArrayList<>();
List<Server> normalServerList = new ArrayList<>();
for(Server server : serverList) {
NacosServer nacosServer = (NacosServer) server;
if(nacosServer.getMetadata().containsKey("gray") && nacosServer.getMetadata().get("gray").equals("true")) {
grayServerList.add(server);
} else {
normalServerList.add(server);
}
}
if(grayInvocation) {
return grayServerList.get(random.nextInt(grayServerList.size()));
} else {
return normalServerList.get(random.nextInt(normalServerList.size()));
}
} finally {
RibbonRequestContextHolder.clearContext();
}
}
}
服务提供者通过spring.cloud.nacos.discovery.metadata.gray=false 配置项决定元数据内容
最后发起服务调用的时候传递Header中的Gray信息,如果为true,只会路由到灰度实例,否则路由到正常实例。
OpenFeign配置拦截:
public class GrayRequestInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
if (template.headers().containsKey("Gray")) {
String value = template.headers().get("Gray").iterator().next();
if (value.equals("true")) {
RibbonRequestContextHolder.getCurrentContext().put("Gray", Boolean.TRUE.toString());
}
}
}
}