当前位置: 首页 > news >正文

【SpringBoot】优化慢启动应用的用户体验

通过深入分析SpringBoot中WebServer的启动流程,插入自定义的Loading页面展示逻辑,优化软件使用时的用户体验。

背景

Java本身的特点,再加上开发人员能力差,软件开发工程化程度低等等问题,经过一段时间的迭代之后,经常会出现的一个问题就是应用启动越来越慢。

过往我也是收集了不少相关资料,例如 云音乐服务端应用启动时间下降40%实践分享,7min到40s: SpringBoot 启动优化实践!等等。

本文跳出与上述文章类似的技术角度,借鉴Jenkins的启动流程 —— Jenkins也是Java开发的,而且其启动速度虽然经过不少优化,但依然有着非常明显的延迟,但Jenkins通过加入Loading页面的方式,极大提升了用户在等待系统就绪期间的用户体验 —— 启动之后马上呈现给用户一个Loading页面,待后台服务能够正常提供服务之后冲向到常规Index页面。

实现

Jenkins默认适用的是winstone 容器,所以我在一开始所设想的"拿来主义"被掐死在了摇篮里,最后经过一番摸索,基于SpringBoot + Undertow实现了类似的效果。

思路:

  1. SpringBoot项目中,单单一个WebServer的启动是很快的,只是SpringBoot的启动流程里,其会在WebServer启动之前初始化所有的Bean,而这是整个启动流程里最耗时的。
  2. 所以,我们将在SpringBoot容器中的Bean实例化之前启动我们自定义的"minimal Undertow Server"来向用户提供对于 loading页面的响应。
  3. 待Spring容器准备进行WebServer的启动时,停止我们的"minimal Undertow Server",以避免端口占用。
  4. 前端loading页面将定期轮询后端服务启动情况,待其正常响应时跳转到相应的主体服务页面。

直接上代码:

// ====================== 1. 应用启动入口类// 应用启动入口public static void main(String[] args) {final SpringApplication springApplication = new SpringApplication(SpringBootTestApplication.class);// 读取用户配置, 决定启动方式final Boolean humanableStart = Convert.toBool(CommonUtil.getProperty("START_HUMANABLE", "false"));if (humanableStart) {springApplication.setApplicationContextClass(AnnotationConfigServletWebServerApplicationContextEx.class); // 自定义}// 启动Spring容器springApplication.run(args);}// ======================== 2. SpringBoot不同版本实现方式不同
// 这里以 SpringBoot 2.3.3为例, 更高版本需要实现 ApplicationContextFactory 接口
class AnnotationConfigServletWebServerApplicationContextWithHumanableStart extends AnnotationConfigServletWebServerApplicationContext {Undertow minimalUndertowserver;@Overrideprotected void prepareRefresh() {// 尽量提前"minimal Undertow Server"的创建, 优化用户体验.String property = this.getEnvironment().getProperty("server.port");minimalUndertowserver = minimalUndertowserver(Convert.toInt(property));super.prepareRefresh();}@Overrideprotected void onRefresh() {// ServletWebServerApplicationContext正是通过覆写本方法来实现 WebServer 创建的// 同时会向容器中注入webServerStartStop Bean,借助Spring的生命周期回调接口SmartLifecycle来负责将webServer的开启和关闭;super.onRefresh();}@Overrideprotected void finishRefresh() {// 关键流程:  AbstractApplicationContext.refresh()// super.finishRefresh()中将触发 WebServerStartStopLifecycle.start() 以启动webserver, 所以我们得在它之前将我们的轻量级webserver关闭掉.minimalUndertowserver.stop();super.finishRefresh();}static Undertow minimalUndertowserver(int port) {// 这个loading.html是从jenkins里扒过来了, 也算是实现了部分"拿来主义"final String loadingHtml = ResourceUtil.readStr("static/loading.html", CharsetUtil.CHARSET_UTF_8);// Start the minimal Undertow serverUndertow undertow = Undertow.builder().addHttpListener(port, "0.0.0.0").setHandler(new HttpHandler() {@Overridepublic void handleRequest(HttpServerExchange exchange) throws Exception {exchange.getResponseHeaders().put(Headers.CONTENT_TYPE, "text/html");exchange.getResponseSender().send(loadingHtml);}}).build();undertow.start();return undertow;}
}

原理分析

Spring容器之所以启动慢,主要原因肯定就是各类Bean的实例化耗时叠加

// AbstractApplicationContext.java
// Spring核心启动流程
@Override
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh();  // 我们在这里启动 minimal Undertow Server// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);// Initialize message source for this context.initMessageSource();// Initialize event multicaster for this context.initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.onRefresh();// Check for listener beans and register them.registerListeners();// Instantiate all remaining (non-lazy-init) singletons.finishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.finishRefresh();  // 在这里关闭 minimal Undertow Server.} catch (BeansException ex) {if (logger.isWarnEnabled()) {logger.warn("Exception encountered during context initialization - " +"cancelling refresh attempt: " + ex);}// Destroy already created singletons to avoid dangling resources.destroyBeans();// Reset 'active' flag.cancelRefresh(ex);// Propagate exception to caller.throw ex;}finally {// Reset common introspection caches in Spring's core, since we// might not ever need metadata for singleton beans anymore...resetCommonCaches();}}
}

