实现微服务预热调用之后再开始服务(上)

本文涉及的产品
云原生网关 MSE Higress,422元/月
注册配置 MSE Nacos/ZooKeeper,118元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 实现微服务预热调用之后再开始服务(上)

最近线上发现一个现象,应用实例刚刚启动的时候,开始接收请求之后发生了一小段时间的请求阻塞,从 HTTP Servlet 请求队列监控上可以看出(基于 spring-web 的普通阻塞的 HTTP 服务器是有 HTTP 线程池的,当线程是满了之后,请求在阻塞队列中等待处理。基于 spring-webflux 的没有这个现象,但是考虑的背压问题其实和这个场景类似):


image.png


然后阻塞数量很快就下去了,通过 JFR 发现和 Spring 各种懒加载的 Bean,各种资源或者连接池初始化等有关。这些资源可以理解为是懒加载的,是在请求真正用到的时候才会初始化。这些资源初始化之前,微服务就已经注册到注册中心并开始接受请求了。这样在平常业务低峰发布的时候,是没有问题的,因为这些资源的初始化耗时也就在几十到几百毫秒之间。但是在业务高峰需要动态扩容的时候,就会受一些影响,因为请求压力会立刻大量打到这些新启动的实例上,这种情况下,初始化耗时的影响就比较大了

所以,我们希望在微服务开始真正提供服务之前,将这些比较耗时的需要初始化的资源提前初始化完成之后,再告诉注册中心我们可以开始接受处理请求了。


Spring Boot 中的 MVC Servlet 与 Web Service Servlet 的提前初始化


在微服务实例启动后,我们发送第一个请求的时候,会看到类似于下面的日志:

INFO: Initializing Servlet 'dispatcherServlet'
INFO: Initializing Spring DispatcherServlet 'dispatcherServlet'

这是在初始化 Servlet。

MVC Servlet 指的就是 Spring-MVC 的 Servlet,其实就是提供 HTTP 服务的一些核心 Servlet,例如最核心的 org.springframework.web.servlet.DispatcherServlet。这些默认是懒加载的,需要通过下面的配置打开:

spring.mvc.servlet.load-on-startup: 1

除了 MVC Servlet,另一些 Servlet 可能是提供除了 HTTP 以外的其他应用协议的 Servlet,这些 Servlet 默认也是懒加载的,需要通过下面的配置打开:

spring.webservices.servlet.load-on-startup: 1

例如 WebSocket 的 org.springframework.ws.transport.http.MessageDispatcherServlet 就是通过这个配置进行初始化的。


spring-data-redis 连接池的初始化


我们项目中使用的是 spring-data-redis + lettuce。如果没有使用 Pipeline 等需要独占连接的 redis 操作,其实不用使用连接池。但是我们在使用中为了效率,尽量都是用 Pipeline,所以我们都启用了连接池配置。其连接池实现基于 common-pools2 库。相关配置如下所示:

  • spring.redis.lettuce.pool.enabled: 是否启用连接池,如果依赖中有 common-pools2 依赖自动会启用。一般这个配置是用来关闭连接池的。
  • spring.redis.lettuce.pool.max-active: 连接池最大连接数量,默认是 8
  • spring.redis.lettuce.pool.max-idle:连接池中最多保留的空闲连接数量,默认是 8,这个配置需要和 spring.redis.lettuce.pool.time-between-eviction-runs 一起配置才可以生效。
  • spring.redis.lettuce.pool.max-wait:从连接池获取连接最大的等待时间(毫秒),超过这个等待时间则会抛出异常,默认是 -1,即不超时
  • spring.redis.lettuce.pool.min-idle:连接池中最小的空闲连接数量,默认是 0,这个配置需要和 spring.redis.lettuce.pool.time-between-eviction-runs 一起配置才可以生效。
  • spring.redis.lettuce.pool.time-between-eviction-runs:common-pools 中 Evictor 初始执行延迟以及执行间隔。不配置的话,就没有启用 Evictor 任务。

这个配置经常有人会误用,只配置了 spring.redis.lettuce.pool.min-idle 但是没有配置 spring.redis.lettuce.pool.time-between-eviction-runs,导致 Evictor 任务没有启动,导致并没有初始化最小连接数量的连接。

Evictor 任务包括将池中空闲的超过 spring.redis.lettuce.pool.max-idle 配置数量的对象,进行过期,以及空闲对象不足 spring.redis.lettuce.pool.min-idle 时,创建对象补足:

public void run() {
    final ClassLoader savedClassLoader =
            Thread.currentThread().getContextClassLoader();
    try {
        if (factoryClassLoader != null) {
            // Set the class loader for the factory
            final ClassLoader cl = factoryClassLoader.get();
            if (cl == null) {
                // The pool has been dereferenced and the class loader
                // GC'd. Cancel this timer so the pool can be GC'd as
                // well.
                cancel();
                return;
            }
            Thread.currentThread().setContextClassLoader(cl);
        }
        // Evict from the pool
        try {
            evict();
        } catch(final Exception e) {
            swallowException(e);
        } catch(final OutOfMemoryError oome) {
            // Log problem but give evictor thread a chance to continue
            // in case error is recoverable
            oome.printStackTrace(System.err);
        }
        // Re-create idle instances.
        try {
            ensureMinIdle();
        } catch (final Exception e) {
            swallowException(e);
        }
    } finally {
        // Restore the previous CCL
        Thread.currentThread().setContextClassLoader(savedClassLoader);
    }
}

