背景
运行了一段时间后的应用,执行速度会比刚启动的应用更快。在运行过程中,JVM虚拟机会把高频的代码编译成机器码,被加载过的类也会被缓存到JVM缓存中,再次使用的时候不会触发临时加载,这样就使得“热点”代码的执行不用每次都通过解释,从而提升执行速度。
这些“临时数据”,在应用重启后就消失了。刚启动的应用就承担像停机前一样的流量,会使应用在启动之初就处于高负载状态,从而导致调用方过来的请求可能出现大面积超时,进而对线上业务产生损害行为
。
让应用一开始只接少许流量。低功率运行一段时间后,再逐渐提升至最佳状态。【启动预热
】
什么是启动预热?
简单来说,就是让刚启动的服务提供方应用不承担全部的流量,而是让它被调用的次数随着时间的移动慢慢增加,最终让流量缓和地增加到跟已经运行一段时间后的水平一样。
RPC中如何实现启动预热
调用方负载均衡在选择连接的时候,区分一下是否是刚启动不久的应用,对于刚启动的应用,可以让它被选择到的概率特别低,但这个概率会随着时间的推移慢慢变大,从而实现一个动态增加流量的过程。
具体实现
调用方如何知道服务提供方启动时间
?
- 一种是服务提供方在启动的时候,把自己启动的时间告诉注册中心
- 另外一种就是注册中心收到的服务提供方的请求注册时间
调用方通过服务发现,除了可以拿到IP列表,同时拿到对应的启动时间。把该时间作用在负载均衡上,基于权重的负载均衡,但是这个权重是由服务提供方设置的,属于一个固定状态。现在要让这个权重变成动态的,并且是随着时间的推移慢慢增加到服务提供方设定的固定值
,整个过程如下:
保证当服务提供方运行时长小于预热时间时,对服务提供方进行降权,减少被负载均衡选择的概率,避免让应用在启动之初就处于高负载状态,从而实现服务提供方在启动后有一个预热的过程。
启动预热更多是从调用方的角度
出发,去解决服务提供方应用冷启动的问题,让调用方的请求量通过一个时间窗口过渡,慢慢达到一个正常水平,从而实现平滑上线。
服务提供方出发,与热启动息息相关,那就是延迟暴露
。
延迟暴露
Spring容器会顺序加载Spring Bean,如果某个Bean是RPC服务的话,不光要把它注册到Spring-BeanFactory里面去,还要把这个Bean对应的接口注册到注册中心。注册中心在收到新上线的服务提供方地址的时候,会把这个地址推送到调用方应用内存中;当调用方收到这个服务提供方地址的时候,就会去建立连接发请求。
服务提供方应用可能还在加载其它的Bean。对于调用方来说,只要获取到了服务提供方的IP,就有可能发起RPC调用,但如果这时候服务提供方没有启动完成的话,就会导致调用失败,从而使业务受损。
如何避免服务启动尚未完成,就有流量打过来?
RPC服务注册到注册中心时机过早,这就导致在后续加载没有完成的情况下服务提供方的地址就被服务调用方感知到了。
接口注册到注册中心的时间挪到应用启动完成后
。具体的做法就是在应用启动加载、解析Bean的时候,如果遇到了RPC服务的Bean,只先把这个Bean注册到Spring-BeanFactory里面去,而并不把这个Bean对应的接口注册到注册中心,只有等应用启动完成后,才把接口注册到注册中心用于服务发现,从而实现让服务调用方延迟获取到服务提供方地址。
上述方法仅能保证服务启动完毕,但是JVM内存还是冷的,此时如果大量请求进来,依旧可能出现大面积超时。
如何解决?
利用服务提供方把接口注册到注册中心的那段时间。
在服务提供方应用启动后,接口注册到注册中心前,预留一个Hook
,让用户实现可扩展的Hook逻辑。Hook里面模拟调用逻辑,从而使JVM指令能够预热起来,也可以在Hook里面事先预加载一些资源,只有等所有的资源都加载完成后,最后才把接口注册到注册中心
。整个应用启动过程如下:
可以预留一些接口,用户可以自定义扩展,在应用启动完毕之后,发布注册中心之前,做“启动预热”。
标签:调用,服务,应用,启动,优雅,Bean,注册,节点 From: https://blog.csdn.net/qq_43417581/article/details/143896508