本文主要是介绍从负载均衡的预热谈到AOT,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
在公司内部RPC框架最近的一次提交中看到了这么一个commit,主要是为了
- 平滑扩容
- 服务重启
- 瞬时流量
点进去之后看到了熟悉的代码,没错儿,这正是Dubbo负载均衡策略中使用的预热(warmup)逻辑,
AbstractLoadBalance
getWeight()
我们内部的这次提交连注释都是照搬过来的,在调用方发起请求时,这块代码对服务端列表InvokerList(ProviderList)中配置了warmup时间的服务重新进行权重的计算,对于当前时间戳较新的服务,按比例降低权重,防止瞬时流量RT过长,产生超时甚至打垮服务。
/*** Calculate the weight according to the uptime proportion of warmup time* the new weight will be within 1(inclusive) to weight(inclusive)** @param uptime the uptime in milliseconds* @param warmup the warmup time in milliseconds* @param weight the weight of an invoker* @return weight which takes warmup into account*/static int calculateWarmupWeight(int uptime, int warmup, int weight) {int ww = (int) ( uptime / ((float) warmup / weight));return ww < 1 ? 1 : (Math.min(ww, weight));}
对于预热场景,目前我了解的包括以下几个方向
- 缓存预热
- 代码预热
- 基于负载均衡的服务端预热
缓存预热
这个场景,大家应该是都比较熟悉的,日常项目中的缓存场景非常普遍,在高并发场景下,或者低延时业务,为了业务的平滑丝滑,上线前或者重启时,我们一般会将高频的配置业务数据提前加载进缓存。
如果没有配置,那么刚启动时,热点数据会直接穿透缓存直达DB,首先响应时间会变长,再者初始化时DB的压力也会很大,对于业务的使用者来说会有卡顿的感觉。
提前配置了热点数据之后,系统的响应接口RT会比较稳定,不太有毛刺现象的产生。
举个简单的栗子,我们兼职小程序首页每个专题页的职位在配置线上前,在运营后台都会提前加载专题页进入redis,防止请求直接打到DB。
代码预热
代码预热我认为分为两个方面
- 基于真实业务流量或者基准测试,在上线前对服务核心链路进行预热,主要为了资源的加载,数据层面的缓存,不过我们服务不是低延时场景,我对此也了解不深,本文不过多叙述
- JVM的类加载编译
围绕第二点来谈谈,对于一个javaer,JVM的启动速度一直以来都被吐槽诟病。
JVM的口号是 一次编译,处处运行,但是实际上Javac前端编译器编译之后,不同系统对于编译后的字节码文件还是需要解释执行,所以某种程度Java算是半编译半解释型语言,为了提高运行速度,JVM在1.3版本就引入了JIT即时编译这个大杀器。
对于热点代码(热点代码的探测在此不讨论),JIT(JustInTime)编译技术是在通常的编译过程之上做了增强,JVM基于C1、C2编译器,会根据运行过程中代码执行的热点情况,把一些热点代码提前编译成机器码,等下次执行这些热点代码的时候,就不用实时编译成机器码了,而是直接运行机器码即可,这样就提高了Java的运行速度。正所谓Just-In-Time。
但是JIT的重点是在运行过程中对热点代码进行编译缓存,那么JVM启动时的速度仍然很慢,那么JVM在启动这方面有没有什么优化呢,有的,相信大家在看到JIT相关介绍时,往往伴随着另外一项概念AOT(Ahead-Of-Time)提前编译
AOT 见名知意,提前编译,也就是提前在应用启动之前将代码编译成可执行的机器码。有以下优点
- 在程序运行前编译,可以避免在运行时的编译性能消耗和内存消耗
- 可以在程序运行初期就达到最高性能,程序启动速度快
- 运行产物只有机器码,打包体积小
对于C++或者C语言等编译型语言来说,我理解AOT并不是什么新技术,但是对于Java号称一次编译处处运行的特性来说,很明显AOT和Java是站在对立面的。提前编译会破坏处处运行的特性,直接跳过字节码那就牺牲了跨平台的特性。
在JEP 295 提案中, JDK9中JVM确实也引入了AOT编译,目的就是为了减少java应用程序启动的耗时,对代码进行warm up;需要使用新的编译命令jaotc对代码进行编译
but由于使用适用范围和人数并不广泛在JDK17的时候移除了这项特性。
除了JVM官方,不得不说GraalVM在AOT这项特性的推进,GraalVM现在已经成长为一个比较完善的通用虚拟机,除了Java之外,对JS、Rust、Pthon等等语言方面的高效运行和高质量编译也支持的很好。对于AOT也是GraalVM主推的特性,有这方面需要的应用可考虑使用这个编译器,GraalVM目前比较活跃,各项研究开发进度也很稳定推进。
SpringBoot3去年发布,其中Spring Native也是升级的一个重大特性。
支持使用GraalVM将Spring的应用程序编译成可执行的镜像文件,这可以显著提高内存和启动性能。Spring Native也是Spring在云原生时代的一个展望,是Spring面向未来的有利竞争优势。
基于负载均衡的服务预热
如我开头所说的,在服务层面,为了提高整体服务的可用性,在请求方获取服务列表进行负载均衡时,会将服务的启动时间加入计算,以降低服务的权重,给新加入的服务一些预热时间后,再慢慢承接更多的请求,直到达到相应的权重比例。
这篇关于从负载均衡的预热谈到AOT的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!