相关

  1. Jenkins源码 - loading页面
  2. Jenkins源码 - loading后端代码
  3. GitHub - jenkinsci-winstone

相关文章:

【SpringBoot】优化慢启动应用的用户体验

通过深入分析SpringBoot中WebServer的启动流程,插入自定义的Loading页面展示逻辑,优化软件使用时的用户体验。 背景 Java本身的特点,再加上开发人员能力差,软件开发工程化程度低等等问题,经过一段时间的迭代之后&…...

String str=“i“ 与 String str=new String (“i“) 一样吗?

String str"i" 与 String strnew String ("i") String str"i" 这种形式声明了一个 String 对象,其中 "i" 字符串字面量直接赋值给 str。在 Java 中,字符串字面量会自动放入字符串常量池中,这是一个…...

【数据结构】二叉树链式结构的实现

前置声明:在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在大家对二叉树结构掌握还不够深入,为了降低大家学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉…...

如何有效找到目标客户群体?

在激烈的市场竞争中,找到并锁定目标客户群体是企业成功的关键。以下是几种有效的策略,帮助您精准定位并吸引目标客户。 1. 明确市场定位与客户画像 首先,企业需要明确市场定位,并绘制详细的客户画像,包括年龄、性别、…...

机器学习-混淆矩阵

文章目录 一、混淆矩阵1.混淆矩阵简介2.混淆矩阵图列 二、混淆矩阵指标1. 准确率(Accuracy)2. 精确率(Precision)3. 召回率(Recall)4. F1分数(F1 Score) 三、总结 一、混淆矩阵 1.混…...

数据结构----栈

一丶概念 只能在一端进行插入和删除操作的线性表(又称为堆栈),进行插入和删除操作的一端称为栈顶,另一端称为栈底 二丶特点 先进后出 FILO first in last out 后进先出 LIFO last in first out 三丶顺序栈 逻辑结构&…...

STL六大组件

