Merge remote-tracking branch 'origin/3.1.x'

This commit is contained in:
Olga Maciaszek-Sharma
2022-10-19 12:30:23 +02:00
2 changed files with 46 additions and 5 deletions

View File

@@ -36,6 +36,14 @@ import org.springframework.web.context.request.RequestContextHolder;
import static feign.Util.checkNotNull;
/**
* @author Grzejszczak
* @author Sharma
* @author Niang
* @author Bohutskyi
* @author kim
* @author Vicasong
*/
class FeignCircuitBreakerInvocationHandler implements InvocationHandler {
private final CircuitBreakerFactory factory;
@@ -122,9 +130,13 @@ class FeignCircuitBreakerInvocationHandler implements InvocationHandler {
private Supplier<Object> asSupplier(final Method method, final Object[] args) {
final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
final Thread caller = Thread.currentThread();
return () -> {
boolean isAsync = caller != Thread.currentThread();
try {
RequestContextHolder.setRequestAttributes(requestAttributes);
if (isAsync) {
RequestContextHolder.setRequestAttributes(requestAttributes);
}
return dispatch.get(method).invoke(args);
}
catch (RuntimeException throwable) {
@@ -133,6 +145,11 @@ class FeignCircuitBreakerInvocationHandler implements InvocationHandler {
catch (Throwable throwable) {
throw new RuntimeException(throwable);
}
finally {
if (isAsync) {
RequestContextHolder.resetRequestAttributes();
}
}
};
}

View File

@@ -19,6 +19,9 @@ package org.springframework.cloud.openfeign.circuitbreaker;
import java.time.Duration;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.function.Function;
import feign.RequestInterceptor;
@@ -27,6 +30,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
@@ -40,6 +44,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.http.HttpHeaders;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
@@ -47,6 +52,7 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
@@ -58,14 +64,18 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
*
* @author John Niang
*/
@SpringBootTest(classes = AsyncCircuitBreakerTest.Application.class, webEnvironment = RANDOM_PORT,
@SpringBootTest(classes = AsyncCircuitBreakerTests.Application.class, webEnvironment = RANDOM_PORT,
properties = "spring.cloud.openfeign.circuitbreaker.enabled=true")
@AutoConfigureMockMvc
class AsyncCircuitBreakerTest {
class AsyncCircuitBreakerTests {
@Autowired
MockMvc mvc;
@Autowired
@Qualifier("asyncWorker")
ExecutorService asyncCircuitBreakerExecutor;
@Test
void shouldWorkNormally() throws Exception {
mvc.perform(get("/hello/proxy")).andDo(print()).andExpect(status().isOk())
@@ -85,14 +95,28 @@ class AsyncCircuitBreakerTest {
authorization)).andDo(print()).andExpect(status().isOk()).andExpect(content().string(authorization));
}
@Test
void shouldProxyHeaderWhenHeaderSetAndCleanRequestAttributesAfterReturn() throws Exception {
shouldNotProxyAnyHeadersWithoutHeaderSet();
Future<ServletRequestAttributes> future = asyncCircuitBreakerExecutor
.submit(() -> (ServletRequestAttributes) RequestContextHolder.getRequestAttributes());
assertThat(future.get()).as("the RequestAttributes has been cleared").isNull();
}
@EnableAutoConfiguration
@Configuration(proxyBeanMethods = false)
@EnableFeignClients(clients = { TestClient.class })
@Import({ NoSecurityConfiguration.class, TestController.class })
static class Application {
@Bean(name = "asyncWorker", destroyMethod = "shutdown")
ExecutorService asyncCircuitBreakerExecutor() {
return Executors.newSingleThreadExecutor(new CustomizableThreadFactory("async"));
}
@Bean
CircuitBreakerFactory<Duration, ConfigBuilder<Duration>> circuitBreakerFactory() {
CircuitBreakerFactory<Duration, ConfigBuilder<Duration>> circuitBreakerFactory(
@Qualifier("asyncWorker") ExecutorService asyncCircuitBreakerExecutor) {
return new CircuitBreakerFactory<Duration, ConfigBuilder<Duration>>() {
Function<String, Duration> defaultConfiguration = id -> Duration.ofMillis(1000);
@@ -100,7 +124,7 @@ class AsyncCircuitBreakerTest {
@Override
public CircuitBreaker create(String id) {
Duration timeout = super.getConfigurations().computeIfAbsent(id, defaultConfiguration);
return new AsyncCircuitBreaker(timeout);
return new AsyncCircuitBreaker(timeout, asyncCircuitBreakerExecutor);
}
@Override