背景
根据业务需求,我需要自定义负载均衡策略,并根据一定的规则智能路由匹配到相应的服务并返回。代码如下:
DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
String name = loadBalancer.getName();
NamingService namingService = this.nacosDiscoveryProperties.namingServiceInstance();
// 获取指定服务名所有健康实例列表
List<Instance> instances = namingService.selectInstances(name, true);
if (CollectionUtils.isEmpty(instances)) {
return null;
}
//取请求头的值
String headValue = (String) ThreadAttributes.getThreadAttribute(REQUEST_HEADER_NAME);
//没有标识 走默认路由规则
if(StringUtils.isBlank(headValue)){
return new NacosServer(getBenchMarkInstance(instances));
}
List<Instance> metadataMatchInstances = instances.stream()
.filter(instance -> Objects.equals(headValue,instance.getMetadata().get(REQUEST_HEADER_NAME)))
.collect(Collectors.toList());
当调用getLoadBalancer()时,返回的总是匹配到上一次正确的服务,简单来说:假如现在有A、B、C三个服务,当第一次请求A服务的时候可以正常返回对应的A服务,第二次请求B服务的时候,getLoadBalancer()返回的服务实例还是A服务的实例,而且重启之后又会一直返回B服务,此时大家都反馈路由错了的问题。我当时感觉就是当出现线程安全问题的时候才可能出现这种情况。
原因
网关的路由策略出现线程安全问题,因为Iloadbalance是成员变量,而Spring bean实例默认是singleton的,所以有线程安全问题,改为prototype之后,每次都是新的bean,新的对象,新创建的成员变量,所以解决了线程安全问题。
解决办法
加上@Scope(value=“prototype”)即可。