Skip to content

securerandom slow

landon edited this page Sep 14, 2018 · 1 revision

随机数阻塞问题

  1. 现象:现在手机版本登录有一个现象,每次版本热更完毕后(游戏服务器重启),登录游戏服务器(外网阿里云)时要转菊花,转个10个秒才能进入游戏(第一次)

  2. 堆栈

    "NioProcessor-2" #62 prio=5 os_prio=0 tid=0x000000000193a000 nid=0x224e runnable [0x00007f64e39fa000]
       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 <0x0000000692f66dc8> (a sun.security.provider.SecureRandom)
            at java.security.SecureRandom.nextBytes(SecureRandom.java:468)
            - locked <0x0000000692f670e8> (a java.security.SecureRandom)
            at com.xx.net.mina.typical.security.AesAlgorithm.generateIv(AesAlgorithm.java:90)
            at com.xx.net.mina.typical.security.AesKeyConfig.<init>(AesKeyConfig.java:52)
            at com.xx.net.mina.typical.security.CryptoConfigFactory.create(CryptoConfigFactory.java:14)
            at com.xx.net.mina.typical.filters.SessionFilter.messageReceived(SessionFilter.java:97)
            at org.apache.mina.core.filterchain.DefaultIoFilterChain.callNextMessageReceived(DefaultIoFilterChain.java:542)
    
  3. 原因分析

    • 建立连接要走aes加密

    • aes加密算法要用到SecureRandom

      这里的罪魁祸首是SecureRandom generateSeed()。它使用/dev/random生成种子。但是/dev/random是一个阻塞数字生成器,如果它没有足够的随机数据提供,它就一直等,这迫使JVM等待。键盘和鼠标输入以及磁盘活动可以产生所需的随机性或熵。但在一个服务器缺乏这样的活动,可能会出现问题。
      
      随机数产生器会手机来自设备驱动器和其它源的环境噪声数据,并放入熵池中。产生器会评估熵池中的噪声数据的数量。当熵池为空时,这个噪声数据的收集是比较花时间的。这就意味着,Tomcat在生产环境中使用熵池时,会被阻塞较长的时间。
      
  4. 解决方案

    1. 启动参数带上 -Djava.security.egd=file:/dev/./urandom
    2. 在JVM环境中解决,修改 $JAVA_PATH/jre/lib/security/java.security 中改成 securerandom.source=file:/dev/urandom
    3. 最终确认方案2
      • 需要和运维沟通 统一加入到Java项目的checklist
      • 补充:即使跟运维反馈了,还是把参数放到启动shell里稳一点,万一他忘了呢,反正不冲突
  5. 什么是urandom

    /dev/random的一个副本是/dev/urandom(“unblocked”,非阻塞的随机数发生器[4]),它会重复使用熵池中的数据以产生伪随机数据。这表示对/dev/urandom的读取操作不会产生阻塞,但其输出的熵可能小于/dev/random的。它可以作为生成较低强度密码的伪随机数生成器,不建议用于生成高强度长期密码
    
  6. 参考

Clone this wiki locally