为了账号安全,请及时绑定邮箱和手机立即绑定

Spring Boot 冷启动

Spring Boot 冷启动

至尊宝的传说 2022-06-23 20:24:39
我有一个 spring boot 应用程序,它在 openshift 集群的 docker 容器中运行。在稳定状态下,应用程序有 N 个实例(例如 N=5),请求负载均衡到这 N 个实例。一切运行良好,响应时间很短(约 5 毫秒,总吞吐量约 60k)。每当我添加一个新实例时,响应时间都会短暂上升(最高约 70 毫秒),然后恢复正常。我能做些什么来避免这种冷启动吗?我尝试通过在发送流量之前依次发出约 100 个 curl 调用来预热应用程序,但这没有帮助吗?我需要更好的高并发预热脚本吗?有没有更好的方法来处理这个?
查看完整描述

3 回答

?
饮歌长啸

TA贡献1951条经验 获得超3个赞

我们的微服务遇到了类似的问题,为了预热我们添加了一个组件


ApplicationStartup implements ApplicationListener<ApplicationReadyEvent> 

在应用程序启动后立即在应用程序中调用服务,这对我们有用。使用此解决方案,可以保证将在您的有效负载中使用的所有类将在您启动的每个实例中的实例启动后立即加载,并且您不需要外部脚本来进行调用。外部脚本也有问题,我们不能确定调用由新实例处理。


@Component

public class ApplicationStartup implements ApplicationListener<ApplicationReadyEvent> {


    @Autowired

    YourService yourService;


    @Override

    public void onApplicationEvent(final ApplicationReadyEvent event) {


        System.out.println("ApplicationReadyEvent: application is up");

        try {

            // some code to call yourservice with property driven or constant inputs 

        } catch (Exception e) {

            e.printStackTrace();

        }

    }




查看完整回答
反对 回复 2022-06-23
?
慕后森

TA贡献1802条经验 获得超5个赞

如果您的应用程序在您向其提供请求时是健康的,但您仍然遇到响应缓慢的问题,您应该尝试启用分层编译

-XX:CompileThreshold -XX:TieredCompilation

通常,VM 使用解释器来收集有关提供给编译器的方法的分析信息。在分层方案中,除了解释器之外,客户端编译器还用于生成方法的编译版本,这些方法收集有关自身的分析信息。

由于编译代码比解释代码快得多,因此程序在分析阶段以更好的性能执行。


查看完整回答
反对 回复 2022-06-23
?
手掌心

TA贡献1942条经验 获得超3个赞

这个问题可以从两个方面来解决。第一种方法是上菜前先热身。第二种方法是一开始就少给外部的请求,这样可以预留更多的计算资源来完成JVM的一些初始化(比如类加载)。无论哪种方式,都是因为 JVM 需要预热才能启动。这是由JVM的运行原理决定的。特别是HotSpot虚拟机,其执行引擎由解释执行引擎和实时编译执行(JIT)两部分组成。对于 JIT,需要 CPU 资源来实时编译字节码。此外,类的延迟加载在第一次运行时也会需要更多时间。


JVM 预热

JVM预热主要是解决类加载和实时编译两个问题。


对于类加载,只需提前运行覆盖代码路径。

对于JIT来说,C1/C2等分层编译一般是在服务器端开启的(JVM服务器模式)。如果你使用JDK7或以上版本,默认开启分层编译(JDK低版本需要JVM参数:-XX:+TieredCompilation),C1和C2的编译计算资源不同,C2会有更多。预热的目的可能是触发C1/C2编译,这样当官方请求进来时,代码已经预热编译通过。

对于以上两个方向,类加载本身会消耗更多的时间。热身这部分会得到更大的投入产出比。


网络层预热。

从网络层面,给定一定的预热流量,可以是特定的预热流量,也可以是普通的用户请求。


这通常可以在 nginx 层进行流量控制。当一个新启动的节点加入上游时,可以给新节点一个非常低的权重。这样,在初始阶段只进入了少量的流量。因此,预留了足够的计算资源。做代码预热,即类加载和即时编译。如果服务只提供 RPC 服务,不提供 HTTP 服务,则 RPC 框架层可以做流量预热。例如,Dubbo 等 RPC 框架已经具备服务预热功能。同样,预热意味着启动初期的节点只给少量的流量。


预热所需的计算资源在上述方法中都有提及。也就是CPU。如果您的服务主机有足够的计算资源,您可以为每个节点分配更多的 CPU 资源,以加快预热过程。减少预热处理时间。


如果上述网络层和硬件资源和RPC框架不能改变。我们可以在 SpringBoot 微服务中热身。上面的答案已经提到ApplicationReadyEvent,实际更好的实现是监听ContextRefreshedEvent事件。因为 HTTP 端口会在 ApplicationReadyEvent 发生时被初始化和暴露。在预热完成之前可能会有意外的请求进来。


@Component

public class StartWarmUpListener implements ApplicationListener<ContextRefreshedEvent> {

    /**

     * Handle an application event.

     *

     * @param event the event to respond to

     */

    @Override

    public void onApplicationEvent(ContextRefreshedEvent event) {

        // do something about warm-up here.....

    }

}

注意:上面的预热代码并没有预热所有代码。因为Controller层的请求有一些代码路径,直到HTTP服务器没有准备好才能真正执行。我们只能在服务层进行代码覆盖。简而言之,这可能是一种妥协。


查看完整回答
反对 回复 2022-06-23
  • 3 回答
  • 0 关注
  • 384 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信