CentOS 7 下Tomcat啟動超慢的原因及解決方案

weixin_34128411發表於2019-02-24

現象

CentOS 7系統中安裝好openjdk和Tomcat後,啟動過程很慢,長達數分鐘,日誌如下:

17:27:53.596 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet Engine: Apache Tomcat/8.5.38
17:27:53.644 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/opt/tomcat_8_5_38/webapps/docs]
17:32:31.001 WARNING [localhost-startStop-1] org.apache.catalina.util.SessionIdGeneratorBase.createSecureRandom Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [276,660] milliseconds.
17:32:31.022 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/opt/tomcat_8_5_38/webapps/docs] has finished in [277,378] ms
17:32:31.022 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/opt/tomcat_8_5_38/webapps/manager]
17:32:31.101 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/opt/tomcat_8_5_38/webapps/manager] has finished in [79] ms
17:32:31.101 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/opt/tomcat_8_5_38/webapps/examples]
17:32:31.509 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/opt/tomcat_8_5_38/webapps/examples] has finished in [408] ms
17:32:31.510 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/opt/tomcat_8_5_38/webapps/host-manager]
17:32:31.559 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/opt/tomcat_8_5_38/webapps/host-manager] has finished in [49] ms
17:32:31.559 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/opt/tomcat_8_5_38/webapps/ROOT]
17:32:31.576 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/opt/tomcat_8_5_38/webapps/ROOT] has finished in [17] ms
17:32:31.605 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"]
17:32:31.660 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["ajp-nio-8009"]
17:32:31.662 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 278084 ms

tomcat啟動耗時278084ms摺合278秒,對於剛剛安裝的乾淨tomcat,這肯定是不對勁的。
其中有一條日誌引起了筆者的注意:

17:32:31.001 WARNING [localhost-startStop-1] org.apache.catalina.util.SessionIdGeneratorBase.createSecureRandom Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [276,660] milliseconds.

顯然tomcat執行到這裡時出問題了,google了一下,經過一番搜尋明白了其中的緣由。

原因

在tomcat官方wiki文件的HowToFasterStartUp章節中,Entropy Source部分有一段這樣的說明:

Tomcat 7+嚴重依賴SecureRandom類為其sessionID和其他地方提供隨機值。如果用於初始化SecureRandom的熵源缺少熵,則可能會在啟動期間導致延遲,具體取決於您的JRE。發生這種情況時,您會在日誌中看到警告,例如:

<DATE> org.apache.catalina.util.SessionIdGenerator createSecureRandom
INFO: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [5172] milliseconds.

有一種方法可以通過設定以下系統屬性來配置JRE以使用非阻塞熵源:

-Djava.security.egd = file:/dev/./urandom

另請注意,使用非阻塞源替換阻塞熵源實際上會降低安全性,因為您獲得的隨機資料較少。

從這裡我們得知Tocmat的Session ID是通過SHA1PRNG演算法計算得到的,計算Session ID的時候必須有一個金鑰,為了提高安全性Tomcat在啟動的時候會通過隨機生成一個金鑰,它強依賴於獲取熵池中的隨機數來進行建立。
那麼什麼是/dev/random?什麼是熵池

/dev/random
從維基百科得知,在UNIX作業系統(包括類UNIX系統)中,/dev/random是一個特殊的裝置檔案,可以用作隨機數生成器或偽隨機數生成器。
Linux核心中的是第一個以背景噪聲產生真正的隨機數產生的實現,它允許程式訪問來自裝置驅動程式或其它來源的背景噪聲。
Linux上有兩個通用的隨機裝置:/dev/random/dev/urandom。其中/dev/random的隨機性最好,因為它是一個阻塞的裝置。而/dev/random的一個副本是/dev/urandom(“unblocked”,非阻塞的隨機數生成器),它會重複使用熵池中的資料以產生偽隨機資料。這表示對/dev/urandom的讀取操作不會產生阻塞,但其輸出的熵可能小於/dev/random的。所以它可以作為生成較低強度密碼的偽隨機數生成器,不建議用於生成高強度長期密碼。

熵池
熵池本質上是若干位元組,/proc/sys/kernel/random/entropy_avail中儲存了熵池現在的大小,/proc/sys/kernel/random/poolsize是熵池的最大容量,單位都是bit。如果entropy_avail的值小於要產生的隨機數bit數,那麼/dev/random就會堵塞。

為什麼熵池不夠用?

熵池實際上是從各種noice source中獲取資料,noice source可能是鍵盤事件、滑鼠事件、裝置時鐘中等。linux核心從2.4升級到2.6時,處於安全性的考慮,廢棄了一些source。source減少了,熵池補給的速度當然也變慢,進而不夠用。
其實,通過消耗熵池,可以構造DDOS攻擊。原理很簡單,熵池空了,依賴隨機數的業務(SSL,加密等)就不能正常進行。

通過以上資訊,筆者得知該問題是由於熵池不足導致的。怎麼解決?

解決方案

方法一、降低熵生成的隨機性要求

使用非阻塞性的生成器/dev/urandom代替/dev/random

