本文將介紹我的工作。 我將概述我的旅程,以實際找到正在發生的事情的根本原因。 我在本文中的目標是突出顯示可以使用的技術示例,以實際跟蹤奇怪的和隨機的錯誤。
最后,我找到了一個非常清楚的解釋,說明問題發生的原因和原因。 本文標題中存在“隨機性”并非巧合。
開始–問題出在哪
問題是,在遵循使用開發虛擬機的指南時,有時啟動應用程序會花費很長時間,然后失敗。 兩次啟動應用程序后,我馬上遇到了問題。 其他人也有同樣的問題。
有人告訴我,沒有人真正知道它為什么會以這種方式運行-但是重新啟動VM可能會解決它。 它不適合我。
但是我喜歡深入研究問題-所以我想知道出了什么問題。
將檔案耙到Solr
運行rake app:run
有時會發生錯誤。 通過使用--trace --verbose
(調試選項)運行rake,我可以看到該應用程序正在等待黑子啟動Apache Solr搜索服務。
所以問題是,該過程始終花在哪里? 開始的好處是,rake和sunspot用Ruby編寫。 因此,我閱讀了代碼,并添加了一些輸出語句,以查看哪個語句花費了很長時間。 原來,這是Java進程啟動了Solr。
挖掘Java
Apache Solr將一些輸出發送到sdtout-但黑子將其隱藏在您的面前。 但是由于調試時任何輸出都非常有用,因此我想查看輸出。
我使用ps -aux
查找了ps -aux
發出的確切命令行參數,并從命令行手動啟動了該過程。 幸運的是,它仍然花費了一些時間-所以我知道問題不在Ruby代碼中。
通過端口映射程序nmap,我發現啟動該服務需要2秒鐘到2分鐘以上的時間。 但是耙任務只等待了10秒鐘。
現在,有了控制臺輸出,我可以看到,Solr在內部使用Jetty – 在版本6.1.3中是Jetty ,這花了很多時間– 在這種情況下,它花了時間。 因此,我從源代碼管理中下載了6.1.3版的代碼,并查看了代碼。
我發現了一個名為DEBUG
的系統屬性,它會增加輸出。 我啟用了它,并看到最后的調試輸出(花了很長時間)與啟動會話服務有關(不幸的是,我丟失了指向代碼的指針)。
這并沒有真正幫助我。 我嘗試使Jetty日志與log4j一起工作以最終看到更多內容,但未成功-但我失敗了,不得不放棄當晚(第二個)。
調試Java代碼
因此,如果您沒有從日志中獲取任何信息,則仍然可以使用調試器。 但是,代碼在VM(Ubuntu盒)上失敗了,但在我的本地計算機上卻失敗了。 但是幸運的是Java有一個遠程調試器 。
我使用遠程調試參數啟動Java進程,并使用SVN中的Jetty代碼在IntelliJ中附加了一個偵聽器。 我試圖使執行在最后一個日志輸出附近的斷點處停止–但是此方法經常被調用,而我沒有在正確的時機到達斷點。
但是,當事情花費很長時間時,僅停止正在運行的進程并查看堆棧跟蹤可能會有所幫助。 因此,我停止了執行,并從Jetty中搜索了最后一個方法框架,該框架正在調用JDK –在這里,我發現了以下注釋:
//This operation may block on some systems with low entropy. See this page
//for workaround suggestions:
//http://docs.codehaus.org/display/JETTY/Connectors+slow+to+startup
Log.debug('Init SecureRandom.');
_random=new SecureRandom();
解決方案
最后–我確實轉到了引用的Jetty Wiki頁面 。 我發現,Java的java.util.SecureRandom
使用來自操作系統的真實熵-當沒有可用的熵時,它將阻塞。
僅使用普通的java.util.Random
的方法,來自Wiki頁面的解決方案將很困難,因為配置文件位于sunspot和Solr內部。 但是行“ NB一些解決方法報告使用/dev/./urandom而不是/ dev / urandom”給了我一個提示,那就是另找。
我用Google搜索解決方案,并在Stack Overflow上發現,可以在Java進程/dev/./urandom
/dev/urandom
隨機替換為/dev/./urandom
。 這可以通過使用命令行參數-Djava.security.egd=file:/dev/./urandom
或通過將其替換為文件/usr/lib/jvm/java-6-openjdk/jre/lib/security/java.security
(在Ubuntu機器上)。
那怎么了? Java的安全隨機數將不接受/dev/urandom
作為隨機數的源 。 /dev/urandom
是無阻塞的,這意味著它將在沒有可用的實際熵的情況下返回可以猜測的隨機數。 因此,Java默認為/dev/random
,它將阻塞并等待,直到出現熵為止。
這就是為什么在生產或開發機器上極不可能發生阻塞的原因-但是在使用虛擬機時,它確實發生了,因為虛擬機的網絡流量和IO并不多。 當您經常重啟應用程序時(尤其是在某些情況下無法正常工作時),這種情況通常會發生。
解決方法有點有趣。 Java根據字符串拒絕/dev/urandom
。 它不會拒絕/dev/./urandom
,它當然指向同一個可能不安全的隨機數生成器。
最后的話
該修補程序會帶來潛在的安全風險–但是在開發虛擬機上,這不是問題。
如您所見,在系統中查找錯誤或問題涉及使用技術,這些技術使您更接近根本原因。 但是,沒有黃金路。 有時,一種技術將無法為您提供更多信息。 因此,您將不得不嘗試另一種技術。 有時,您的直覺也會對您有所幫助。
到達這里后,您對這個故事有何看法?
參考:在Johannes Thones博客博客上,我們的JCG合作伙伴 Johannes Thones 狩獵隨機的錯誤-一個真實的故事 。
翻譯自: https://www.javacodegeeks.com/2012/10/hunting-random-bug-true-story.html