本文将介绍我的工作。 我将概述我的旅程,以实际找到正在发生的事情的根本原因。 我在本文中的目标是突出显示可以使用的技术示例,以实际跟踪奇怪的和随机的错误。
最后,我找到了一个非常清楚的解释,说明问题发生的原因和原因。 本文标题中存在“随机性”并非巧合。
开始–问题出在哪
问题是,在遵循使用开发虚拟机的指南时,有时启动应用程序会花费很长时间,然后失败。 两次启动应用程序后,我马上遇到了问题。 其他人也有同样的问题。
有人告诉我,没有人真正知道它为什么会以这种方式运行-但是重新启动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