1、可在JVM環境中配置
通過配置發生器指定熵收集守護程式
修改$JAVA_PATH/jre/lib/security/java.security中引數securerandom.source為:

securerandom.source=file:/dev/./urandom

2、也可在Tomcat環境中配置
通過配置JRE使用非阻塞的Entropy Source獲取熵
$TOMCAT_HOME/bin/catalina.sh中加入:

if [[ "$JAVA_OPTS" != *-Djava.security.egd=* ]]; then
    JAVA_OPTS="$JAVA_OPTS -Djava.security.egd=file:/dev/./urandom"
fi

這個系統屬性egd表示熵收集守護程式(entropy gathering daemon)。

方法二、使用熵生成器補充熵池(推薦)

1、[硬體隨機數生成器]安裝並使用rng-tools作為額外的熵隨機數生成器(推薦)

# 安裝rng-tools
yum install rng-tools -y
# 測試rngd
rngd -f
# 啟動rngd
systemctl start rngd
# 設定自啟動
systemctl enable rngd
# 檢視自啟動列表
systemctl list-unit-files

cat /dev/random命令會消耗熵池,rngd守護程式會補充熵池,可使用如下命令來測試隨機數生成的情況:

cat /dev/random | rngtest -c 100
# 測試結果
rngtest 6
Copyright (c) 2004 by Henrique de Moraes Holschuh
This is free software; see the source for copying conditions.  There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

rngtest: starting FIPS tests...
rngtest: bits received from input: 2000032
rngtest: FIPS 140-2 successes: 100
rngtest: FIPS 140-2 failures: 0
rngtest: FIPS 140-2(2001-10-10) Monobit: 0
rngtest: FIPS 140-2(2001-10-10) Poker: 0
rngtest: FIPS 140-2(2001-10-10) Runs: 0
rngtest: FIPS 140-2(2001-10-10) Long run: 0
rngtest: FIPS 140-2(2001-10-10) Continuous run: 0
rngtest: input channel speed: (min=13.823; avg=42.721; max=10761.019)Kibits/s
rngtest: FIPS tests speed: (min=93.041; avg=129.531; max=136.239)Mibits/s
rngtest: Program run time: 45733452 microseconds

2、[軟體隨機數生成器]在rng-tools仍不滿足的情況下,可使用haveged作為額外的熵隨機數生成器

haveged專案的目的是提供一個簡單易用的不可預測隨機數生成器,基於HAVEGE演算法。Haveged可以解決在某些情況下,系統熵過低的問題。此程式無法保證熵的質量,如果對安全要求較高,請考慮使用硬體隨機數生成器rng-tools。

要檢查是否需要 Haveged,可使用下面命令檢視當前收集到的熵:

cat /proc/sys/kernel/random/entropy_avail

如果結果比較低 (<1000),建議安裝 haveged,否則加密程式會處於等待狀態,直到系統有足夠的熵。

# 安裝haveged
yum install haveged -y
# 啟動haveged
systemctl start haveged
# 設定自啟動
systemctl enable haveged

安裝 haveged 之後,可以再次檢視系統熵看下有無提升。


因為方法一存在一定的不安全性,且需要對環境進行配置,為了滿足熵的需要,這裡筆者選擇了第二種方法,使用rng-tools作為額外的熵隨機數生成器,同以上操作後順利解決了問題。

操作後重啟tomcat日誌如下,啟動速度快了兩個數量級:

17:58:07.068 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet Engine: Apache Tomcat/8.5.38
17:58:07.088 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/opt/tomcat_8_5_38/webapps/docs]
17:58:07.740 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/opt/tomcat_8_5_38/webapps/docs] has finished in [652] ms
17:58:07.740 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/opt/tomcat_8_5_38/webapps/manager]
17:58:07.815 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/opt/tomcat_8_5_38/webapps/manager] has finished in [75] ms
17:58:07.816 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/opt/tomcat_8_5_38/webapps/examples]
17:58:08.241 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/opt/tomcat_8_5_38/webapps/examples] has finished in [425] ms
17:58:08.241 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/opt/tomcat_8_5_38/webapps/host-manager]
17:58:08.268 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/opt/tomcat_8_5_38/webapps/host-manager] has finished in [27] ms
17:58:08.269 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/opt/tomcat_8_5_38/webapps/ROOT]
17:58:08.306 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/opt/tomcat_8_5_38/webapps/ROOT] has finished in [37] ms
17:58:08.335 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"]
17:58:08.424 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["ajp-nio-8009"]
17:58:08.429 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 1411 ms

參考文件:
https://stackoverflow.com/questions/40383430/tomcat-takes-too-much-time-to-start-java-securerandom
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6202721
http://openjdk.java.net/jeps/123
https://zh.wikipedia.org/zh-hans//dev/random
https://wiki.apache.org/tomcat/HowTo/FasterStartUp
https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/security_guide/sect-security_guide-encryption-using_the_random_number_generator
https://wiki.archlinux.org/index.php/Rng-tools
https://wiki.archlinux.org/index.php/Haveged
https://www.digitalocean.com/community/tutorials/how-to-setup-additional-entropy-for-cloud-servers-using-haveged

相關文章