欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

加速spring/springboot應(yīng)用啟動(dòng)速度詳解

 更新時(shí)間:2023年07月31日 10:07:30   作者:wen-pan  
這篇文章主要介紹了加速spring/springboot應(yīng)用啟動(dòng)速度詳解,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì)對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下

Gitee地址 :https://gitee.com/mr_wenpan/basis-enhance/tree/master

一、解決的痛點(diǎn)

在實(shí)際使用 Spring/Spring Boot 開發(fā)中,一些 Bean 在初始化過(guò)程中,會(huì)執(zhí)行一些準(zhǔn)備操作,如:

  • 拉取遠(yuǎn)程配置
  • 初始化數(shù)據(jù)源
  • 加載數(shù)據(jù)到j(luò)vm本地緩存

在應(yīng)用啟動(dòng)期間,這些 Bean 會(huì)增加 Spring 上下文刷新時(shí)間,導(dǎo)致應(yīng)用啟動(dòng)耗時(shí)變長(zhǎng)。為了加速應(yīng)用啟動(dòng),enhance-boot-async-init模塊提供了通過(guò)配置的可選項(xiàng),將 Bean 的初始化方法(init-method)使用單獨(dú)線程異步執(zhí)行的能力,加快 Spring 上下文加載過(guò)程,提高應(yīng)用啟動(dòng)速度。僅需要將 @BasisAsyncInit注解標(biāo)注到需要異步執(zhí)行的初始化方法上即可實(shí)現(xiàn)應(yīng)用啟動(dòng)過(guò)程中異步初始化。

加粗樣式## 二、異步初始化組件說(shuō)明

  • 需要顯示的將@BasisAsyncInit注解標(biāo)注在某些需要在啟動(dòng)過(guò)程中異步初始化的方法上才能實(shí)現(xiàn)啟動(dòng)過(guò)程中異步執(zhí)行。自動(dòng)探測(cè)所有可異步初始化的功能待實(shí)現(xiàn)(因?yàn)橐M(jìn)行依賴關(guān)系分析,比如在執(zhí)行異步初始化某個(gè)類時(shí),該類依賴關(guān)系非常復(fù)雜,并且可能他所依賴的某些bean還為被spring 容器創(chuàng)建,那么此時(shí)就需要去創(chuàng)建依賴的bean,且分析依賴樹。這個(gè)后面實(shí)現(xiàn))
  • 異步初始化時(shí)需要滿足如下條件才能算是安全的穩(wěn)定的異步初始化
    • 所有異步初始化完成之前容器不能算是啟動(dòng)成功。所以在容器啟動(dòng)完成前需要檢查是否所有的異步初始化都執(zhí)行完畢了
    • 如果有某個(gè)異步初始化執(zhí)行失敗了,則不能讓容器完成啟動(dòng),而是要阻止容器啟動(dòng)(如果異步初始化失敗了,但此時(shí)容器啟動(dòng)完成了,比如:將restful接口對(duì)外暴露了,那么將會(huì)導(dǎo)致調(diào)用失?。?/li>
    • 異步啟動(dòng)的線程池可根據(jù)項(xiàng)目的需要?jiǎng)討B(tài)配置
  • @BasisAsyncInit 注解可以使用在類上,也可以配合@Bean注解標(biāo)注在注入方法上,eg:
// 在注入HelloService處標(biāo)注@BasisAsyncInit注解(此時(shí)HelloService上可以不標(biāo)注)
@BasisAsyncInit
@Bean(initMethod = "init")
public HelloService helloService() {
  return new HelloService();
}
// 或者直接標(biāo)注在類上
@BasisAsyncInit
public class HelloService {
}

二、使用

1、yml配置文件中開啟異步初始化

basis:
  enhance:
    async:
      init:
        # 開啟異步初始化功能
        enable: true
        # 異步初始化線程池核心線程數(shù)量(不配置的話默認(rèn)是 2*cpu核數(shù) + 1)
        asyncInitBeanCoreSize: 10
        # 異步初始化線程池最大線程數(shù)量(不配置的話默認(rèn)是 2*cpu核數(shù) + 1)
        asyncInitBeanMaxSize: 20

2、創(chuàng)建異步初始化的bean

