ribbon中使用HystrixRequestVariableDefault的一个注意事项
ribbon中自定义路由使用HystrixRequestVariableDefault
最近在研究SpringCloud的灰度发布方案,具体方案是在zuul过滤器中判断哪些用户属于灰度用户,通过HystrixRequestVariableDefault实现跨线程池传递线程变量,然后重写ribbon的路由策略,转发到对应的server,流程如下:
请求==》zuul==》zuul拦截器==》自定义ribbon负载均衡算法==》灰度服务
各部分代码简化如下:
PreRequestGrayFilter :
public class PreRequestGrayFilter extends ZuulFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(PreRequestGrayFilter.class);
// 返回过滤器的类型
// 可选项:pre, route, post, error
@Override
public String filterType() {
return "pre";
}
// 返回一个int值来指定过滤器的执行顺序
// 不同的过滤器允许返回相同的数字
@Override
public int filterOrder() {
return 1;
}
// 返回一个boolean值来判断该过滤器是否要执行
@Override
public boolean shouldFilter() {
return true;
}
// 过滤器具体逻辑
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
String gray=request.getParameter("gray");
//add header
ctx.addZuulRequestHeader("gray-head", gray);
// HystrixRequestContext.initializeContext();
if (StringUtils.isNotEmpty(gray)){
GrayHolder.setGray();
}
}
GrayHolder
public class GrayHolder {
private static HystrixRequestVariableDefault<String> gray ;
/* static {
System.out.println("init holder");
}*/
public static String isGray(){
return gray.get();
}
public static void setGray(){
HystrixRequestContext.initializeContext();
gray = new HystrixRequestVariableDefault<>();
gray.set("true");
}
}
使用HystrixRequestVariableDefault时需要注意,需要先调用HystrixRequestContext.initializeContext()进行初始化,不然会报空指针。
最后时路由策略:
public class GrayMetadataRule extends ZoneAvoidanceRule {
private Random random = new Random();
@Override
public Server choose(Object key) {
String isgray=GrayHolder.isGray();
List<Server> serverList = this.getPredicate().getEligibleServers(this.getLoadBalancer().getAllServers(), key);
List<Server> backList= serverList.stream().filter(server -> {
Map<String, String> metadata = ((DiscoveryEnabledServer) server).getInstanceInfo().getMetadata();
String metaVersion = metadata.get("gray");
return isgray.equals(metaVersion);
}).collect(Collectors.toList());
return backList.get(random.nextInt(backList.size()));
// return super.choose(key);
}
}
在application.yml中配置
service-one:
ribbon:
NFLoadBalancerRuleClassName: com.zhang.springcloud.zuul.ribbon.GrayMetadataRule
遇到的问题
在GrayMetadataRule中调用GrayHolder.isGray()时发现取到一个null,经过debug调试,发现在preFilter中使用的HystrixRequestVariableDefault实例和后续GrayMetadataRule中调用get时不是同一个实例,怀疑GrayMetadataRule没有通过spring进行加载初始化,后续在GrayHolder 中追加了一个静态块
static {
System.out.println("init holder");
}
证实GrayHolder被初始化了两次,怀疑是通过配置文件配置的GrayMetadataRule通过不同的方式进行的类加载,后续修改成SpringBean的形式
@Bean
public IRule ribbonRule() {
return new GrayMetadataRule();//这里配置策略,和配置文件对应
}
问题得到解决