好家伙,原来是这原因导致java应用刚开始会响应比较慢

前言


其实一直以来,我都有发现应用在刚起来那会,接口响应速度是比较慢的,可以通过postman看下接口响应速度,我就有点闷逼了,它在干嘛?

image.png

今天晚上我看的why大的一篇文章,然后我自己又去查了查资料,终于被我逮到了

java 类加载过程


这个应该是面试的时候经常被问到,加载->校验->解析->初始化->使用->卸载。就是从java类,变成字节码,然后还需要解析成机器能识别的语言,对类里头参数初始化,最后才能用。

这里就提到了编译器,JIT。

什么是JIT?

image.png

JIT编译器在程序启动后运行,并将代码(通常是字节码或某种虚拟机指令)实时(或称为“实时”)编译成通常更快的形式,通常是主机CPU的本机指令集。JIT可以访问动态运行时信息,而标准编译器没有,并且可以进行更好的优化,比如频繁使用的内联函数。这与传统编译器不同,传统编译器在程序首次运行之前将所有代码编译为机器语言。换言之,传统编译器在第一次运行程序之前将整个程序构建为EXE文件。对于较新样式的程序,使用伪代码(p代码)生成程序集。只有在操作系统上执行程序(例如,双击其图标)后,(JIT)编译器才会启动并生成基于英特尔的处理器或其他处理器能够理解的机器代码(m代码)。

image.png

明白怎么一回事了吧,就是运行时将字节码转成机器能识别的machine code,只有需要执行程序的时候,才会去编译、解析,好家伙,难怪java应用起来之后响应这么慢~

那么它的优化是在执行大量请求之后,会将热点的代码进行缓存,这样就不用每次都解析。这就是为啥机器跑热乎之后,接口顺畅起来了~

阿里JVM团队对它的优化


jwarmup,就是在灰度环境将那些抢手的类提前编译好,而不是要用的时候再进行编译,这样就解决了线上环境那些热点的类需要执行时编译、解析慢点的问题

image.png

其实就是预热,当然这个比较高端,鉴于本人还是小菜鸟,我们就玩点可以上手的操作

线程池预热


多线程还有线程池,都是开发经常用的一些应对高并发的手段。

了解线程池原理的同学都知道,它不是一下子就跑起来的。线程池的目的就是减少线程创建时候的时间,但是它还没热起来之前,也是需要进入内部线程创建阶段的,这时我们在项目起来之后,就可以先预热下线程池

executorService.prestartAllCoreThreads();
复制代码

dubbo预热


当然我很少用dubbo,一个是确实它用起来比较繁琐的,我一个fegin接口就能搞定,非要配置各种版本,服务端、客户端的东西,另一个平时开发也用的少。

它通过AbstractLoadBalance#getWeight,对不同客户端进行权重配置,也就是说那些刚刚起来的节点,流量是一点一点给,直到服务热起来,才把所有流量打上去,有点意思~

nacos服务发现预热


之前做过全链路灰度,改造了网关层的负载均衡,还有服务东西走向流量的rpc层的负载均匀,里头都是基于nacos服务发现做的改造。其实我们也可以在程序起来那会,往nacos服务元数据,metaData塞入服务启动时间,网关层接收到流量请求后,通过负载均衡filter,拿到instanceid,然后将metaData取出来,根据时间进行给予权重。

当然服务层预热意义不大,因为有灰度来支持这种预热。那么rpc是不是可以这样搞呢?我觉得是可以的,比如说feign的负载均衡。

接口的预热


当然这操作骚了点,其实跟线程池预热一个思想,就是提前跑。我们在apm上可以看到一些热点接口对吧,然后把它们记录下来,是不是我们在应用起来之后,第一波跑个假的流量呢?

image.png

不管什么手段,比如说灰度流量调用,还是暗箱操作,我们只要带上特定标识,程序不给处理对应的业务逻辑,是不是能提前热乎下~

当然了,服务在灰度的基础上,没有必要这么搞,确实有点骚~

猜你喜欢

转载自juejin.im/post/7136974358710321166