关于Java程序服务预热那些事

hello,大家好,我是张张,「架构精进之路」公号作者。

一、背景

1.1 什么是服务预热

所谓服务预热,就是在服务启动完成到对外提供服务之前,针对特定场景提供一些初始化准备操作。

比如线程池预热、缓存预热、数据库预热、web预热和JVM预热等。

1.2 为什么要预热

因为服务刚启动的时候需要一段磨合期,这段期间,服务运行状态没有达到最佳。因此,如果这时候一下子将服务流量提升到正常水平,就有可能导致大量请求超时或者瞬间将系统压垮。

针对 Web 应用的场景,将Java程序启动分为两个过程:

1、启动过程:是指从JVM 启动到程序初始化完成能响应第一个请求的阶段。由于启动时需要动态类加载和程序启动时的初始化,导致启动会比较慢。启动慢会影响程序的响应度,减慢 Web 应用扩容。

2、预热过程:是指从JVM 启动到程序优化完成、达到性能峰值的阶段。预热慢会导致 Web 应用无法及时的处理完用户请求,造成大量请求超时。

二、如何预热

2.1 预热缓存

a.连接池预热

和数据库连接池一样,我们可以在应用启动时,根据需要初始化若干连接放入连接池,从而避免请求过来的时候再创建而影响性能。

系统启动时,尝试获取若干连接(这里取最小闲置),此时连接池为空,生成连接后会放入连接池,等请求进来的时候就不会再去创建连接了。

b.热点数据预热

  • 数据存到redis中

处理方案:

1、配置一个定时任务,刷新缓存数据。

2、直接写个缓存刷新页面,上线后手动刷新。

  • 数据存到本地

对于一些访问度特别高的热点数据,在极端情况下,考虑到服务到redis有网络延迟以及序列化和反序列化消耗,所以放入redis缓存是不太理想的。

那么对于这些数据我们可以考虑放入本地缓存,当然这些数据量不能太大。对于极端的热点数据放入本地缓存后,请求获取数据在应用维度基本没有任何消耗,没有网络延迟也没有序列化和反序列化消耗。

可以使用guava 的cache

2.2 预热线程池

1.请求接收线程池

以tomcat为例,我们可以配置初始化创建线程的数量来做线程池预热。

2.自定义线程池

对于自定义线程池,初始化时候调用prestartAllCoreThreads方法即可。

3.预热DB连接池

服务启动时根据需要创建若干数据库连接,放到连接池中,然后应用启动处理数据库读写请求时,可以直接从连接池中拿连接来用,避免了读写请求创建连接并放入连接池的流程耗时。

我们常用的连接池Druid提供了比较方便的连接池预热能力。常见的配置方式:

4.JVM预热

大家都知道,Java语言是解释执行和编译执行共同存在的。即Java源代码文件(.java)首先由Javac编译为字节码(.class),然后字节码由各操作系统对应的虚拟机解释或编译执行。

为了提高执行速度,引入了JIT(Just-in-time compilation)。在运行时,JIT会把编译过来的机器码保存起来,以便下次使用。JIT只会对经常执行的热点代码进行编译,如循环,高频度使用的方法等。

JVM默认开启JIT编译,可以检查启动参数-Xint和-Djava.compiler=NONE,如果有说明关闭了JIT,根据需要可以移除重新开启JIT。

此外,JDK1.8引入了Lambda表达式,给开发人员带来了很多方便,但是过度的使用Lambda表达式也会带来负面效果。lambda表达式的原理是在执行时生成匿名内部类,并且需要加载和编译。

所以对于一些热点代码可以选择不使用Lambda表达式,或者在服务启动时进行预调用,经过充分的调用之后使其成为热点代码,后续的调用就会走JIT编译,就会跳过生成匿名内部类、加载的步骤,也就提升了执行效率。

三、预热的问题及解决

3.1 时长

既然在服务启动阶段加入了预热逻辑,就会产生损耗,最直接的体现就是服务启动时间变长,可能几分钟到十几分钟不等。

但启动慢不一定就是坏事,相当于是在对外提供服务之前做了充足的初始化以及预热准备。

当然事情都有两面性,关键的是要在启动快与慢之间找到一个平衡点,在保证服务能够在可接受的时间内启动,并且上线之后短时间内就能提供高质量的服务。

3.2 CPU升高

在实际应用过程中,遇到了CPU突增的情况。如下图,服务在启动时会收到CPU报警,查看监控发现CPU使用率是平时的2倍以上。

查看启动时的预热代码,发现会触发下面的请求,如图,请求量是平时正常水平的几百倍。因此定位应该是预热代码的问题。

预热代码如下:

上述代码使用CountDownLatch作为计数器,在服务启动时,触发三个热点接口,以达到预热的目的。但是由于参数过多导致服务的请求激增,引发报警。

问题定位之后,我们降低了入参数量,减少了循环次数,从而减少了服务自请求次数,问题得以修复。

四、总结

以上是服务预热的一些常见方式,服务预热是把双刃剑,在使用之前,需要搞清楚哪些服务需要预热。对于需要预热的服务,需要搞清楚具体需要预热哪些内容,不能盲目预热,容易适得其反。

在预热之后,也需要观察相关的指标,检查预热是否生效。

·END·

希望今天的讲解对大家有所帮助,谢谢!

Thanks for reading!

作者:架构精进之路,十年研发风雨路,大厂架构师,CSDN 博客专家,专注架构技术沉淀学习及分享,职业与认知升级,坚持分享接地气儿的干货文章,期待与你一起成长。

关注并私信我回复"01",送你一份程序员成长进阶大礼包,欢迎勾搭。

相关推荐
黄俊懿7 分钟前
【深入理解SpringCloud微服务】手写实现各种限流算法——固定时间窗、滑动时间窗、令牌桶算法、漏桶算法
java·后端·算法·spring cloud·微服务·架构
2401_8574396934 分钟前
“衣依”服装销售平台:Spring Boot技术应用与优化
spring boot·后端·mfc
Jerry.ZZZ1 小时前
系统设计,如何设计一个秒杀功能
后端
车载诊断技术2 小时前
什么是汽车中的SDK?
网络·架构·汽车·soa·电子电器架构
九圣残炎3 小时前
【springboot】简易模块化开发项目整合Redis
spring boot·redis·后端
.生产的驴3 小时前
Electron Vue框架环境搭建 Vue3环境搭建
java·前端·vue.js·spring boot·后端·electron·ecmascript
爱学的小涛3 小时前
【NIO基础】基于 NIO 中的组件实现对文件的操作(文件编程),FileChannel 详解
java·开发语言·笔记·后端·nio
爱学的小涛3 小时前
【NIO基础】NIO(非阻塞 I/O)和 IO(传统 I/O)的区别,以及 NIO 的三大组件详解
java·开发语言·笔记·后端·nio
北极无雪3 小时前
Spring源码学习:SpringMVC(4)DispatcherServlet请求入口分析
java·开发语言·后端·学习·spring
爱码少年3 小时前
springboot工程中使用tcp协议
spring boot·后端·tcp/ip