开发环境
框架 | 版本 |
---|---|
Springcloud | 2020.0.2 |
Nacos | 2.2.1.RELEASE |
实现目标
由于loadbalancer
等负载均衡的缓存默认是30s,因此如果发生了服务上下线
,服务消费端不能够第一时间知道获取,会产生调用远程服务
失败等情况。因此我们想要实现当服务上下线时,能够第一时间通知到服务消费端,另其做出一系列操作。
主要思路
我的思路是利用Nacos
的监听服务变换接口,修改loadbalance的缓存列表,来实现实时修改服务缓存列表
阅读源码
通过查看org.springframework.cloud.loadbalancer.config
包下的配置类发现了两个类
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Caffeine.class, CaffeineCacheManager.class })
protected static class CaffeineLoadBalancerCacheManagerConfiguration {
@Bean(autowireCandidate = false)
@ConditionalOnMissingBean
LoadBalancerCacheManager caffeineLoadBalancerCacheManager(LoadBalancerCacheProperties cacheProperties) {
return new CaffeineBasedLoadBalancerCacheManager(cacheProperties);
}
}
@Configuration(proxyBeanMethods = false)
@Conditional(OnCaffeineCacheMissingCondition.class)
@ConditionalOnClass(ConcurrentMapWithTimedEviction.class)
protected static class DefaultLoadBalancerCacheManagerConfiguration {
@Bean(autowireCandidate = false)
@ConditionalOnMissingBean
LoadBalancerCacheManager defaultLoadBalancerCacheManager(LoadBalancerCacheProperties cacheProperties) {
return new DefaultLoadBalancerCacheManager(cacheProperties);
}
}
可以看到在默认的情况下,Spring LoadBalancer会使用DefaultLoadBalancerCacheManager
实现类来作为缓存管理,那么我们就来看看其源码吧
public class DefaultLoadBalancerCacheManager implements LoadBalancerCacheManager {
private final ConcurrentMap<String, Cache> cacheMap = new ConcurrentHashMap<>(16);
public DefaultLoadBalancerCacheManager(LoadBalancerCacheProperties loadBalancerCacheProperties,
String... cacheNames) {
cacheMap.putAll(createCaches(cacheNames, loadBalancerCacheProperties).stream()
.collect(Collectors.toMap(DefaultLoadBalancerCache::getName, cache -> cache)));
}
public DefaultLoadBalancerCacheManager(LoadBalancerCacheProperties loadBalancerCacheProperties) {
this(loadBalancerCacheProperties, SERVICE_INSTANCE_CACHE_NAME);
}
private Set<DefaultLoadBalancerCache> createCaches(String[] cacheNames,
LoadBalancerCacheProperties loadBalancerCacheProperties) {
return Arrays.stream(cacheNames).distinct()
.map(name -> new DefaultLoadBalancerCache(name,
new ConcurrentHashMapWithTimedEviction<>(loadBalancerCacheProperties.getCapacity(),
new DelayedTaskEvictionScheduler<>(aScheduledDaemonThreadExecutor())),
loadBalancerCacheProperties.getTtl().toMillis(), false))
.collect(Collectors.toSet());
}
private ScheduledExecutorService aScheduledDaemonThreadExecutor() {
return Executors.newSingleThreadScheduledExecutor(runnable -> {
Thread thread = Executors.defaultThreadFactory().newThread(runnable);
thread.setDaemon(true);
return thread;
});
}
@Override
@Nullable
public Cache getCache(String name) {
return cacheMap.get(name);
}
@Override
public Collection<String> getCacheNames() {
return Collections.unmodifiableSet(cacheMap.keySet());
}
}
可以看到存储缓存列表就是通过cacheMap
这一对象实现,因此如果我们将其缓存Map清除,就能够实现实时修改缓存服务列表的效果了,我们可以通过反射拿到其cacheMap进行clear操作。
主要代码实现
@Override
public void onEvent(Event event) {
try {
if (event instanceof NamingEvent){
nacosWeightRandomLoadBanlancer = (NacosWeightRandomLoadBanlancer) applicationContext.getBean(ReactorLoadBalancer.class);
LoadBalancerCacheManager loadBalancerCacheManager = applicationContext.getBean(LoadBalancerCacheManager.class);
log.info("bean:{}",loadBalancerCacheManager);
ConcurrentMap<String, Cache> cacheMap = (ConcurrentMap<String, Cache>) ReflectUtil.getFieldValue(loadBalancerCacheManager, "cacheMap");
for (Map.Entry<String, Cache> stringCacheEntry : cacheMap.entrySet()) {
String key = stringCacheEntry.getKey();
Cache cache = stringCacheEntry.getValue();
log.info("key:{}\tvalue:{}",key,cache);
cache.clear();
}
}
} catch (Exception e) {
log.error("e:{}",e);
e.printStackTrace();
}
}
}
通过实现Nacos监听服务变换接口,来清除缓存服务列表,从而实现实时服务上下线的监听变换。
提取优化
待写