我InheritableThreadLocal<ConcurrentHashMap<String, Object>>
,当请求通过过滤器,并在其中设置一些TRANSACTION_ID初始化线程。
现在,在服务层,我打电话通过CompletableFuture 10所不同的API调用。 所有API服务类有一个execute
正在使用RestTempate作出API调用方法。 我把@HystrixCommand
上execute
方法。
执行方法是无效的类型,但它放入的InheritableThreadLocal对象的API响应。
问题是,当一个API调用失败猬打电话FallBackMethod,当我提出的InheritableThreadLocal错误响应,我不能够发送错误响应到客户端。
ThreadLocalUtil.class
public class ThreadLocalUtil {
private static InheritableThreadLocal<ConcurrentHashMap<String, Object>> transmittableThreadLocal = new InheritableThreadLocal<>();
public static void addDataToThreadLocalMap(String key, Object value) {
Map<String, Object> existingDataMap = transmittableThreadLocal.get();
if (value != null) {
existingDataMap.put(key, value);
}
}
public static Object getDataFromThreadLocalMap(String key) {
Map<String, Object> existingDataMap = transmittableThreadLocal.get();
return existingDataMap.get(key);
}
public static void clearThreadLocalDataMap() {
if (transmittableThreadLocal != null)
transmittableThreadLocal.remove();
}
public static Object getRequestData(String key) {
Map<String, Object> existingDataMap = transmittableThreadLocal.get();
if (existingDataMap != null) {
return existingDataMap.get(key);
}
return "-1";
}
public static void initThreadLocals() {
ConcurrentHashMap<String, Object> dataForDataMap = new ConcurrentHashMap<String, Object>();
String requestId = "REQUEST_ID_" + System.currentTimeMillis();
dataForDataMap.put("REQUEST_ID", requestId);
transmittableThreadLocal.set(dataForDataMap);
}
}
CommonFilter.class
@Component
@Order(1)
public class CommonFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
try {
ThreadLocalUtil.initThreadLocals();
filterChain.doFilter(request, response);
} catch (Exception e) {
if (e instanceof ServletException) {
throw (ServletException) e;
}
} finally {
ThreadLocalUtil.clearThreadLocalDataMap();
}
}
EmployeeService.class
@Component
public abstract class EmployeeService {
@Autowired
private ThreadLocalUtil threadLocalUtil;
public abstract void getEmployee(int employeeId);
public void fallbackMethod(int employeeid) {
threadLocalUtil.addDataToThreadLocalMap("ErrorResponse", "Fallback response:: No employee details available temporarily");
}
}
EmployeeServiceImpl.class
@Service
public class EmployeeServiceImpl extends EmployeeService {
@HystrixCommand(fallbackMethod = "fallbackMethod", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "900"),
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "10") })
public void getEmployee(int employeeId) {
System.out.println("Getting Employee details for " + employeeId + ", threadLocalUtil : " + threadLocalUtil.getDataFromThreadLocalMap("EMPLOYE_ID"));
String response = restTemplate.exchange("http://localhost:8011/findEmployeeDetails/{employeeid}",
HttpMethod.GET, null, new ParameterizedTypeReference<String>() {
}, employeeId).getBody();
threadLocalUtil.addDataToThreadLocalMap("Response", response);
}
@Autowired
RestTemplate restTemplate;
@Autowired
private ThreadLocalUtil threadLocalUtil;
}
所以,首先因为内部的Hystrix
使用ThreadPoolExecutor
(线程创建一次,重复使用),所以它是错误使用InheritableThreadLocal
。
从上面的问题,你在我的询问博客 ,我明白你的问题是
InheritableThreadLocal
成为锥备用方法无效
这进一步增加了这一点(您可以验证这一点)
InheritableThreadLocal
成为锥备用方法无效仅在超时的情况下,而不是在任何其他异常的情况下,
我会建议别人提及我博客 。 猬回退在超时情况下,发生在椎定时器线程。 猬后退执行线程您可以通过登录验证这一点Thread.currentThread().getName()
由于母hystrix-timer thread
是不是你调用线程,所以你transmittableThreadLocal.get()变为零。
为了解决这个问题,我会建议使用HystrixCommandExecutionHook和HystrixRequestVariableDefault 。 使用此您可以实现钩喜欢onStart, onExecutionStart, onFallbackStart
等,其中你需要获取/设置ThreadLocal的变量。 欲了解更多详情您可以参考在博客的最后一节。
更新:为了您的使用情况,您可以修改如下代码:
ThreadLocalUtil.java
public class ThreadLocalUtil {
private static ThreadLocal<ConcurrentHashMap<String, Object>> transmittableThreadLocal = new ThreadLocal<>();
public static ConcurrentHashMap<String, Object> getThreadLocalData() {
return transmittableThreadLocal.get();
}
public static void setThreadLocalData(ConcurrentHashMap<String, Object> data) {
transmittableThreadLocal.set(data);
}
public static void addDataToThreadLocalMap(String key, Object value) {
Map<String, Object> existingDataMap = transmittableThreadLocal.get();
if (value != null) {
existingDataMap.put(key, value);
}
}
public static Object getDataFromThreadLocalMap(String key) {
Map<String, Object> existingDataMap = transmittableThreadLocal.get();
return existingDataMap.get(key);
}
public static void clearThreadLocalDataMap() {
if (transmittableThreadLocal != null)
transmittableThreadLocal.remove();
}
public static Object getRequestData(String key) {
Map<String, Object> existingDataMap = transmittableThreadLocal.get();
if (existingDataMap != null) {
return existingDataMap.get(key);
}
return "-1";
}
public static void initThreadLocals() {
transmittableThreadLocal.set(new ConcurrentHashMap<>());
String requestId = "REQUEST_ID_" + System.currentTimeMillis();
addDataToThreadLocalMap("REQUEST_ID", requestId);
}
}
EmployeeService.java
@Component
public abstract class EmployeeService {
public abstract void getEmployee(int employeeId);
public void fallbackMethod(int employeeid) {
threadLocalUtil.addDataToThreadLocalMap("ErrorResponse", "Fallback response:: No employee details available temporarily");
}
}
EmployeeServiceImpl.java
@Service
public class EmployeeServiceImpl extends EmployeeService {
@HystrixCommand(fallbackMethod = "fallbackMethod", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "900"),
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "10") })
public void getEmployee(int employeeId) {
System.out.println("Getting Employee details for " + employeeId + ", threadLocalUtil : " + threadLocalUtil.getDataFromThreadLocalMap("EMPLOYEE_ID"));
String response = restTemplate.exchange("http://localhost:8011/findEmployeeDetails/{employeeid}",
HttpMethod.GET, null, new ParameterizedTypeReference<String>() {
}, employeeId).getBody();
threadLocalUtil.addDataToThreadLocalMap("Response", response);
}
@Autowired
RestTemplate restTemplate;
}
HystrixHook.java
public class HystrixHook extends HystrixCommandExecutionHook {
private HystrixRequestVariableDefault<ConcurrentHashMap<String, Object>> hrv = new HystrixRequestVariableDefault<>();
@Override
public <T> void onStart(HystrixInvokable<T> commandInstance) {
HystrixRequestContext.initializeContext();
getThreadLocals();
}
@Override
public <T> void onExecutionStart(HystrixInvokable<T> commandInstance) {
setThreadLocals();
}
@Override
public <T> void onFallbackStart(HystrixInvokable<T> commandInstance) {
setThreadLocals();
}
@Override
public <T> void onSuccess(HystrixInvokable<T> commandInstance) {
HystrixRequestContext.getContextForCurrentThread().shutdown();
super.onSuccess(commandInstance);
}
@Override
public <T> Exception onError(HystrixInvokable<T> commandInstance, HystrixRuntimeException.FailureType failureType, Exception e) {
HystrixRequestContext.getContextForCurrentThread().shutdown();
return super.onError(commandInstance, failureType, e);
}
private void getThreadLocals() {
hrv.set(ThreadLocalUtil.getThreadLocalData());
}
private void setThreadLocals() {
ThreadLocalUtil.setThreadLocalData(hrv.get());
}
}
AbcApplication.java
public class AbcApplication {
public static void main(String[] args) {
HystrixPlugins.getInstance().registerCommandExecutionHook(new HystrixHook());
SpringApplication.run(Abc.class, args);
}
}
希望这可以帮助
文章来源: Not able to access InheritableThreadLocal object from parent thread in fall back method