Nacos+Springloadbalancer实现快速上下线

开发环境

框架 版本
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监听服务变换接口,来清除缓存服务列表,从而实现实时服务上下线的监听变换。

提取优化

待写

猜你喜欢

转载自juejin.im/post/7116743190681485349