@Slf4j
// 炸裂標(biāo)注該bean是異步初始化的
@BasisAsyncInit
public class HelloService {
    /**
     * init方法
     */
    public void init() throws InterruptedException {
        log.info("i am HelloService.init method, start........");
      // 睡一會(huì)兒,這里模擬初始化方法非常耗時(shí),初始化方法執(zhí)行完畢前容器不允許正常啟動(dòng)成功
        TimeUnit.SECONDS.sleep(10);
      // 這里模擬異步初始化失敗容器不允許正常啟動(dòng)
//        final int i = 1 / 0;
        log.info("i am HelloService.init method, end........");
    }
    public String sayHello(String name) {
        log.info("hello {}", name);
        return "hello-" + name;
    }
}

3、注入異步初始化的bean并指定初始化方法

@Bean(initMethod = "init")
public HelloService helloService() {
    return new HelloService();
}

4、啟動(dòng)應(yīng)用觀察日志

①、正常啟動(dòng)日志

  • 可以看到HelloService的init方法是被異步線程pool-1-thread-1執(zhí)行的,而不是main線程
  • 可以看到main線程一直是等到異步線程pool-1-thread-1執(zhí)行完畢后再讓容器啟動(dòng)成功的

2023-07-30 16:09:29.238  INFO 24094 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 711 ms
2023-07-30 16:09:29.307  INFO 24094 --- [           main] o.b.e.a.init.executor.AsyncTaskExecutor  : create async-init-bean thread pool, corePoolSize: 10, maxPoolSize: 20.
2023-07-30 16:09:29.308  INFO 24094 --- [pool-1-thread-1] o.e.async.init.service.HelloService      : i am HelloService.init method, start........
2023-07-30 16:09:29.541  INFO 24094 --- [           main] io.undertow                              : starting server: Undertow - 2.2.8.Final
2023-07-30 16:09:29.545  INFO 24094 --- [           main] org.xnio                                 : XNIO version 3.8.0.Final
2023-07-30 16:09:29.549  INFO 24094 --- [           main] org.xnio.nio                             : XNIO NIO Implementation Version 3.8.0.Final
2023-07-30 16:09:29.578  INFO 24094 --- [           main] org.jboss.threads                        : JBoss Threads version 3.1.0.Final
2023-07-30 16:09:29.623  INFO 24094 --- [           main] o.s.b.w.e.undertow.UndertowWebServer     : Undertow started on port(s) 8080 (http)
2023-07-30 16:09:39.310  INFO 24094 --- [pool-1-thread-1] o.e.async.init.service.HelloService      : i am HelloService.init method, end........
2023-07-30 16:09:39.311  INFO 24094 --- [pool-1-thread-1] o.b.e.a.i.p.AsyncProxyBeanPostProcessor  : org.enhance.async.init.service.HelloService(helloService) init method execute 10003 ms.
2023-07-30 16:09:39.321  INFO 24094 --- [           main] o.e.async.init.DemoAsyncInitApplication  : Started DemoAsyncInitApplication in 11.168 seconds (JVM running for 11.853)

②、初始化方法執(zhí)行異常日志

  • 可以看到初始化方法異步執(zhí)行失敗,容器不會(huì)啟動(dòng)成功,不會(huì)將服務(wù)暴露出去

