Solution
- MessageDigest => 根据需要经常创建新实例
- KeyFactory => 使用单个共享实例
- SecureRandom => 使用堆栈对象池 http://commons.apache.org/proper/commons-pool/apidocs/org/apache/commons/pool/impl/StackObjectPool.html
- 密码 => 使用堆栈对象池 http://commons.apache.org/proper/commons-pool/apidocs/org/apache/commons/pool/impl/StackObjectPool.html
Question
我在编写安全代码时经常遇到困境构架:“池或不池”
基本上这个问题分为两个“组”:
第 1 组:SecureRandom
因为调用nextBytes(...)
是同步的,它可能成为 Web 应用程序/多线程应用程序的瓶颈
第 2 组:加密货币服务提供商,例如MessageDigest
, Signature
, Cipher
, KeyFactory
,...(因为成本getInstance()
?)
你有什么意见 ?
你对此类问题有什么习惯?
编辑 09/07/2013
我终于花时间测试了@QwerkyShare
我自己上课,我发现结果相当......令人惊讶。
这门课缺乏我主要关心的内容:像游泳池这样的通用对象池 http://commons.apache.org/proper/commons-pool/apidocs/org/apache/commons/pool/impl/GenericObjectPool.html or 堆栈对象池 http://commons.apache.org/proper/commons-pool/apidocs/org/apache/commons/pool/impl/StackObjectPool.html.
所以我重新设计了课程来测试所有 4 个替代方案:
- 具有同步功能的单个共享实例gist https://gist.github.com/GrmpCerber/5956460
- 每个循环内的新实例(我对可以将摘要创建拉到循环之外的情况不感兴趣)gist https://gist.github.com/GrmpCerber/5956464
- 通用对象池:gist https://gist.github.com/GrmpCerber/5956444
- 堆栈对象池:gist https://gist.github.com/GrmpCerber/5956417
我必须将循环数降低到 100000,因为 1M 占用池的时间太多。
我还添加了一个Thread.yield()
在每个循环的末尾,使负载具有更好的形状。
结果(累积运行时间):
- MessageDigest
- 新实例:420 秒
- 单实例:550秒
- StackObjectPool:800秒
- 通用对象池:1900 秒
- KeyFactory
- 新实例:400s
- 单实例:350秒
- 堆栈对象池:2900 秒
- 通用对象池:3500 秒
- SecureRandom
- 堆栈对象池:1600 秒
- 新实例:2300 秒
- 通用对象池:2300s
- 单实例:2800秒
- Cipher
- 堆栈对象池:2800 秒
- 通用对象池:3500 秒
- 单实例:5100秒
- 新实例:8000秒
结论
对于 MessageDigest 和 KeyFactory,池是性能杀手,甚至比具有同步瓶颈的单个实例更糟糕,而当涉及到 SecureRandom 和 Cipher 时,它们确实非常有用
如果你给 100 个线程访问共享MessageDigest
让它们分别计算 1,000,000 个哈希值,然后在我的机器上,第一个线程在 70,160 毫秒内完成,最后一个线程在 98,748 毫秒内完成。
如果线程创建一个新实例MessageDigest
每次,第一个线程在 43,392 毫秒内完成,最后一个线程在 58,691 毫秒内完成。
Edit:
事实上,在这个示例中,只有两个线程,创建新实例的示例运行得更快。
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Share {
final byte[] bytes = new byte[100];
final MessageDigest sharedDigest;
final ExecutorService pool;
int threads = 100;
Share() throws NoSuchAlgorithmException {
sharedDigest = MessageDigest.getInstance("MD5");
pool = Executors.newFixedThreadPool(threads);
}
void go() {
for (int i=0; i<threads; i++) {
pool.execute(new Runnable() {
public void run() {
long start = System.currentTimeMillis();
for (int i=0; i<1000000; i++) {
/*
synchronized (sharedDigest) {
sharedDigest.reset();
sharedDigest.update(bytes);
sharedDigest.digest();
}*/
try {
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.reset();
digest.update(bytes);
digest.digest();
} catch (Exception ex) {
ex.printStackTrace();
}
}
long end = System.currentTimeMillis();
System.out.println(end-start);
pool.shutdown();
}
});
}
}
public static void main(String[] args) throws Exception {
Share share = new Share();
share.go();
}
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)