Spring Boot 冷启动

plupiseo  于 2022-11-21  发布在  Spring
关注(0)|答案(6)|浏览(269)

我有一个spring Boot 应用程序,我在openshift集群的docker容器中运行它。在稳定状态下,有N个应用程序示例(假设N=5),请求被负载平衡到这N个示例上。一切都运行良好,响应时间很短(约5ms,总吞吐量约60k)。
每当我添加一个新示例时,响应时间都会短暂地增加(最多70毫秒),然后恢复正常。
我能做些什么来避免这种冷启动吗?我试着在发送流量之前连续进行100次curl调用来预热应用程序,但没有帮助?
我是否需要更好的高并发预热脚本?有没有更好的方法来处理这个问题?
谢谢

qltillow

qltillow1#

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

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();
        }
    }

}
ma8fv8wu

ma8fv8wu2#

如果在向应用程序提供请求时应用程序运行正常,但仍然存在响应缓慢的问题,则应尝试启用分层编译

-XX:CompileThreshold -XX:TieredCompilation

通常,VM使用解释器来收集方法的分析信息,这些信息被馈送到编译器中。在分层方案中,除了解释器之外,客户端编译器被用来生成方法的编译版本,这些方法收集关于它们自身的分析信息。
由于编译的代码比解释的代码快得多,因此程序在性能分析阶段的执行性能更好。

9w11ddsr

9w11ddsr3#

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

  1. JVM预热
    JVM预热主要解决类加载和实时编译两个问题。
  • 对于类加载,只需提前运行覆盖代码路径。
  • 对于JIT,分层编译(如C1/C2)通常在服务器端启用(JVM服务器模式)。如果您使用的是JDK 7或更高版本,则默认启用分层编译(较低版本的JDK需要JVM参数:-XX:+TieredCompilation),C1和C2的编译计算资源不一样,C2会有更多,预热的目的可能是为了触发C1/C2编译,这样当正式的请求进来的时候,代码已经预热,编译通过了。

对于以上两个方向,类加载本身会消耗更多的时间,预热这部分会得到更大的投入产出比。
1.网络层预热。
从网络级,给定一定量的预热流量,其可以是特定预热流量或正常用户请求。
这一般可以在nginx层进行流量控制,当一个新启动的节点加入上游时,可以给这个新节点一个很低的权值,这样在初始阶段只进入少量的流量,这样就预留了足够的计算资源,要做代码预热,也就是类加载和即时编译,如果服务只提供RPC服务,而不是HTTP服务,RPC框架层可以做流量预热,比如杜博等RPC框架已经有了服务预热功能,同样预热也是指启动初期的节点只给予少量的流量。
预热所需要的计算资源在上面的方法中都提到了,那就是CPU,如果你的服务主机有足够的计算资源,你可以给每个节点分配更多的CPU资源来加快预热的速度,减少预热处理的时间。
如果上面的网络层和硬件资源以及RPC框架都无法更改,我们可以在SpringBoot微服务内部预热一下,上面的答案已经提到了ApplicationReadyEvent,实际上更好的实现是侦听ContextRefreshedEvent事件。因为当ApplicationReadyEvent发生时,HTTP端口将被初始化并公开。在warm-向上完成。

@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.....
    }
}

注意:上面的预热代码并没有预热所有的代码。因为来自控制器层的请求有一些代码路径在HTTP服务器没有准备好之前不能实际执行。我们只能在服务层执行代码覆盖。简而言之,这可能是一种妥协。

eqfvzcg8

eqfvzcg84#

首先,我会尝试启用JIT compilation并比较结果。这里有一个good article in Baeldung比较Graal与默认的C1和C2 JIT编译器的性能--您可能希望针对您的工作负载运行一些测试。基本上,在运行Java应用程序时,您需要设置以下选项:
-XX:+解除锁定实验VM选项-XX:+启用JVMCI-XX:+使用JVMCI编译器
此外,请确保您已经使用Sping Boot 的actuator健康检查URL(/actuator/health)进行了configured readiness probe in OpenShift。否则,您的容器可能会在准备好提供服务之前收到流量。
就绪探测确定容器是否已准备好为请求提供服务。如果就绪探测使容器失败,端点控制器将确保容器的IP地址已从所有服务的端点中删除。就绪探测可用于向端点控制器发出信号,表明即使容器正在运行,它不应接收来自代理的任何通信。通过配置Pod配置的template.spec.containers.readinessprobe节来设置就绪检查。
最后,让NGINX或其他反向代理缓存您的响应也会有所帮助。

pftdvrlh

pftdvrlh5#

在我的场景中,我模拟了100多个curl请求来初始化那些客户机池、预加载缓存或其他惰性加载工具。
我在WarmupHealthIndicator implements HealthIndicator上完成了这项工作,它实现了一个Spring执行器健康检查端点。
最后,在预热完成之前,Nginx(或其他负载均衡器)的任何健康检查都将获得5xx状态代码,以及以下正文消息。2并且在状态启动后,应用程序初始化将不需要任何流量。

{
  "status": "DOWN",
  "details": {
    "warmup": {
      "status": "DOWN"
    },
    "diskSpace": {
      "status": "UP",
      "details": {
        "total": 536608768000,
        "free": 395195826176,
        "threshold": 10485760
      }
    }
  }
}

此外,NGINX Plus有一个付费功能slow_start,可以为您的兴趣做同样的事情。

t40tm48m

t40tm48m6#

当Sping Boot 应用程序启动时,JVM需要加载各种类进行初始化,导致HTTP请求的响应时间较长。
如果要预热http组件,可以参考:

@Component
public class WarmUpListener implements ApplicationListener<ApplicationReadyEvent> {
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        // Warm up
    }
}

或者试试这个 Boot 器:warmup-spring-boot-starter,可以在应用程序提供外部服务之前对HTTP相关组件进行预热,从而减少HTTP请求响应时间。

相关问题