STL(Standard Template Library,标准模板库)是C标准库的一部分,提供了丰富且高效的数据结构和算法。STL主要由6大组件构成,分别是容器、算法、迭代器、适配器、仿函数和空间配置器。 容器(Containers&#…...

【机器学习】CNN的数学基础

🌈个人主页: 鑫宝Code 🔥热门专栏: 闲话杂谈| 炫酷HTML | JavaScript基础 ​💫个人格言: "如无必要,勿增实体" 文章目录 CNN的数学基础1. 引言2. 卷积运算2.1 连续卷积2.2 离散卷积2.3 互相关 3. 激活函…...

最小路径和[中等]

优质博文:IT-BLOG-CN 一、题目 给定一个包含非负整数的m x n网格grid,请找出一条从左上角到右下角的路径,使得路径上的数字总和为最小。 说明:每次只能向下或者向右移动一步。 示例 1: 输入:grid [[…...

【题库】——数组 小鱼比可爱

#include<bits/stdc.h> using namespace std; int main() {int n,m,i;cin>>n;int arr[n]; for(i0;i<n;i) {int count 0;cin>>arr[i];for(mi;m>0;m--){if(arr[i]>arr[m])count;} cout<<count<<" "; } return 0; }...

基于飞腾平台的Hbase的安装配置

【写在前面】 飞腾开发者平台是基于飞腾自身强大的技术基础和开放能力&#xff0c;聚合行业内优秀资源而打造的。该平台覆盖了操作系统、算法、数据库、安全、平台工具、虚拟化、存储、网络、固件等多个前沿技术领域&#xff0c;包含了应用使能套件、软件仓库、软件支持、软件适…...

【springboot】springboot接口参数全局解密,解决request内容修改后如何重新设置回去的问题

文章目录 核心思路spring&servelt基础核心接口类核心代码 body解密核心原理讲解get解密核心原理讲解get query请求讲解get pathVariables请求讲解 总结 本文不仅介绍了body内容修改后如何传递&#xff0c;也介绍了get请求 在修改内容后如何继续传递。 【原创作者 csdn: 孟秋…...

yml基本语法

YAML&#xff08;YAML Ain’t Markup Language&#xff09;是一种简洁且易读的数据序列化格式&#xff0c;常用于配置文件。Spring Boot 中的 application.yml 文件使用 YAML 来配置应用程序的属性。 YAML 基本语法 1. 键值对 基本的键值对表示形式为&#xff1a;key: value…...

橙色简洁大气体育直播自适应模板赛事直播门户自适应网站源码

源码名称&#xff1a;酷黑简洁大气体育直播自适应模板赛事直播门户网站 源码开发环境&#xff1a;帝国cms 7.5 安装环境&#xff1a;phpmysql 带采集&#xff0c;可以挂着电脑上自动采集发布&#xff0c;无需人工操作&#xff01; 橙色简洁大气体育直播自适应模板赛事直播门户…...

【启明智显技术分享】工业级HMI芯片Model系列GUI合成到项目中的指南

在工业自动化、智能终端HMI、车载仪表盘等领域&#xff0c;高性能的HMI&#xff08;人机界面&#xff09;芯片是不可或缺的核心组件。启明智显推出的Model系列&#xff08;如Model3C、Model3、Model4&#xff09;HMI芯片&#xff0c;以其卓越的性能和广泛的应用领域&#xff0c…...

开源服务器运维工具1Panel

1Panel是杭州飞致云信息科技有限公司推出的一款现代化、开源的Linux服务器运维管理面板。 以下是对1Panel的详细介绍&#xff1a; 一、基本信息 产品名称&#xff1a;1Panel所属公司&#xff1a;杭州飞致云信息科技有限公司编写语言&#xff1a;Golang上线时间&#xff1a;20…...

新版本源2.0大模型发布:Yuan2-2B-July-hf

​ 引言 近日&#xff0c;浪潮信息的新一代基础语言大模型源2.0 迎来了重要更新。浪潮信息正式发布了 Yuan2-2B-July-hf 模型&#xff0c;标志着源2.0系列模型在性能和功能上的进一步提升。这一版本将为开发者和研究人员提供更强大的工具&#xff0c;以满足各种语言处理需求。…...

用python生成GIF动图—用于博客插图或封面等

生成GIF动图&#x1f680; 由于目前自己是在做大模型&#xff0c;还有一些树莓派硬件之类的东西&#xff0c;一是大模型的流式输出的例子需要用到GIF&#xff0c;二是做单片机的时候例如一些灯的闪烁和变化需要用到&#xff0c;所以之前也是一直有这个打算所以就记录一下这个生…...

[RCTF2019]draw

下载是一个文本文档&#xff0c;百度AI cs pu lt 90 fd 500 rt 90 pd fd 100 rt 90 repeat 18[fd 5 rt 10] lt 135 fd 50 lt 135 pu bk 100 pd setcolor pick [ red orange yellow green blue violet ] repeat 18[fd 5 rt 10] rt 90 fd 60 rt 90 bk 30 rt 90 fd 60 pu lt 90 f…...

设计模式 - 责任链模式

💝💝💝首先,欢迎各位来到我的博客!本文深入理解设计模式原理、应用技巧、强调实战操作,提供代码示例和解决方案,适合有一定编程基础并希望提升设计能力的开发者,帮助读者快速掌握并灵活运用设计模式。 💝💝💝如有需要请大家订阅我的专栏【设计模式】哟!我会定…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

练习(含atoi的模拟实现,自定义类型等练习)

一、结构体大小的计算及位段 &#xff08;结构体大小计算及位段 详解请看&#xff1a;自定义类型&#xff1a;结构体进阶-CSDN博客&#xff09; 1.在32位系统环境&#xff0c;编译选项为4字节对齐&#xff0c;那么sizeof(A)和sizeof(B)是多少&#xff1f; #pragma pack(4)st…...

在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module

1、为什么要修改 CONNECT 报文&#xff1f; 多租户隔离&#xff1a;自动为接入设备追加租户前缀&#xff0c;后端按 ClientID 拆分队列。零代码鉴权&#xff1a;将入站用户名替换为 OAuth Access-Token&#xff0c;后端 Broker 统一校验。灰度发布&#xff1a;根据 IP/地理位写…...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。

1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj&#xff0c;再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...

九天毕昇深度学习平台 | 如何安装库?

pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子&#xff1a; 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

管理学院权限管理系统开发总结

文章目录 &#x1f393; 管理学院权限管理系统开发总结 - 现代化Web应用实践之路&#x1f4dd; 项目概述&#x1f3d7;️ 技术架构设计后端技术栈前端技术栈 &#x1f4a1; 核心功能特性1. 用户管理模块2. 权限管理系统3. 统计报表功能4. 用户体验优化 &#x1f5c4;️ 数据库设…...

GitFlow 工作模式(详解)

今天再学项目的过程中遇到使用gitflow模式管理代码&#xff0c;因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存&#xff0c;无论是github还是gittee&#xff0c;都是一种基于git去保存代码的形式&#xff0c;这样保存代码…...