2023-07-30 16:12:30.000  INFO 24127 --- [           main] o.e.async.init.DemoAsyncInitApplication  : Starting DemoAsyncInitApplication using Java 1.8.0_281 on wenpf-MacBook-Pro.local with PID 24127 (/Users/wenpanfeng/MyFolder/DevelopWorkspace/IdeaWorkSpace/StudyProjects/basis-enhance/enhance-demo-parent/demo-enhance-async-init/target/classes started by wenpanfeng in /Users/wenpanfeng/MyFolder/DevelopWorkspace/IdeaWorkSpace/StudyProjects/basis-enhance)
2023-07-30 16:12:30.002  INFO 24127 --- [           main] o.e.async.init.DemoAsyncInitApplication  : No active profile set, falling back to default profiles: default
2023-07-30 16:12:30.763  WARN 24127 --- [           main] io.undertow.websockets.jsr               : UT026010: Buffer pool was not set on WebSocketDeploymentInfo, the default pool will be used
2023-07-30 16:12:30.779  INFO 24127 --- [           main] io.undertow.servlet                      : Initializing Spring embedded WebApplicationContext
2023-07-30 16:12:30.779  INFO 24127 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 731 ms
2023-07-30 16:12:30.860  INFO 24127 --- [           main] o.b.e.a.init.executor.AsyncTaskExecutor  : create async-init-bean thread pool, corePoolSize: 10, maxPoolSize: 20.
2023-07-30 16:12:30.861  INFO 24127 --- [pool-1-thread-1] o.e.async.init.service.HelloService      : i am HelloService.init method, start........
2023-07-30 16:12:31.109  INFO 24127 --- [           main] io.undertow                              : starting server: Undertow - 2.2.8.Final
2023-07-30 16:12:31.114  INFO 24127 --- [           main] org.xnio                                 : XNIO version 3.8.0.Final
2023-07-30 16:12:31.119  INFO 24127 --- [           main] org.xnio.nio                             : XNIO NIO Implementation Version 3.8.0.Final
2023-07-30 16:12:31.151  INFO 24127 --- [           main] org.jboss.threads                        : JBoss Threads version 3.1.0.Final
2023-07-30 16:12:31.197  INFO 24127 --- [           main] o.s.b.w.e.undertow.UndertowWebServer     : Undertow started on port(s) 8080 (http)
2023-07-30 16:12:40.867  INFO 24127 --- [           main] io.undertow                              : stopping server: Undertow - 2.2.8.Final
2023-07-30 16:12:40.879  INFO 24127 --- [           main] ConditionEvaluationReportLoggingListener : 

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled.
2023-07-30 16:12:40.899 ERROR 24127 --- [           main] o.s.boot.SpringApplication               : Application run failed

org.basis.enhance.async.init.exception.BasisAsyncInitException: 異步初始化失敗.
    at org.basis.enhance.async.init.executor.AsyncTaskExecutor.ensureAsyncTasksFinish(AsyncTaskExecutor.java:94) ~[classes/:na]
    at org.basis.enhance.async.init.listener.AsyncTaskExecutionListener.onApplicationEvent(AsyncTaskExecutionListener.java:28) ~[classes/:na]
    at org.basis.enhance.async.init.listener.AsyncTaskExecutionListener.onApplicationEvent(AsyncTaskExecutionListener.java:20) ~[classes/:na]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:176) ~[spring-context-5.3.8.jar:5.3.8]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:169) ~[spring-context-5.3.8.jar:5.3.8]
    at org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:143) ~[spring-context-5.3.8.jar:5.3.8]
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:421) ~[spring-context-5.3.8.jar:5.3.8]
    at org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:378) ~[spring-context-5.3.8.jar:5.3.8]
    at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:938) ~[spring-context-5.3.8.jar:5.3.8]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:586) ~[spring-context-5.3.8.jar:5.3.8]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:144) ~[spring-boot-2.4.8.jar:2.4.8]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:771) [spring-boot-2.4.8.jar:2.4.8]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:763) [spring-boot-2.4.8.jar:2.4.8]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:438) [spring-boot-2.4.8.jar:2.4.8]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:339) [spring-boot-2.4.8.jar:2.4.8]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1329) [spring-boot-2.4.8.jar:2.4.8]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1318) [spring-boot-2.4.8.jar:2.4.8]
    at org.enhance.async.init.DemoAsyncInitApplication.main(DemoAsyncInitApplication.java:15) [classes/:na]
Caused by: java.util.concurrent.ExecutionException: java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at java.util.concurrent.FutureTask.report(FutureTask.java:122) ~[na:1.8.0_281]
    at java.util.concurrent.FutureTask.get(FutureTask.java:192) ~[na:1.8.0_281]
    at org.basis.enhance.async.init.executor.AsyncTaskExecutor.ensureAsyncTasksFinish(AsyncTaskExecutor.java:92) ~[classes/:na]
    ... 17 common frames omitted
