SpringBoot應用首次啟動慢的問題
最近一個專案中,遇到了一個奇怪的現象,spring boot應用啟動後,第一次訪問頁面總是會有大量的ajax請求pedding,然後重新整理頁面,大量的IOException
錯誤:
org.apache.catalina.connector.ClientAbortException: java.io.IOException: Broken pipe
at org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:356)
at org.apache.catalina.connector.OutputBuffer.flushByteBuffer(OutputBuffer.java:815)
at org.apache.catalina.connector.OutputBuffer.append(OutputBuffer.java:720)
at org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:391)
at org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:369)
at org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:96)
at com.fasterxml.jackson.core.json.UTF8JsonGenerator._flushBuffer(UTF8JsonGenerator.java:2039)
at com.fasterxml.jackson.core.json.UTF8JsonGenerator._writeBytes(UTF8JsonGenerator.java:1127)
at com.fasterxml.jackson.core.json.UTF8JsonGenerator.writeFieldName(UTF8JsonGenerator.java:253)
這個問題存在於伺服器(CentOS 7)上,在開發者機器(Windows、Mac)上無法復現,這就比較詭異了。首先想到的是日誌檢視法。日誌級別設為debug,檢視啟動整個流程,沒有發現明顯異常,只是看到在IOException
之前發現有連續的幾行類似的日誌:
[12-05 13:54:32.912| WARN|1-exec-3|o.a.catalina.util.SessionIdGeneratorBase.log:179] Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [67,400] milliseconds.
[12-05 13:54:32.915| WARN|1-exec-2|o.a.catalina.util.SessionIdGeneratorBase.log:179] Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [67,401] milliseconds.
[12-05 13:54:32.912| WARN|-exec-12|o.a.catalina.util.SessionIdGeneratorBase.log:179] Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [7,738] milliseconds.
[12-05 13:54:32.917| WARN|1-exec-9|o.a.catalina.util.SessionIdGeneratorBase.log:179] Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [7,755] milliseconds.
[12-05 13:54:32.917| WARN|1-exec-7|o.a.catalina.util.SessionIdGeneratorBase.log:179] Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [7,755] milliseconds.
這明顯是在生成Session ID上消耗了太長的時間。為了證明這一點,我在頁面ajax hang住期間,執行jstack ${pid}
命令,顯示hang住期間的記憶體堆疊。堆疊顯示如下(有精簡):
"http-nio-7001-exec-4" #47 daemon prio=5 os_prio=0 tid=0x00007f851609b000 nid=0x34c7 in Object.wait() [0x00007f84befed000]
java.lang.Thread.State: RUNNABLE
at sun.security.provider.SecureRandom.engineNextBytes(SecureRandom.java:221)
- locked <0x00000000f517be18> (a sun.security.provider.SecureRandom)
at java.security.SecureRandom.nextBytes(SecureRandom.java:468)
at java.security.SecureRandom.next(SecureRandom.java:491)
at java.util.Random.nextInt(Random.java:329)
at org.apache.catalina.util.SessionIdGeneratorBase.createSecureRandom(SessionIdGeneratorBase.java:269)
at org.apache.catalina.util.SessionIdGeneratorBase.getRandomBytes(SessionIdGeneratorBase.java:203)
at org.apache.catalina.util.StandardSessionIdGenerator.generateSessionId(StandardSessionIdGenerator.java:34)
at org.apache.catalina.util.SessionIdGeneratorBase.generateSessionId(SessionIdGeneratorBase.java:195)
at org.apache.catalina.session.ManagerBase.generateSessionId(ManagerBase.java:831)
at org.apache.catalina.session.ManagerBase.createSession(ManagerBase.java:663)
at org.apache.catalina.connector.Request.doGetSession(Request.java:3039)
at org.apache.catalina.connector.Request.getSession(Request.java:2429)
at org.apache.catalina.connector.RequestFacade.getSession(RequestFacade.java:896)
at org.apache.catalina.connector.RequestFacade.getSession(RequestFacade.java:908)
......
"http-nio-7001-exec-3" #46 daemon prio=5 os_prio=0 tid=0x00007f8515910800 nid=0x34c6 in Object.wait() [0x00007f84bf0ee000]
java.lang.Thread.State: RUNNABLE
at sun.security.provider.SecureRandom.engineNextBytes(SecureRandom.java:221)
- locked <0x00000000f5096b80> (a sun.security.provider.SecureRandom)
at java.security.SecureRandom.nextBytes(SecureRandom.java:468)
at java.security.SecureRandom.next(SecureRandom.java:491)
at java.util.Random.nextInt(Random.java:329)
at org.apache.catalina.util.SessionIdGeneratorBase.createSecureRandom(SessionIdGeneratorBase.java:269)
at org.apache.catalina.util.SessionIdGeneratorBase.getRandomBytes(SessionIdGeneratorBase.java:203)
at org.apache.catalina.util.StandardSessionIdGenerator.generateSessionId(StandardSessionIdGenerator.java:34)
at org.apache.catalina.util.SessionIdGeneratorBase.generateSessionId(SessionIdGeneratorBase.java:195)
at org.apache.catalina.session.ManagerBase.generateSessionId(ManagerBase.java:831)
at org.apache.catalina.session.ManagerBase.createSession(ManagerBase.java:663)
at org.apache.catalina.connector.Request.doGetSession(Request.java:3039)
at org.apache.catalina.connector.Request.getSession(Request.java:2429)
at org.apache.catalina.connector.RequestFacade.getSession(RequestFacade.java:896)
at org.apache.catalina.connector.RequestFacade.getSession(RequestFacade.java:908)
......
"http-nio-7001-exec-1" #44 daemon prio=5 os_prio=0 tid=0x00007f8514970800 nid=0x34c4 runnable [0x00007f84bf2ef000]
java.lang.Thread.State: RUNNABLE
at java.io.FileInputStream.readBytes(Native Method)
at java.io.FileInputStream.read(FileInputStream.java:255)
at sun.security.provider.SeedGenerator$URLSeedGenerator.getSeedBytes(SeedGenerator.java:539)
at sun.security.provider.SeedGenerator.generateSeed(SeedGenerator.java:144)
at sun.security.provider.SecureRandom$SeederHolder.<clinit>(SecureRandom.java:203)
at sun.security.provider.SecureRandom.engineNextBytes(SecureRandom.java:221)
- locked <0x00000000f7173220> (a sun.security.provider.SecureRandom)
at java.security.SecureRandom.nextBytes(SecureRandom.java:468)
at java.security.SecureRandom.next(SecureRandom.java:491)
at java.util.Random.nextInt(Random.java:329)
at org.apache.catalina.util.SessionIdGeneratorBase.createSecureRandom(SessionIdGeneratorBase.java:269)
at org.apache.catalina.util.SessionIdGeneratorBase.getRandomBytes(SessionIdGeneratorBase.java:203)
at org.apache.catalina.util.StandardSessionIdGenerator.generateSessionId(StandardSessionIdGenerator.java:34)
at org.apache.catalina.util.SessionIdGeneratorBase.generateSessionId(SessionIdGeneratorBase.java:195)
at org.apache.catalina.session.ManagerBase.generateSessionId(ManagerBase.java:831)
at org.apache.catalina.session.ManagerBase.createSession(ManagerBase.java:663)
at org.apache.catalina.connector.Request.doGetSession(Request.java:3039)
at org.apache.catalina.connector.Request.getSession(Request.java:2429)
at org.apache.catalina.connector.RequestFacade.getSession(RequestFacade.java:896)
at org.apache.catalina.connector.RequestFacade.getSession(RequestFacade.java:908)
......
大量的執行緒hang在sun.security.provider.SecureRandom.engineNextBytes(SecureRandom.java:221)
階段。上搜尋引擎(一定要英文的)搜尋類似的內容,發現這不是一個個例,甚至JDK bug列表匯中就有相似的bug,如JDK-6521844 : SecureRandom hangs on Linux Systems,但這些bug都標記為fixed。但明顯沒有完全fix掉啊。繼續找,找到兩篇文獻Avoiding JVM Delays Caused by Random Number Generation,How do I make Tomcat startup faster?,正好記錄了這個隨機數生成慢的原因和解決方案。
原來,Java隨機數生成依賴熵源(Entropy Source),預設的阻塞型的 /dev/random
熵源可能導致阻塞,而換一個非阻塞的 /dev/urandom
的熵源就可以了。
具體操作來說,有兩種方法,一種是修改Java配置檔案(見Avoiding JVM Delays Caused by Random Number Generation),另一個是修改應用啟動指令碼。對於需要多例項各處部署的應用來說,修改啟動指令碼是成本最低,最可控的方案。在啟動指令碼中加入配置屬性:-Djava.security.egd=file:/dev/./urandom
,然後啟動,問題解決。需要注意的是,spring boot中,這個引數應該加在-jar
引數之前,如果加在-jar
引數之後,可能不起作用。
相關文章
- 一個JAVA應用啟動緩慢問題排查 --來自jdk SecureRandom 的困惑JavaJDKrandom
- WEB應用訪問緩慢的問題定位Web
- 解決Chrome瀏覽器啟動速度慢的問題Chrome瀏覽器
- Swift 首次除錯斷點慢的問題解法 | 優酷 Swift 實踐Swift除錯斷點
- win10 上使用GIT慢的問題,或者命令列反應慢的問題Win10Git命令列
- 利用 Arthas 解決啟動 StandbyNameNode 載入 EditLog 慢的問題
- 生產環境部署springcloud微服務啟動慢的問題排查SpringGCCloud微服務
- 記一次docker容器啟動/關閉,非常慢的問題Docker
- Android應用為啥啟動慢?小米工程師回應Android工程師
- SpringBoot 應用程式啟動過程探祕Spring Boot
- ssh連線反應慢問題
- 解決建立SpringBoot工程載入較慢的問題Spring Boot
- 解決Weblogic域建立、啟動、進入控制檯慢問題Web
- SpringBoot 居然有 44 種應用啟動器Spring Boot
- rabbitmq的啟動問題MQ
- php CURL 伺服器響應慢的問題PHP伺服器
- 筆記:啟動登入HOMESTEAD、建立應用、解決問題筆記
- 記一次springboot的開機啟動自動關閉問題Spring Boot
- 如何解決"應用程式無法啟動,因為應用程式的並行配置不正確"問題並行
- SpringBoot2.x入門:應用打包與啟動Spring Boot
- 資料庫響應慢問題處理資料庫
- Node 應用的 Systemd 啟動
- SpringBoot mysql驅動問題Spring BootMySql
- fastclick.js解決移動端點選事件反應慢問題ASTJS事件
- 解決github訪問慢的問題Github
- Springboot程式啟動慢及JVM上的隨機數與熵池策略Spring BootJVM隨機熵
- laravel sail 首次啟動報錯LaravelAI
- 一個面試題引起的SpringBoot啟動解析面試題Spring Boot
- 應用SqlitePCL應該注意的問題SQLite
- Arthas實踐–快速排查SpringBoot應用404/401問題Spring Boot
- 如何解決蘋果Mac安裝Axure首次開啟報錯的問題?蘋果Mac
- Node應用的Systemd啟動(轉)
- JdonFramework應用問題?????Framework
- 解決weblogic啟動慢和建立域慢的方法Web
- 一次資料庫響應緩慢的問題排查資料庫
- flutter 應用啟動流程Flutter
- 使用 docker-sync 解決 docker for Mac 啟動的虛擬容器程式執行緩慢的問題DockerMac
- SpringBoot使用IDEA設定的外部Tomcat啟動,遇到的問題和解決Spring BootIdeaTomcat