java - 无法在回退方法中从父线程访问 InheritableThreadLocal 对象
问题描述
我有InheritableThreadLocal<ConcurrentHashMap<String, Object>>
一个线程在请求通过过滤器到达时进行初始化并在其中设置一些 transaction_id。
现在在服务层,我通过 CompletableFuture 调用了 10 个不同的 API 调用。所有 API 服务类都有一个execute
使用 RestTempate 进行 API 调用的方法。我把@HystrixCommand
方法execute
。
execute 方法是 void 类型,但它将 API 响应放在 InheritableThreadLocal 对象中。
问题是当 API 调用失败 Hystrix 调用 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
在 hystrix 后备方法中变为 null
进一步添加到此(您可以验证这一点)
InheritableThreadLocal
仅在超时的情况下而不是在任何其他异常的情况下在 hystrix 回退方法中变为 null
我会推荐其他人参考我的博客。Hystrix在超时的情况下回退,发生在 hystrix-timer 线程中。
Hystrix 后备执行线程
您可以通过记录来验证这一点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);
}
}
希望这可以帮助
推荐阅读
- ssl - 当 TLS 和 SSL 不同并且 TLS 与 SSL 相比是新的时,为什么 IIS 不显示启用 tls 的特定设置
- angular - 如何在 li 中设置默认值并从角度 6 中的单击事件更新 li
- ibm-cloud - 部署 ASP.net 4.5 IBM 云
- python - 为什么 Python 的子进程调用不能在 Windows 上与 sh 一起正常工作?
- django - 芹菜节拍处理的消息未从就绪状态中删除
- mongodb - 如何将参数传递给 Mongodb find() 函数调用?
- jquery - sociallocker 不是一个功能
- unity3d - “gradle 无法获取依赖项”与 Admob 统一集成
- python - 使用分段函数的复杂曲线拟合
- android - 如何从特定行和列的 SQLite 数据库中检索单个数据?