
前言
目前所有的服务都是基于springboot/springcloud体系,注册中心使用eureka。
背景:
-
新的功能上线后,特别是大版本上线,不能完全保证新功能的质量,特别是对历史用户,数据差异性比较大,又无法复现;
-
为了测试哪套流程的转化率比较高;
想要的效果
这里就会有以下几个问题:
-
如果识别是灰度用户?
-
如何识别是灰度服务?
-
如何让灰度用户走灰度服务?
-
如何让灰度标签传递下去?
系统访问如下:
-
用户可以通过网关打上灰度标签;
-
用户可以根据网关路由是走api还是走service;
-
用户可以根据自定义路由走灰度还是走普通服务;
如何识别灰度用户?
用户在网关里鉴权完成后,根据用户的属性来动态判断;
目前项目里写死了三个用户信息,token分别是:abc,bcd,其他;
其中:
abc: userId = 1 name = 'yxkong' mobile = '15600000269'
bcd: userId = 901 name = '鱼翔空' mobile = '15600000279'
其他:userId = 901 name = '火星' mobile = '15600000270'
private String getUserInfo(String token){
if("abc".equals(token)){
return "{\"userId\":\"1\",\"name\":\"yxkong\",\"mobile\":\"15600000269\"}";
}else if("bcd".equals(token)){
return "{\"userId\":\"901\",\"name\":\"鱼翔空\",\"mobile\":\"15600000279\"}";
}else {
return "{\"userId\":\"901\",\"name\":\"火星\",\"mobile\":\"15600000270\"}";
}
}
灰度拦截策略是:
mobile 取模是6 或9
userId 大于等于900
rules.add(new LoadBalancerRule("mobile","mod10","6,9","and"));
rules.add(new LoadBalancerRule("userId","gte","900","and"));
如何识别灰度服务?
利用eureka的metadata-map自定义元数据
eureka:
:
:
version: 2.0
label: gray
# 访问地址(注意,postman访问最好,或者curl,直接chrome访问把xml都过滤l )
http://127.0.0.1:8765/eureka/apps/
可以参考下 EurekaInstanceConfigBean,在 instance配置中有个metadataMap 这里可以放置自定义的元数据
package org.springframework.cloud.netflix.eureka;
@ConfigurationProperties("eureka.instance")
public class EurekaInstanceConfigBean implements CloudEurekaInstanceConfig, EnvironmentAware {
private int leaseRenewalIntervalInSeconds = 30;
private int leaseExpirationDurationInSeconds = 90;
private String virtualHostName = "unknown";
private String instanceId;
private String secureVirtualHostName = "unknown";
private String aSGName;
private Map<String, String> metadataMap = new HashMap();
}
如何让灰度用户走灰度服务?
重写ribbon的RoundRobinRule负载均衡策略,在负载均衡的时候,走到对应的服务上。
1,判断是否是灰度用户;
2,拿到灰度服务进行路由;
如何让灰度标签传递下去?
通过spring的Interceptor在进入spring的接口请求时,将灰度标签从header中获取并初始化到HystrixRequestContext中,本质上是保存在ThreadLocal中
(Servlet.class)
public class ServletAutoConfiguration {
(WebHandler.class)
public WebMvcConfigurer configurer() {
return new WebConfig();
}
class WebConfig implements WebMvcConfigurer {
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(interceptor()).addPathPatterns("/**");
}
private HandlerInterceptor interceptor() {
return new HandlerInterceptor() {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
GrayHolder.initHystrixRequestContext(request.getHeader(GrayHolder.LABEL_KEY),request.getHeader(GrayHolder.VERSION_KEY));
return true;
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
GrayHolder.shutdownHystrixRequestContext();
}
};
}
}
}
文章评论