Spring MVC 与线程

最近又被一个连环坑坑惨了,我踩的坑是与 Spring MVC 的线程模型有关的。

我们的系统现在有两个 war 包,分别部署在不同的 JBoss 上,第一个是互联网可访问的,叫 front 吧,第二个是在防火墙之后的,只能通过 front 来访问,叫 backend,是可以访问数据库的。

front 通过 Hessian 调用 backend 的服务。

现在有个需求要求 backend 记录互联网用户的 IP 和其他一些参数,接口太多,不可能每个方法再添加参数,由于 Hessian 也运行是在 HTTP 上的,所以我想在 Hessian 的 HTTP 请求头里把这些需要的参数传过去。

根据 debug,Hessian 采用的是 JDK 的 HessianURLConnectionFactory 来创建连接,处理 HTTP 请求,所以我就扩展了它的方法 public HessianConnection open(URL url) throws IOException ,在里面把参数放到请求头里。由于要取的是客户端实际 IP 等信息,所以还需要获取客户端的请求 HttpServletRequest ,刚好 Spring MVC 里面有个 RequestContextHolder 工具,持有了 HttpServletRequest 。然后就有了下面这行代码:HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); 。坑就挖成了。

点开 RequestContextHolder.getRequestAttributes() 方法来看看:

public static RequestAttributes getRequestAttributes() {
     RequestAttributes attributes = requestAttributesHolder.get();
     if (attributes == null) {
          attributes = inheritableRequestAttributesHolder.get();
     }
     return attributes;
}

requestAttributesHolder 是这样的:

private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
          new NamedThreadLocal<RequestAttributes>("Request attributes");

NamedThreadLocal 是这样定义的: public class NamedThreadLocal<T> extends ThreadLocal<T> {
也就是说这个请求是跟线程绑定的,在别的线程是获取不到的。

其他的坑是这样的:
在 front 有个地方用了异步去调用 backend,其实是根本没必要用异步的(因为当前线程发出请求后是在等待响应的);而这个异步调用在测试环境时没有启用,被开关切去走另一个同步的分支,而生产环境确是必须走这个异步分支的。


欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。