SpringCloud常⻅问题及解决方案

1. Eureka 服务发现慢问题

如果你刚刚接触Eureka,对Eureka的设计和实现都不是很了解,可能就会遇到一些无法快速解决的问 题,这些问题包括:新服务上线后,服务消费者不能访问到刚上线的新服务,需要过一段时间后才能访 问?或是将服务下线后,服务还是会被调用到,一段时候后才彻底停止服务,访问前期会导致频繁报 错?这些问题还会让你对 Spring Cloud 产生严重的怀疑,这难道不是一个 Bug?

问题场景: 上线一个新的服务实例,但是服务消费者无感知,过了一段时间才知道 某一个服务实例下线了,服务消费者无感知,仍然向这个服务实例在发起请求

这其实就是服务发现的一个问题,当我们需要调用服务实例时,信息是从注册中心Eureka获取的,然后 通过Ribbon选择一个服务实例发起调用,如果出现调用不到或者下线后还可以调用的问题,原因肯定是 服务实例的信息更新不及时导致的。

Eureka 服务发现慢的原因

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

在这里插入图片描述

  1. 服务端缓存

服务注册到注册中心后,服务实例信息是存储在注册表中的,也就是内存中。但Eureka为了提高响应速 度,在内部做了优化,加入了两层的缓存结构,将Client需要的实例信息,直接缓存起来,获取的时候直接从缓存中拿数据然后响应给 Client。 ​ 第一层缓存是readOnlyCacheMap,readOnlyCacheMap是采用ConcurrentHashMap来存储数据的,主要负责定时与readWriteCacheMap进行数据同步,默认同步时间为30秒一次。
第二层缓存是readWriteCacheMap,readWriteCacheMap采用Guava来实现缓存。缓存过期时间默认 为180秒,当服务下线、过期、注册、状态变更等操作都会清除此缓存中的数据。
Client获取服务实例数据时,会先从一级缓存中获取,如果一级缓存中不存在,再从二级缓存中获取, 如果二级缓存也不存在,会触发缓存的加载,从存储层拉取数据到缓存中,然后再返回给 Client。
Eureka 之所以设计二级缓存机制,也是为了提高 Eureka Server 的响应速度,缺点是缓存会导致 Client 获取不到最新的服务实例信息,然后导致无法快速发现新的服务和已下线的服务。
了解了服务端的实现后,想要解决这个问题就变得很简单了,我们可以缩短只读缓存的更新时间 (eureka.server.response-cache-update-interval-ms)让服务发现变得更加及时,或者直接将只读缓 存关闭(eureka.server.use-read-only-response-cache=false),多级缓存也导致C层面(数据一致 性)很薄弱。
Eureka Server 中会有定时任务去检测失效的服务,将服务实例信息从注册表中移除,也可以将这个失 效检测的时间缩短,这样服务下线后就能够及时从注册表中清除。

  1. 客户端缓存

客户端缓存主要分为两块内容,一块是 Eureka Client 缓存,一块是 Ribbon 缓存。

Eureka Client 缓存

EurekaClient负责跟EurekaServer进行交互,在EurekaClient中的 com.netflix.discovery.DiscoveryClient.initScheduledTasks() 方法中,初始化了一个 CacheRefreshThread 定时任务专⻔用来拉取 Eureka Server 的实例信息到本地。所以我们需要缩短这个定时拉取服务信息的时间间隔(eureka.client.registryFetchIntervalSeconds) 来快速发现新的服务。

Ribbon 缓存

Ribbon会从EurekaClient中获取服务信息,ServerListUpdater是Ribbon中负责服务实例 更新的组件,默认的实现是PollingServerListUpdater,通过线程定时去更新实例信息。定时刷新的时 间间隔默认是30秒,当服务停止或者上线后,这边最快也需要30秒才能将实例信息更新成最新的。我们 可以将这个时间调短一点,比如 3 秒。
刷新间隔的参数是通过 getRefreshIntervalMs 方法来获取的,方法中的逻辑也是从 Ribbon 的配置中进 行取值的。
将这些服务端缓存和客户端缓存的时间全部缩短后,跟默认的配置时间相比,快了很多。我们通过调整 参数的方式来尽量加快服务发现的速度,但是还是不能完全解决报错的问题,间隔时间设置为3秒,也 还是会有间隔。所以我们一般都会开启重试功能,当路由的服务出现问题时,可以重试到另一个服务来 保证这次请求的成功。

2. Spring Cloud 超时设置问题

在SpringCloud中,应用的组件较多,只要涉及通信,就有可能会发生请求超时。那么如何设置超时时间? 在 Spring Cloud 中,超时时间只需要重点关注 Ribbon 和 Hystrix 即可。

Ribbon

如果采用的是服务发现方式,就可以通过服务名去进行转发,需要配置Ribbon的超时。 Rbbon的超时可以配置全局的ribbon.ReadTimeout和ribbon.ConnectTimeout。也可以在前面指定服 务名,为每个服务单独配置,比如 user-service.ribbon.ReadTimeout。

其次是Hystrix的超时配置,Hystrix的超时时间要大于Ribbon的超时时间,因为Hystrix将请求包装了起 来,特别需要注意的是,如果Ribbon开启了重试机制,比如重试3 次,Ribbon 的超时为 1 秒,那么 Hystrix 的超时时间应该大于 3 秒,否则就会出现 Ribbon 还在重试中,而 Hystrix 已经超时的现象。

Hystrix

Hystrix全局超时配置就可以用default来代替具体的command名称。 hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=3000 如果想对具体的 command 进行配置,那么就需要知道 command 名称的生成规则,才能准确的配置。

如果我们使用 @HystrixCommand 的话,可以自定义 commandKey。如果使用FeignClient的话,可以为FeignClient来指定超时时间: hystrix.command.UserRemoteClient.execution.isolation.thread.timeoutInMilliseconds = 3000

如果想对FeignClient中的某个接口设置单独的超时,可以在FeignClient名称后加上具体的方法: hystrix.command.UserRemoteClient#getUser(Long).execution.isolation.thread.timeoutInMilliseconds = 3000

Feign

Feign本身也有超时时间的设置,如果此时设置了Ribbon的时间就以Ribbon的时间为准,如果没设置Ribbon的时间但配置了Feign的时间,就以Feign的时间为准。Feign的时间同样也配置了连接超时时间(feign.client.config.服务名称.connectTimeout)和读取超时时间(feign.client.config.服务名 称.readTimeout)。

建议:我们配置Ribbon超时时间和Hystrix超时时间即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值