在不检查异常的情况下查找我们是否在线程绑定请求中的安全方法
Safe way to find if we are within a thread bound request without checking for an Exception
问题
所以我们有一个请求拦截器 (Feign),它检查 autowired
HttpServletRequest
是否有 headers,然后 propagates/copies 这些到传出请求。我们的拦截器的工作是将 headers 从一个微服务传播到另一个微服务,这样即使图中的最后一个 micro-service 也有关于谁发起请求的信息(例如租户)。
有时我们调用 feign 作为 HTTP 请求线程的结果,有时我们在 start-up 或调度线程中调用它。
在调度线程的情况下,我们希望能够检测请求是否存在而无需执行 try/catch。这是我们是发起方,不需要复制任何东西的情况。
我原以为下面的方法会起作用,但我们得到一个抛出异常的代理 object:
以下检查失败,因为 this.request 不为空:
this.request!=null && this.request.getHeader("X-Application")
出现以下错误:
No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
我理解错误。我想避免做明显的解决方法,比如:
当前的解决方法 - 笨拙且糟糕
//TODO: Review this
boolean requestExists = true;
try{
request.getHeader(APPLICATION_HEADER);
}catch (IllegalStateException e ){
requestExists = false;
}
导致问题的当前代码
public class ServiceNameFeignInterceptor implements RequestInterceptor {
private static final Logger log = LoggerFactory.getLogger(ServiceNameFeignInterceptor.class);
final TenantIdResolver tenantResolver;
final ApplicationNameResolver appResolver;
private final String APPLICATION_HEADER = "X-Application";
private final String TENANT_ID = "X-Tenant-Id";
...
@Autowired
HttpServletRequest request;
public void apply(RequestTemplate requestTemplate) {
...
if (this.request!=null && this.request.getHeader("X-Application") != null) {
log.info("Application header found in the request !!!");
requestTemplate.header("X-Application", new String[]{this.request.getHeader("X-Application")});
requestTemplate.header("X-Tenant-Id", new String[]{this.request.getHeader("X-Tenant-Id")});
} else {
log.info("Setting {} as {} for URL {} ", new Object[]{"X-Application", appName, requestTemplate.url()});
requestTemplate.header("X-Application", new String[]{appName});
requestTemplate.header("X-Tenant-Id", new String[]{appName});
}
}
当前选项
请指正以下几点或提出更好的方案。
我目前有三个选择:
使用try/catch解决方案(最不受欢迎)
检查线程局部变量是否存在请求
传递我们自己的附加线程局部变量,这将是一个标志(我们不在请求上下文中)。
问题
我不喜欢 1,因为捕获异常非常昂贵,而且它们可能会掩盖任何真正的错误。
我不喜欢 2,因为如果 spring 实现发生变化,实现细节可能会发生变化(例如密钥),并且我们在 starter 中的实现会中断。但无论如何,每当升级 spring 启动时,各种次要或主要的事情都需要修复。
我喜欢选项 3,因为在调用我们的假客户端之前设置标志是一种有意识的行为。因此不存在错误 un-noticed.
的风险
意见、选项、解决方案?
更新
一位团队成员建议我们使用:new NamedThreadLocal("Request attributes");
他们建议这样做是因为在以下位置实施:
所以我们会使用类似的东西:
ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal("Request attributes");
RequestAttributes attributes = (RequestAttributes)requestAttributesHolder.get();
boolean requestExists = attributes != null;
但这完全取决于 spring 的内部结构,并且他们继续使用 "Request attributes"。
我有类似的问题,我使用RequestContextHolder
检查请求是否绑定到线程。根据 doc,getRequestAttributes
returns null if no RequestAttributes bound to the thread.
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
if (attrs == null) {
return;
}
问题
所以我们有一个请求拦截器 (Feign),它检查 autowired
HttpServletRequest
是否有 headers,然后 propagates/copies 这些到传出请求。我们的拦截器的工作是将 headers 从一个微服务传播到另一个微服务,这样即使图中的最后一个 micro-service 也有关于谁发起请求的信息(例如租户)。
有时我们调用 feign 作为 HTTP 请求线程的结果,有时我们在 start-up 或调度线程中调用它。
在调度线程的情况下,我们希望能够检测请求是否存在而无需执行 try/catch。这是我们是发起方,不需要复制任何东西的情况。
我原以为下面的方法会起作用,但我们得到一个抛出异常的代理 object:
以下检查失败,因为 this.request 不为空:
this.request!=null && this.request.getHeader("X-Application")
出现以下错误:
No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
我理解错误。我想避免做明显的解决方法,比如:
当前的解决方法 - 笨拙且糟糕
//TODO: Review this
boolean requestExists = true;
try{
request.getHeader(APPLICATION_HEADER);
}catch (IllegalStateException e ){
requestExists = false;
}
导致问题的当前代码
public class ServiceNameFeignInterceptor implements RequestInterceptor {
private static final Logger log = LoggerFactory.getLogger(ServiceNameFeignInterceptor.class);
final TenantIdResolver tenantResolver;
final ApplicationNameResolver appResolver;
private final String APPLICATION_HEADER = "X-Application";
private final String TENANT_ID = "X-Tenant-Id";
...
@Autowired
HttpServletRequest request;
public void apply(RequestTemplate requestTemplate) {
...
if (this.request!=null && this.request.getHeader("X-Application") != null) {
log.info("Application header found in the request !!!");
requestTemplate.header("X-Application", new String[]{this.request.getHeader("X-Application")});
requestTemplate.header("X-Tenant-Id", new String[]{this.request.getHeader("X-Tenant-Id")});
} else {
log.info("Setting {} as {} for URL {} ", new Object[]{"X-Application", appName, requestTemplate.url()});
requestTemplate.header("X-Application", new String[]{appName});
requestTemplate.header("X-Tenant-Id", new String[]{appName});
}
}
当前选项
请指正以下几点或提出更好的方案。
我目前有三个选择:
使用try/catch解决方案(最不受欢迎)
检查线程局部变量是否存在请求
传递我们自己的附加线程局部变量,这将是一个标志(我们不在请求上下文中)。
问题
我不喜欢 1,因为捕获异常非常昂贵,而且它们可能会掩盖任何真正的错误。
我不喜欢 2,因为如果 spring 实现发生变化,实现细节可能会发生变化(例如密钥),并且我们在 starter 中的实现会中断。但无论如何,每当升级 spring 启动时,各种次要或主要的事情都需要修复。
我喜欢选项 3,因为在调用我们的假客户端之前设置标志是一种有意识的行为。因此不存在错误 un-noticed.
的风险意见、选项、解决方案?
更新
一位团队成员建议我们使用:new NamedThreadLocal("Request attributes");
他们建议这样做是因为在以下位置实施:
所以我们会使用类似的东西:
ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal("Request attributes");
RequestAttributes attributes = (RequestAttributes)requestAttributesHolder.get();
boolean requestExists = attributes != null;
但这完全取决于 spring 的内部结构,并且他们继续使用 "Request attributes"。
我有类似的问题,我使用RequestContextHolder
检查请求是否绑定到线程。根据 doc,getRequestAttributes
returns null if no RequestAttributes bound to the thread.
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
if (attrs == null) {
return;
}