java - 生成随机 UUID 非阻塞
问题描述
通过使用 Blockhound io.projectreactor.tools blockhound-junit-platform我发现 UUID.randomUUID 是一个阻塞调用,这对我们来说是个问题,因为我们使用的是 Spring boot Webflux 版本 2.2.2.RELEASE
是否有任何其他方法可以以非阻塞方式获取随机 uuid,或者是否有任何其他 java 库推荐用于非阻塞生成随机字符串。
来自blockhound的堆栈跟踪:
java.lang.Error: Blocking call! java.io.FileInputStream#readBytes
at reactor.blockhound.BlockHound$Builder.lambda$new$0(BlockHound.java:196) ~[blockhound-1.0.1.RELEASE.jar:na]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
Error has been observed at the following site(s):
|_ checkpoint ⇢ org.springframework.boot.actuate.metrics.web.reactive.server.MetricsWebFilter [DefaultWebFilterChain][ExceptionHandlingWebHandler]
Stack trace:
at reactor.blockhound.BlockHound$Builder.lambda$new$0(BlockHound.java:196) ~[blockhound-1.0.1.RELEASE.jar:na]
at reactor.blockhound.BlockHound$Builder.lambda$install$6(BlockHound.java:318) ~[blockhound-1.0.1.RELEASE.jar:na]
at reactor.blockhound.BlockHoundRuntime.checkBlocking(BlockHoundRuntime.java:46) ~[na:na]
at java.base/java.io.FileInputStream.readBytes(FileInputStream.java) ~[na:na]
at java.base/java.io.FileInputStream.read(FileInputStream.java:279) ~[na:na]
at java.base/java.io.FilterInputStream.read(FilterInputStream.java:133) ~[na:na]
at java.base/sun.security.provider.NativePRNG$RandomIO.readFully(NativePRNG.java:424) ~[na:na]
at java.base/sun.security.provider.NativePRNG$RandomIO.ensureBufferValid(NativePRNG.java:526) ~[na:na]
at java.base/sun.security.provider.NativePRNG$RandomIO.implNextBytes(NativePRNG.java:545) ~[na:na]
at java.base/sun.security.provider.NativePRNG.engineNextBytes(NativePRNG.java:220) ~[na:na]
at java.base/java.security.SecureRandom.nextBytes(SecureRandom.java:741) ~[na:na]
at java.base/java.util.UUID.randomUUID(UUID.java:150) ~[na:na]
解决方案
这是关于如何使用uuid-creator减少 Linux 上的线程争用的三个示例的列表。
另一个示例使用ulid-creator生成单调 ULID,然后将它们转换为 RFC-4122 UUID(标准)。单调 ULID 生成速度非常快。
基准代码可在GitHub Gist上获得。
示例 1:使用UuidCreator
和SHA1PRNG
如何设置供SHA1PRNG
以下人员使用的算法UuidCreator
:
# Append to /etc/environment or ~/.profile
# Use the the algorithm SHA1PRNG for SecureRandom
export UUIDCREATOR_SECURERANDOM="SHA1PRNG"
// generate a random-based UUID
UUID uuid = UuidCreator.getRandomBased();
UuidCreator
使用固定的SecureRandom
. 该变量UUIDCREATOR_SECURERANDOM
告诉UuidCreator
使用另一种SecureRandom
算法,而不是NativePRNG
在 Linux 上。UuidCreator
与SHA1PRNG
算法一起生成的 UUID 的线程争用(更少阻塞)比UUID.randomUUID()
.
查看使用 4 个线程的基准测试:
----------------------------------------------------------------------
Benchmark Mode Cnt Score Error Units
----------------------------------------------------------------------
UUID.randomUUID() thrpt 5 1423,060 ± 30,125 ops/ms
UuidCreator.getRandomBased() thrpt 5 10616,016 ± 281,486 ops/ms
----------------------------------------------------------------------
示例 2:使用ThreadLocalRandom
如何使用以下方法实现 UUID 生成器ThreadLocalRandom
:
public class UuidGenerator {
private static final RandomBasedFactory UUID_FACTORY;
static {
UUID_FACTORY = new RandomBasedFactory((int length) -> {
final byte[] bytes = new byte[length];
ThreadLocalRandom.current().nextBytes(bytes);
return bytes;
});
}
public static UUID generate() {
return UUID_FACTORY.create();
}
}
ThreadLocalRandom
是一个快速(且不安全)的随机生成器,没有线程争用(非阻塞)。
查看使用 4 个线程的基准测试:
----------------------------------------------------------------------
Benchmark Mode Cnt Score Error Units
----------------------------------------------------------------------
UUID.randomUUID() thrpt 5 1423,060 ± 30,125 ops/ms
UuidGenerator.generate() thrpt 5 85390,979 ± 1564,589 ops/ms
----------------------------------------------------------------------
示例 3:使用RandomBasedFactory[]
和SHA1PRNG
如何使用 ana 数组RandomBasedFactory
和SHA1PRNG
算法实现 UUID 生成器:
public static class UuidGenerator {
private static final int SIZE = 8; // you choose
private static final RandomBasedFactory[] FACTORIES;
static {
FACTORIES = new RandomBasedFactory[SIZE];
try {
for (int i = 0; i < FACTORIES.length; i++) {
// SHA1PRNG or DRBG can be used to reduce thread contention.
SecureRandom argument = SecureRandom.getInstance("SHA1PRNG");
FACTORIES[i] = new RandomBasedFactory(argument);
}
} catch (NoSuchAlgorithmException e) {
// oops!
}
}
public static UUID generate() {
// calculate the factory index given the current thread ID
final int index = (int) Thread.currentThread().getId() % SIZE;
return FACTORIES[index].create();
}
}
一组RandomBasedFactory
with 和SHA1PRNG
算法可以生成具有较少线程争用(较少阻塞)的 UUID。
查看使用 4 个线程的基准测试:
----------------------------------------------------------------------
Benchmark Mode Cnt Score Error Units
----------------------------------------------------------------------
UUID.randomUUID() thrpt 5 1423,060 ± 30,125 ops/ms
UuidGenerator.generate() thrpt 5 10048,747 ± 195,209 ops/ms
----------------------------------------------------------------------
示例 4:使用UlidCreator
和Monotonic ULIDs
如何使用ulid-creator从Monotonic ULID生成 RFC-4122 UUID :
// create a Monotonic ULID and convert it to RFC-4122 UUID v4
UUID uuid = UlidCreator.getMonotonicUlid().toRfc4122().toUuid();
查看使用 4 个线程的基准测试:
-----------------------------------------------------------------------
Benchmark Mode Cnt Score Error Units
-----------------------------------------------------------------------
UUID.randomUUID() thrpt 5 1423,060 ± 30,125 ops/ms
UlidCreator.getMonotonicUlid() thrpt 5 7391,917 ± 871,799 ops/ms
-----------------------------------------------------------------------
编辑:添加了使用单调 ULID 的第四个示例。
推荐阅读
- java - 如何在java中读取属性文件,该文件在键值对中具有值
- java - 如何在Android中添加向firebase添加数据的进度对话框?
- javascript - 使用输入的数据属性为php post生成多维数组
- ansible - Ansible - pip not found
- css - How to set border radius for color tag's color picker?
- python - Python equivalence for this c++ program
- javascript - Spinner 不在 onEndReachedThreshold Flatlist 中工作
- laravel - laravel 创建多个会话
- javascript - Jquery Button functionality not working when using onload event
- javascript - 在 IE 和 Edge 中发出的 XHR 请求是否存在已取消状态?