Nacos+Springloadbalancer实现快速上下线
开发环境
| 框架 |
版本 |
| Springcloud |
2020.0.2 |
| Nacos |
2.2.1.RELEASE |
实现目标
由于loadbalancer等负载均衡的缓存默认是30s,因此如果发生了服务上下线,服务消费端不能够第一时间知道获取,会产生调用远程服务失败等情况。因此我们想要实现当服务上下线时,能够第一时间通知到服务消费端,另其做出一系列操作。
主要思路
我的思路是利用Nacos的监听服务变换接口,修改loadbalance的缓存列表,来实现实时修改服务缓存列表
阅读源码
通过查看org.springframework.cloud.loadbalancer.config包下的配置类发现了两个类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| @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实现类来作为缓存管理,那么我们就来看看其源码吧
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| 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操作。
主要代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @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监听服务变换接口,来清除缓存服务列表,从而实现实时服务上下线的监听变换。
提取优化
待写