对于这种连接池,最好初始化足够的连接数量,即配置 spring.redis.lettuce.pool.min-idle 以及 spring.redis.lettuce.pool.time-between-eviction-runs。由于我们的项目中大量使用了 Pipeline,线程会独占一个连接进行操作。所以初始化的连接数量最好等于线程池的数量,在我们项目中即 Http Servlet 线程池的数量。


数据库连接池的初始化


这里以 druid 连接池为例子,druid 连接池底层其实也是类似于 common-pools 的实现,但是配置设计的更全面复杂一些。可以直接配置 initialSize:

DruidDataSource dataSource = new DruidDataSource();
dataSource.setInitialSize(50);
dataSource.setMaxActive(50);

我们设置初始连接池大小就是最大连接数

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
1月前
|
弹性计算 API 持续交付
后端服务架构的微服务化转型
本文旨在探讨后端服务从单体架构向微服务架构转型的过程,分析微服务架构的优势和面临的挑战。文章首先介绍单体架构的局限性,然后详细阐述微服务架构的核心概念及其在现代软件开发中的应用。通过对比两种架构,指出微服务化转型的必要性和实施策略。最后,讨论了微服务架构实施过程中可能遇到的问题及解决方案。
|
3月前
|
Cloud Native Java API
聊聊从单体到微服务架构服务演化过程
本文介绍了从单体应用到微服务再到云原生架构的演进过程。单体应用虽易于搭建和部署,但难以局部更新;面向服务架构(SOA)通过模块化和服务总线提升了组件复用性和分布式部署能力;微服务则进一步实现了服务的独立开发与部署,提高了灵活性;云原生架构则利用容器化、微服务和自动化工具,实现了应用在动态环境中的弹性扩展与高效管理。这一演进体现了软件架构向着更灵活、更高效的方向发展。
|
4月前
|
监控 负载均衡 安全
微服务(五)-服务网关zuul(一)
微服务(五)-服务网关zuul(一)
|
21天前
|
NoSQL 前端开发 测试技术
👀探秘微服务:从零开启网关 SSO 服务搭建之旅
单点登录(Single Sign-On,简称SSO)是一种认证机制,它允许用户只需一次登录就可以访问多个应用程序或系统。本文结合网关和SaToken快速搭建可用的Session管理服务。
75 8
|
2月前
|
弹性计算 持续交付 API
构建高效后端服务:微服务架构的深度解析与实践
在当今快速发展的软件行业中,构建高效、可扩展且易于维护的后端服务是每个技术团队的追求。本文将深入探讨微服务架构的核心概念、设计原则及其在实际项目中的应用,通过具体案例分析,展示如何利用微服务架构解决传统单体应用面临的挑战,提升系统的灵活性和响应速度。我们将从微服务的拆分策略、通信机制、服务发现、配置管理、以及持续集成/持续部署(CI/CD)等方面进行全面剖析,旨在为读者提供一套实用的微服务实施指南。
|
1月前
|
弹性计算 Kubernetes API
构建高效后端服务:微服务架构的深度剖析与实践####
本文深入探讨了微服务架构的核心理念、设计原则及实现策略,旨在为开发者提供一套系统化的方法论,助力其构建灵活、可扩展且易于维护的后端服务体系。通过案例分析与实战经验分享,揭示了微服务在提升开发效率、优化资源利用及增强系统稳定性方面的关键作用。文章首先概述了微服务架构的基本概念,随后详细阐述了其在后端开发中的应用优势与面临的挑战,最后结合具体实例,展示了如何从零开始规划并实施一个基于微服务的后端项目。 ####
|
2月前
|
监控 持续交付 数据库
构建高效的后端服务:微服务架构的深度解析
在现代软件开发中,微服务架构已成为提升系统可扩展性、灵活性和维护性的关键。本文深入探讨了微服务架构的核心概念、设计原则和最佳实践,通过案例分析展示了如何在实际项目中有效地实施微服务策略,以及面临的挑战和解决方案。文章旨在为开发者提供一套完整的指导框架,帮助他们构建出更加高效、稳定的后端服务。
|
3月前
|
Kubernetes 负载均衡 Docker
构建高效后端服务:微服务架构的探索与实践
【10月更文挑战第20天】 在数字化时代,后端服务的构建对于任何在线业务的成功至关重要。本文将深入探讨微服务架构的概念、优势以及如何在实际项目中有效实施。我们将从微服务的基本理念出发,逐步解析其在提高系统可维护性、扩展性和敏捷性方面的作用。通过实际案例分析,揭示微服务架构在不同场景下的应用策略和最佳实践。无论你是后端开发新手还是经验丰富的工程师,本文都将为你提供宝贵的见解和实用的指导。
|
2月前
|
Kubernetes API Docker
构建高效后端服务:微服务架构的深度实践与优化####
本文深入探讨了微服务架构在现代后端开发中的应用,通过剖析其核心概念、设计原则及实施策略,结合具体案例分析,展示了如何有效提升系统的可扩展性、可靠性和维护性。文章还详细阐述了微服务拆分的方法论、服务间通信的最佳实践、以及容器化与编排工具(如Docker和Kubernetes)的应用技巧,为读者提供了一份全面的微服务架构落地指南。 ####
|
3月前
|
监控 API 持续交付
构建高效后端服务:微服务架构的深度探索
【10月更文挑战第20天】 在数字化时代,后端服务的构建对于支撑复杂的业务逻辑和海量数据处理至关重要。本文深入探讨了微服务架构的核心理念、实施策略以及面临的挑战,旨在为开发者提供一套构建高效、可扩展后端服务的方法论。通过案例分析,揭示微服务如何帮助企业应对快速变化的业务需求,同时保持系统的稳定性和灵活性。
54 9