Caused by: java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
    at org.basis.enhance.async.init.processor.AsyncProxyBeanPostProcessor$AsyncInitializeBeanMethodInvoker.lambda$invoke$0(AsyncProxyBeanPostProcessor.java:119) ~[classes/:na]
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511) ~[na:1.8.0_281]
    at java.util.concurrent.FutureTask.run(FutureTask.java:266) ~[na:1.8.0_281]
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_281]
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_281]
    at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_281]
Caused by: java.lang.reflect.InvocationTargetException: null
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_281]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_281]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_281]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_281]
    at org.basis.enhance.async.init.processor.AsyncProxyBeanPostProcessor$AsyncInitializeBeanMethodInvoker.lambda$invoke$0(AsyncProxyBeanPostProcessor.java:115) ~[classes/:na]
    ... 5 common frames omitted
Caused by: java.lang.ArithmeticException: / by zero
    at org.enhance.async.init.service.HelloService.init(HelloService.java:25) ~[classes/:na]
    ... 10 common frames omitted

三、原理/源碼淺析

  • 使用實(shí)現(xiàn)了BeanFactoryPostProcessor接口的 AsyncInitBeanFactoryPostProcessor 類,在bean定義信息創(chuàng)建完成后會(huì)調(diào)postProcessBeanFactory方法的特性,在該方法中掃描容器中每個(gè)bean定義信息
  • 在解析bean定義信息時(shí),如果發(fā)現(xiàn)某個(gè)bean標(biāo)注了@BasisAsyncInit注解,則查找該bean的init方法,并以beanId為key,初始化方法名稱為value,將初始化方法保存到map中(ASYNC_BEAN_INFO_CACHE),后續(xù)統(tǒng)一處理
    • 源碼 @see org.basis.enhance.async.init.processor.AsyncInitBeanFactoryPostProcessor#postProcessBeanFactory
  • 再借助實(shí)現(xiàn)了BeanPostProcessor接口的AsyncProxyBeanPostProcessor類,攔截spring中每個(gè)bean的創(chuàng)建過(guò)程(bean初始化方法執(zhí)行前,see: postProcessBeforeInitialization),通過(guò)beanName名稱去ASYNC_BEAN_INFO_CACHE緩存中查找該bean是否有需要異步初始化的方法。
    • 如果沒有找到,則說(shuō)明該bean沒有需要異步初始化的方法,直接返回這個(gè)bean即可
    • 如果找到了,則在這里攔截該bean的創(chuàng)建(spring中常用的代理對(duì)象創(chuàng)建攔截點(diǎn)),為該bean創(chuàng)建一個(gè)代理對(duì)象(代理對(duì)象的核心邏輯AsyncInitializeBeanMethodInvoker),攔截該bean的每一個(gè)方法并返回。
    • 源碼 @see org.basis.enhance.async.init.processor.AsyncProxyBeanPostProcessor#postProcessBeforeInitialization
  • 當(dāng)容器啟動(dòng)過(guò)程中執(zhí)行bean的init方法時(shí),此時(shí)如果是需要異步執(zhí)行的初始化方法,則會(huì)進(jìn)入到我們上一步創(chuàng)建的代理對(duì)象的invoke方法內(nèi)。
    • 在該方法中將異步初始化方法提交到線程池中執(zhí)行,提交完成后會(huì)返回一個(gè) Future
    • 并且利用 Future 特性,將submit后返回的Future放到一個(gè)list (FUTURES)中統(tǒng)一管理
    • @see org.basis.enhance.async.init.processor.AsyncProxyBeanPostProcessor.AsyncInitializeBeanMethodInvoker#invoke
    • 那么容器啟動(dòng)過(guò)程中如何感知異步初始化方法執(zhí)行的結(jié)果呢?比如:是否都執(zhí)行完畢了?是否有異常?往下看
  • 利用實(shí)現(xiàn)了ApplicationListener 接口的AsyncTaskExecutionListener類,監(jiān)聽容器啟動(dòng)過(guò)程中發(fā)布的的刷新事件ContextRefreshedEvent,在監(jiān)聽到容器啟動(dòng)過(guò)程中發(fā)布的 ContextRefreshedEvent 事件后,檢查提交的每一個(gè)異步任務(wù)的執(zhí)行情況(利用上一步submit后返回的Future)
    • 如果有任意一個(gè)異步初始化方法執(zhí)行異常,則拋出異常,終止容器繼續(xù)啟動(dòng)
    • 如果所有的異步初始化方法都執(zhí)行完畢,則容器繼續(xù)啟動(dòng)
    • @see org.basis.enhance.async.init.listener.AsyncTaskExecutionListener#onApplicationEvent

