Wrap CacheKeyGenerator MessageDigest in ThreadLocal (#2829)
Co-authored-by: Simone Gerevini <sgerevini@atrify.com>
This commit is contained in:
@@ -31,6 +31,7 @@ import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
/**
|
||||
* @author Marta Medio
|
||||
* @author Ignacio Lozano
|
||||
* @author Simone Gerevini
|
||||
*/
|
||||
public class CacheKeyGenerator {
|
||||
|
||||
@@ -38,19 +39,21 @@ public class CacheKeyGenerator {
|
||||
|
||||
private static final byte[] KEY_SEPARATOR_BYTES = KEY_SEPARATOR.getBytes();
|
||||
|
||||
private final MessageDigest messageDigest;
|
||||
private final ThreadLocal<MessageDigest> messageDigest;
|
||||
|
||||
/* for testing */ static final List<KeyValueGenerator> DEFAULT_KEY_VALUE_GENERATORS = List.of(
|
||||
new UriKeyValueGenerator(), new HeaderKeyValueGenerator(HttpHeaders.AUTHORIZATION, KEY_SEPARATOR),
|
||||
new CookiesKeyValueGenerator(KEY_SEPARATOR));
|
||||
|
||||
public CacheKeyGenerator() {
|
||||
try {
|
||||
messageDigest = MessageDigest.getInstance("MD5");
|
||||
}
|
||||
catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("Error creating CacheKeyGenerator", e);
|
||||
}
|
||||
messageDigest = ThreadLocal.withInitial(() -> {
|
||||
try {
|
||||
return MessageDigest.getInstance("MD5");
|
||||
}
|
||||
catch (NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException("Error creating CacheKeyGenerator", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public String generateMetadataKey(ServerHttpRequest request, String... varyHeaders) {
|
||||
@@ -63,7 +66,7 @@ public class CacheKeyGenerator {
|
||||
|
||||
public String generateKey(ServerHttpRequest request, List<String> varyHeaders) {
|
||||
byte[] rawKey = generateRawKey(request, varyHeaders);
|
||||
byte[] digest = messageDigest.digest(rawKey);
|
||||
byte[] digest = messageDigest.get().digest(rawKey);
|
||||
|
||||
return Base64.getEncoder().encodeToString(digest);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,11 @@
|
||||
|
||||
package org.springframework.cloud.gateway.filter.factory.cache;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
@@ -30,6 +34,7 @@ import static org.springframework.http.HttpHeaders.AUTHORIZATION;
|
||||
|
||||
/**
|
||||
* @author Ignacio Lozano
|
||||
* @author Simone Gerevini
|
||||
*/
|
||||
class CacheKeyGeneratorTest {
|
||||
|
||||
@@ -201,4 +206,49 @@ class CacheKeyGeneratorTest {
|
||||
assertThat(keyWithoutVary).isEqualTo(keyWithFirstVary);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotFailWhenRunningInParallel() throws InterruptedException {
|
||||
MockServerHttpRequest request = MockServerHttpRequest.get("http://this").build();
|
||||
int numberOfThreads = 100;
|
||||
|
||||
List<Exception> exceptions = executeInParallel(Executors.newFixedThreadPool(numberOfThreads), numberOfThreads,
|
||||
() -> cacheKeyGenerator.generateKey(request));
|
||||
|
||||
assertThat(exceptions.size()).isEqualTo(0);
|
||||
}
|
||||
|
||||
private List<Exception> executeInParallel(Executor executor, int nThreads, Runnable action)
|
||||
throws InterruptedException {
|
||||
CountDownLatch ready = new CountDownLatch(nThreads);
|
||||
CountDownLatch start = new CountDownLatch(1);
|
||||
CountDownLatch done = new CountDownLatch(nThreads);
|
||||
List<Exception> exceptions = new ArrayList<>(nThreads);
|
||||
|
||||
for (int i = 0; i < nThreads; i++) {
|
||||
executor.execute(() -> {
|
||||
ready.countDown();
|
||||
try {
|
||||
start.await();
|
||||
action.run();
|
||||
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
catch (RuntimeException e) {
|
||||
exceptions.add(e);
|
||||
}
|
||||
finally {
|
||||
done.countDown();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ready.await();
|
||||
start.countDown();
|
||||
done.await();
|
||||
|
||||
return exceptions;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user