异步处理ServletRequest引起的血案

咱们的APP生产上出了一次比较严重的事故,许多用户投诉登陆后能看到别人的信息,收到投诉后咱们就开始查找问题,通常这样的问题都是线程安全引发的,因此查找缘由的思路也是按线程安全的思路去查。java

业务场景是这样的,用户登陆后,点击一个页面查看信息,这个信息显示了别人的信息。tomcat

登陆交易大体流程以下:安全

//一系列验证
session.setAttribute("id",id); //证件号放入session
//其余操做

查看信息交易的流程以下:服务器

session = request.getSession();
if(session != null && session.getAttribute("LoginStatus") == True) {
    String id = session.getAttribute("id");
    Information info = queryInfo(id);
    return info;
} else {
    return "not login";
}

经过加日志等方法,咱们确认了是查看信息的时候,从session里拿出来的证件号是其余人的,可是究竟是在何时变化的,没找到,由于咱们一直顺着线程安全的思路,找全局变量这样的地方。session

另外还发现有个地方可疑,就是有一个异步的线程,会验证用户证件号,而且从新在session里放一次。curl

public void updateId(HttpServletRequest request) {
    HttpSession session = request.getSession();
    String id = validate();
    session.setAttribute("id",id);
}

这个函数是在登陆主交易调起的线程池处理的,看上去其实没有多大毛病,并且也是老的代码,好久了。并且我发现了一个规律,被别人看到信息的用户,登陆交易都触发了使用新设备登陆,由于咱们加了一个逻辑,对换设备登陆作了验证,这样的话须要验证短信,登陆分两步了,第一步比较快的返回了,可是异步的更新信息流程还在。异步

异步处理ServletRequest引起的血案

因而我怀疑是否是由于Servlet的交易已经返回了,异步的线程虽然拿到了HttpServletRequest,可是这个request已经无效了或者被复用了。ide

我写了下面的代码进行验证,不停的用curl调用。Http请求很快就返回了,可是我会把HttpServletRequest传给一个线程池,等5s后才会去处理这个Request,结果果真是有问题的函数

异步处理ServletRequest引起的血案

结果果真有问题,大部分状况new_session都是null,但也出现了不是null的状况,这时候发现session不是本身的。
异步处理ServletRequest引起的血案
HttpServletRequest是有生命周期的,当一个http请求过来后,应用服务器解析报文,把各类参数放到一个HttpServletRequest对象中,而后传递给Servlet的service函数,service函数根据里面的方法调用对应的doGet/doPost等方法,而一旦service函数调用结束,HttpServletRequest的生命周期就结束了,再这以后你继续使用这个对象,产生的结果是不肯定的。
异步处理ServletRequest引起的血案
网上遇到这类问题的人很少,我专门找了servlet specification,其中有一章讲HttpServletRequest生命周期的。
异步处理ServletRequest引起的血案
从中能够获得以下信息:性能

(1)三种状况下request有效:service函数内,doFilter函数内,startAsync起的异步线程

(2)在三种状况以外,使用request会产生不肯定的结果(indeterminate results)

(3)大部分容器在实现servlet的时候,为了提升性能,会复用request对象,但这不是规范里必须的

其中提到的startAsync是servlet 3.0开始有的,它是为了让一个工做线程能够在作IO或相似阻塞线程的操做的时候能干其它的事情,可是它要求异步线程都结束了,才会将请求返回给客户端,本质上仍是同步的,只是并行了。因此要想异步的处理Request,必须使用servlet本身的异步机制,可是这样并不能知足咱们的需求,由于咱们就是为了避免让主线程等待。

用法示例:
异步处理ServletRequest引起的血案

若是使用了这个,那么客户端须要等待5s才能拿到结果。

我又看了tomcat的源码,发现它确实对Request作了复用:
异步处理ServletRequest引起的血案

虽然问题的缘由很简单,可是产生的后果十分严重。须要异步处理数据的时候必定要特别当心,此处若是传Session就没问题了,可是仍是要尽可能避免。

相关文章
相关标签/搜索