从负载均衡的预热谈到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));
    }

在这里插入图片描述
对于预热场景,目前我了解的包括以下几个方向

  1. 缓存预热
  2. 代码预热
  3. 基于负载均衡的服务端预热
缓存预热

这个场景,大家应该是都比较熟悉的,日常项目中的缓存场景非常普遍,在高并发场景下,或者低延时业务,为了业务的平滑丝滑,上线前或者重启时,我们一般会将高频的配置业务数据提前加载进缓存
如果没有配置,那么刚启动时,热点数据会直接穿透缓存直达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面向未来的有利竞争优势。

基于负载均衡的服务预热

如我开头所说的,在服务层面,为了提高整体服务的可用性,在请求方获取服务列表进行负载均衡时,会将服务的启动时间加入计算,以降低服务的权重,给新加入的服务一些预热时间后,再慢慢承接更多的请求,直到达到相应的权重比例。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值