池或不池java加密服务提供商



解决方案

  • MessageDigest=>根据需要经常创建新实例
  • KeyFactory=>使用单个共享实例
  • SecureRandom=>使用StackObjectPool
  • 密码=>使用StackObjectPool

问题

我在编写安全框架时遇到了一个常见的问题:"池还是不池">

基本上,这个问题分为两个"组":

  1. 组1:SecureRandom,因为对nextBytes(...)的调用是同步的,它可能会成为WebApp/多线程应用的瓶颈

  2. 第2组:加密服务提供商,如MessageDigestSignatureCipherKeyFactory。。。(因为getInstance()的成本?)

你有什么意见?

你对这些问题有什么习惯?

编辑2013年7月9日

我终于花时间自己测试了@QwerkyShare课程,我发现结果很好。。。令人惊讶的

这个类缺少我主要关心的问题:像GenericObjectPool或StackObjectPool这样的池。

因此,我重新设计了这个类,以测试所有4种替代方案:

  • 带有同步要点的单个共享实例
  • 每个循环中的新实例(我对可以将摘要创建拉到循环之外的情况不感兴趣)要点
  • GenericObjectPool:gist
  • StackObjectPool:gist

我不得不将循环数降低到100000,因为1M在池中花费了太多时间。

我还在每个循环的末尾添加了一个Thread.yield(),以使负载具有更好的形状。

结果(累积运行时间):

  • 消息摘要
    • 新实例:420 s
    • 单实例:550 s
    • StackObjectPool:800秒
    • 通用对象池:1900 s
  • KeyFactory
    • 新实例:400
    • 单个实例:350 s
    • StackObjectPool:2900 s
    • 通用对象池:3500 s
  • 安全随机
    • StackObjectPool:1600 s
    • 新实例:2300 s
    • GenericObjectPool:2300
    • 单实例:2800 s
  • 密码
    • 堆栈对象池:2800 s
    • 通用对象池:3500 s
    • 单实例:5100 s
    • 新实例:8000 s

结论

对于MessageDigest和KeyFactory来说,池是性能杀手,甚至比具有同步瓶颈的单个实例更糟糕,而对于SecureRandom和Cipher 来说,它们确实很有用

如果你给100个线程访问一个共享的MessageDigest,并让他们每个计算1000000个哈希,那么在我的机器上,第一个线程在70160ms内完成,最后一个线程在98748ms内完成。

如果线程每次都创建一个新的MessageDigest实例,那么第一个线程将在43392ms和最后58691ms内完成。

编辑:
事实上,在这个例子中,只有两个线程,创建新实例的例子运行得更快。

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();
}
}

这个测试似乎有利于缓存

long t0 = System.currentTimeMillis();
byte[] bytes = new byte[100];
MessageDigest md = MessageDigest.getInstance("MD5");
for(int i = 0; i < 1000000; i++) {
//MessageDigest md = MessageDigest.getInstance("MD5");
md.reset();
md.update(bytes);
md.digest();
}
System.out.println(System.currentTimeMillis() - t0);

当md在循环外时,它会打印579,而在循环内时打印-953。

最新更新