我们的APP生产上出了一次比较严重的事故,许多用户投诉登录后能看到别人的信息,收到投诉后我们就开始查找问题,一般这样的问题都是线程安全引起的,所以查找原因的思路也是按线程安全的思路去查。

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

登录交易大致流程如下:

//一系列验证
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里放一次。

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

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

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

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

结果果然有问题,大部分情况new_session都是null,但也出现了不是null的情况,这时候发现session不是自己的。



HttpServletRequest是有生命周期的,当一个http请求过来后,应用服务器解析报文,把各种参数放到一个HttpServletRequest对象中,然后传递给Servlet的service函数,service函数根据里面的方法调用对应的doGet/doPost等方法,而一旦service函数调用结束,HttpServletRequest的生命周期就结束了,再这之后你继续使用这个对象,产生的结果是不确定的。



网上遇到这类问题的人不多,我专门找了servlet specification,其中有一章讲HttpServletRequest生命周期的。



从中可以得到如下信息:

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

(2)在三种情况之外,使用request会产生不确定的结果(indeterminate results)

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

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

用法示例:

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

我又看了tomcat的源码,发现它确实对Request做了复用:

虽然问题的原因很简单,但是产生的后果十分严重。需要异步处理数据的时候一定要特别小心,此处如果传Session就没问题了,但是还是要尽量避免。

异步处理ServletRequest引发的血案的更多相关文章

  1. [WCF]缺少一行代码引发的血案

    这是今天作项目支持的发现的一个关于WCF的问题,虽然最终我只是添加了一行代码就解决了这个问题,但是整个纠错过程是痛苦的,甚至最终发现这个问题都具有偶然性.具体来说,这是一个关于如何自动为服务接口(契约 ...

  2. dubbox微服务实例及引发的“血案”

    Dubbo 是阿里巴巴公司开源的一个高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成. 主要核心部件: Remoting: 网络通信框架 ...

  3. Integer.parseInt 引发的血案

    Integer.parseInt 处理一个空字符串, 结果出错了, 程序没有注意到,搞了很久, 引发了血案啊!! 最后,终于 观察到了, 最后的部分: Caused by: java.lang.NoC ...

  4. Replication的犄角旮旯(六)-- 一个DDL引发的血案(上)(如何近似估算DDL操作进度)

    <Replication的犄角旮旯>系列导读 Replication的犄角旮旯(一)--变更订阅端表名的应用场景 Replication的犄角旮旯(二)--寻找订阅端丢失的记录 Repli ...

  5. Replication的犄角旮旯(七)-- 一个DDL引发的血案(下)(聊聊logreader的延迟)

    <Replication的犄角旮旯>系列导读 Replication的犄角旮旯(一)--变更订阅端表名的应用场景 Replication的犄角旮旯(二)--寻找订阅端丢失的记录 Repli ...

  6. 转:一个Sqrt函数引发的血案

    转自:http://www.cnblogs.com/pkuoliver/archive/2010/10/06/1844725.html 源码下载地址:http://diducoder.com/sotr ...

  7. 一个Sqrt函数引发的血案(转)

    作者: 码农1946  来源: 博客园  发布时间: 2013-10-09 11:37  阅读: 4556 次  推荐: 41   原文链接   [收藏]   好吧,我承认我标题党了,不过既然你来了, ...

  8. 一个字母引发的血案 java.io.File中mkdir()和mkdirs()

    一个字母引发的血案 明天开始放年假了,临放假前有个爬虫的任务,其中需要把网络图片保存到本地,很简单,马上写完了代码: //省略部分代码... Long fileId= (Long) data.get( ...

  9. form表单提交引发的血案

    最近,公司某条产品线上的一个功能出了问题:点击查询的时候,该页面在IE上直接卡死,chrome上会卡顿一段时间候提交表单进行查询.拿到这个bug单子以后,简单重现了下,基本上定位到是查询操作中的问题, ...

随机推荐

  1. lightoj 1060 - nth Permutation(组合数+贪心)

    题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1060 题解:如果是不重复数的这些操作可以用康托展开的逆来求,如果是有重复数字出 ...

  2. HDFS 读写流程-英

    HDFS 文件读取流程 The client opens the file it wishes to read by calling open() on the FileSystem object, ...

  3. CVE-2014-6271 Shellshock 破壳漏洞 复现

    补坑. 什么是shellshock ShellShock是一个BashShell漏洞(据说不仅仅是Bash,其他shell也可能有这个漏洞). 一般情况来说,系统里面的Shell是有严格的权限控制的, ...

  4. Zabbix4.2对IIS监控摸索记录

    Zabbix是很强大,但是相关的细节技术文档貌似很少,摸索之路就显得异常难. 度娘搜了下,关于Zabbix对IIS的监控资料确实有,确实也讲如何操作了,但是细细按照对方的要求操作下,总是缺数据,no ...

  5. Python作业本——第3章 函数

    今天看完了第三章,习题都是一些概念性的问题,就不一一解答了. 实践项目是创建一个Collatz序列,并且加上验证输入是不是一个整数. def collatz(number): if number % ...

  6. angular关于Bootstrap样式不起作用问题

    跟着慕课网的课程学习Angular,简直要被bootstrap的问题整死了,样式一直出不来,导航完全没有背景颜色.. 我在网上找了很多都试了,以下方法特别受用 1.把 "../node_mo ...

  7. 神奇的 SQL 之层级 → 为什么 GROUP BY 之后不能直接引用原表中的列

    前言 开心一刻 感觉不妙呀,弟弟舔它! 不该舔的,舔到怀疑人生了...... GROUP BY 后 SELECT 列的限制 标准 SQL 规定,在对表进行聚合查询的时候,只能在 SELECT 子句中写 ...

  8. 微软发布.Net Core 3.0 RC1,最终版本定于9月23日

    2019.9.17 微软 宣布推出.NET Core 3.0 Release Candidate 1.就像Preview 9一样,主要专注于为 .NET Core 3.0 发布最终版本 .现在变得非常 ...

  9. 是时候让大家看看你用django写出来的博客了(内含部署教程视频)

    作者:HelloGitHub-追梦人物 文中涉及的示例代码,已同步更新到 HelloGitHub-Team 仓库 博客的基础功能已经开发的差不多了,虽然还有很多地方可以完善,但我们还是希望早点把博客部 ...

  10. logback配置方法

    logback是一个通用可靠.快速灵活的日志框架,它替代了log4j,和slf4j组成新的日志系统. slf4j是一个日志门面,为其他各种日志框架提供了统一的接口,代码中使用slf4j可以避免对某一种 ...