Eureka服务发现慢的原因

Eureka服务发现慢有两个原因,一部分是因为服务端缓存导致,一部分是客户端缓存导致。

Eureka注册发现模型如下图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MZmth6ui-1622015677278)(http://106.75.145.234/upload/2021/05/image-4466447f9c174b1db882a89eef4a9d9e.png)]

1)服务端缓存

服务注册到注册中心后,服务实例是存储在注册表(数据存储层【registry】)中的,但是为了提高响应速度,内部加了两层缓存结构将client需要的实例缓存起来,获取的时候通过缓存响应给client。

  • 第一层缓存readOnlyCacheMap
    • readOnlyCacheMap是采用ConcurrentHashMap存储数据的,每30秒与readWriteCacheMap进行数据同步。
  • 第二层缓存 readWriteCacheMap,
    • 采用Guava来实现缓存。缓存过期时间默认为180秒,当服务下线、过期、注册、状态变更都会清楚缓存中的数据。

Client获取服务实例会先从一级缓存(readOnlyCacheMap)中获取,一级缓存不存在再从二级缓存(readWriteCacheMap)中获取,二级缓存不存在,会从存储层(Registry)中拉取数据存入缓存,再返回给Client。

Eureka二级缓存提高Eureka Server的响应速度,弊端是缓存会导致Client获取不到最新的服务实例信息,无法快速发现新服务和已下线服务。

服务端优化方案:

1、缩短只读缓存更新时间
eureka.server.response-cache-update-interval-ms= 10000 # 默认是30秒
2、关闭只读缓存(一级缓存)
eureka.server.use-read-only-response-cache=false

2)客户端缓存

Eureka Client 缓存

通过剖析源码了解到,EurekaClient负责与EurekaServer进行交互,在EurekaClient中的com.netflix.discovery.DiscoveryClient.initScheduledTasks() ⽅法中初始化了⼀
个 CacheRefreshThread 定时任务专⻔⽤来拉取 Eureka Server 的实例信息到本地。

@ImplementedBy(DiscoveryClient.class)
public interface EurekaClient extends LookupService {
...
}

public class DiscoveryClient implements EurekaClient {
...
    DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,
                    Provider<BackupRegistry> backupRegistryProvider) {
        //构造方法中
        // 初始化调度任务(例如,群集解析器,心跳,实例信息复制器,提取
        initScheduledTasks();
...	
}


    private void initScheduledTasks() {
        if (clientConfig.shouldFetchRegistry()) {
            // 对应eureka.client.registry-fetch-interval-seconds: 30 想注册表中获取实例信息的时间间隔
            int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds();
            int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
            scheduler.schedule(
                    new TimedSupervisorTask(
                            "cacheRefresh",
                            scheduler,
                            cacheRefreshExecutor,
                            registryFetchIntervalSeconds,
                            TimeUnit.SECONDS,
                            expBackOffBound,
                            new CacheRefreshThread()
                    ), registryFetchIntervalSeconds, TimeUnit.SECONDS);
        }
	...
    }		

}

image.png

Ribbon会从EurekaClient中获取服务信息,ServerListUpdater是Ribbon中负责服务实例更新的组件,默认实现是PollingServerListUpdater,当负载均衡器初始化的时候会调用PollingServerListUpdater的start(xxx)方法,方法内会获取PollingServerListUpdater的线程池,定期更新实例信息,默认30秒,通过下面命令调短时间,比如3秒。

#服务名.ribbon.ServerListRefreshInterval = xxx毫秒
user-service.ribbon.ServerListRefreshInterval=3000
# 更多参数设置可以通过类com.netflix.client.config.CommonClientConfigKey查看

Ribbon超时配置

# 全局ribbon超时配置
ribbon.ReadTimeout = 3000 
ribbon.ConnectTimeout = 3000
# 单个服务超时配置
user-service.ribbon.ribbon.ReadTimeout = 3000 
user-service.ribbon.ribbon.ConnectTimeout = 3000

Hystrix超时配置

Hystrix的超时配置要大于Ribbon的超市时间,因为Hystrix将请求进行了一层包装,特别注意,如果ribbon开启了重试机制,例如重试3次,Ribbon超时时间为1秒,则Hystrix超时时间必须大于3秒,否则会导致Ribbon还在重试,Hystrix就已经超时。

#全局配置
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=3000
#具体客户端配置
hystrix.command.user-service.execution.isolation.thread.timeoutInMilliseconds=3000
#只对某个方法进行配置
hystrix.command.user-service$getUser(Long).execution.isolation.thread.timeoutInMilliseconds=3000

@HystryxCommand注解指定超时时间

    @HystrixCommand(
            //线程池表示,要保持唯一,否则会共用同一个线程池
            threadPoolKey = "getProviderPortTimeOutFallBack",
            threadPoolProperties = {
                    @HystrixProperty(name = "coreSize", value = "2"),//线程数
                    @HystrixProperty(name = "maxQueueSize", value = "20")//等待队列长度
            },
            commandProperties = {
                    //HystrixCommandProperties.HystrixCommandProperties(HystrixCommandKey, HystrixCommandProperties.Setter, String)
                    @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000")

                    //hystric高级配置,定制工作过程细节,跳闸配置
                    /**
                     * 8秒内请求数达到2个,并且失败率达到50% hystrix跳闸
                     * 每隔3秒放一部分请求通过,如果成功重置断路器,如果失败,继续重试监听请求是否调用成功
                     */
                    //统计时间窗口定义时长
                    , @HystrixProperty(name = "metrics.rollingStats.timeInMilliseconds", value = "8000")
                    //统计时间窗口内最小请求数
                    , @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "2")
                    //统计时间窗口内的错误数量百分比阈值
                    , @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50")
                    //自我修复时的活动窗口时长
                    , @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "3000")
            }, fallbackMethod = "fallBack" //降级回调方法
    )

Feign超时时间设置

Feign也可以配置超时时间,如果Ribbon也配了超时时间就以Ribbon的时间为准,如果没设置Ribbon时间设置了Feign时间,以Feign时间为准。
Feign的时间配置

feign.client.config.user-service.connectTimeout=1000
feign.client.config.user-service.readTimeout=1000
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值