到此這篇關(guān)于如何加速spring/springboot應(yīng)用啟動(dòng)速度的文章就介紹到這了,更多相關(guān)springboot啟動(dòng)速度內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java調(diào)用python代碼的五種方式總結(jié)

    Java調(diào)用python代碼的五種方式總結(jié)

    這篇文章主要給大家介紹了關(guān)于Java調(diào)用python代碼的五種方式,在Java中調(diào)用Python函數(shù)的方法有很多種,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-09-09
  • java實(shí)現(xiàn)ip地址與十進(jìn)制數(shù)相互轉(zhuǎn)換

    java實(shí)現(xiàn)ip地址與十進(jìn)制數(shù)相互轉(zhuǎn)換

    本文介紹在java中IP地址轉(zhuǎn)換十進(jìn)制數(shù)及把10進(jìn)制再轉(zhuǎn)換成IP地址的方法及實(shí)例參考,曬出來(lái)和大家分享一下
    2012-12-12
  • MyBatis中常用的SQL語(yǔ)句詳解

    MyBatis中常用的SQL語(yǔ)句詳解

    MyBatis是一種優(yōu)秀的持久層框架,它支持定制化SQL、存儲(chǔ)過(guò)程以及高級(jí)映射,本文介紹了MyBatis中常用的SQL語(yǔ)句,包括基本查詢、多條件查詢、關(guān)聯(lián)查詢、分頁(yè)查詢、插入、更新、刪除等,并結(jié)合MyBatis的特性進(jìn)行了說(shuō)明
    2025-03-03
  • java存儲(chǔ)以及java對(duì)象創(chuàng)建的流程(詳解)

    java存儲(chǔ)以及java對(duì)象創(chuàng)建的流程(詳解)

    下面小編就為大家?guī)?lái)一篇java存儲(chǔ)以及java對(duì)象創(chuàng)建的流程(詳解)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-05-05
  • 淺談JAVA內(nèi)存分配與參數(shù)傳遞

    淺談JAVA內(nèi)存分配與參數(shù)傳遞

    這篇文章主要介紹了JAVA內(nèi)存分配與參數(shù)傳遞,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • Java中對(duì)list map根據(jù)map某個(gè)key值進(jìn)行排序的方法

    Java中對(duì)list map根據(jù)map某個(gè)key值進(jìn)行排序的方法

    今天小編就為大家分享一篇Java中對(duì)list map根據(jù)map某個(gè)key值進(jìn)行排序的方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-07-07
  • Java后臺(tái)返回blob格式的文件流的解決方案

    Java后臺(tái)返回blob格式的文件流的解決方案

    在Java后臺(tái)開發(fā)中,經(jīng)常會(huì)遇到需要返回Blob格式的文件流給前端的情況,Blob是一種二進(jìn)制大對(duì)象類型,可以用于存儲(chǔ)大量的二進(jìn)制數(shù)據(jù),例如圖片、音頻、視頻等,本文將為你詳細(xì)介紹如何在Java后臺(tái)中返回Blob格式的文件流,需要的朋友可以參考下
    2024-08-08
  • java ThreadPool線程池的使用,線程池工具類用法說(shuō)明

    java ThreadPool線程池的使用,線程池工具類用法說(shuō)明

    這篇文章主要介紹了java ThreadPool線程池的使用,線程池工具類用法說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2020-10-10
  • java獲取本月日歷表的方法

    java獲取本月日歷表的方法

    這篇文章主要為大家詳細(xì)介紹了java獲取本月日歷表的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • spring依賴注入原理與用法實(shí)例分析

    spring依賴注入原理與用法實(shí)例分析

    這篇文章主要介紹了spring依賴注入原理與用法,結(jié)合實(shí)例形式分析了spring框架依賴注入的概念、原理、用法案例及相關(guān)操作注意事項(xiàng),需要的朋友可以參考下
    2019-10-10

最新評(píng)論