千万不要把Request传递到异步线程里面,有坑

前几天在网上冲浪的时候看到一篇技术文章,讲的是他把一个 request 请求传递到了线程池里面,然后遇到了一个匪夷所思的情况。

他写了这篇文章,把自己针对这个问题的探索过程分享了出来:

《springboot 中如何正确的在异步线程中使用request》
https://www.cnblogs.com/mysgk/p/16470336.html

文章还是挺不错的,把发现问题和解决问题都写的很明白了。

但是,我觉得把探索问题的部分写的太省略了,导致我看完之后都不知道这个问题的根本原因是什么。

而为什么我会对这篇文章特别感兴趣呢?

因为这个“坑”我记得我刚刚入行没两年的也遇到过,我已经不记得自己当时是怎么解决的了,但是我肯定也没有深入的去研究。

因为那个时候遇到问题,就去网上费尽心思的查,粘一个方案过来看能不能用。

如果不能用的话,心里暗骂一句:小可(S)爱(B),然后接着找。

直到找到一个可以用的。

至于为什么能用?

管它呢,研究这玩意干啥。

主要是当时觉得探索这个玩意到进入到源码里面去,一涉及到源码心里就犯怵,所以就敬而远之。

现在不一样了,现在我看到源码我就觉得兴奋,心里想着:多好的素材啊。

既然这次又让我遇到了,所以我决定把几年前的坑填上,盘一盘它。

搞个 Demo

由于这个现象太过匪夷所思,所以写文章的那个老哥认为这个是一个 BUG,还在 Spring 的 github 上提了一个 issues:

https://github.com/spring-projects/spring-framework/issues/28741

这里面他附上了一个可以复现的 Demo,所以我就直接拿来用了。

确实是可以复现,但是其实他提供的这个 Demo 还是有点臃肿,具有一点点的迷惑性,直接给我迷晕了,让我在这上面稍微花了时间。

先给你看一下他的 Demo 是怎么样的。

主要是两个 Controller 接口。

第一个接口是  get 请求类型的 getParams,代码很简单,先放在这里,等下用:

第二个接口是 post 请求类型的 postTest,就这么几行代码:

@PostMapping("/postTest")
public String postTest(HttpServletRequest request) {
    String age1 = request.getParameter("age");
    String name1 = request.getParameter("name");
    System.out.println("age1=" + age1 + ",name1=" + name1);
    new Thread(new Runnable() {
        @Override
        public void run() {
            String age2 = request.getParameter("age");
            String name2 = request.getParameter("name");
            System.out.println("age2=" + age2 + ",name2=" + name2);
            //模拟业务请求
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            age2 = request.getParameter("age");
            name2 = request.getParameter("name");
        }
    }).start();
    return "post success";
}

主要是里面启动了一个线程,在线程里面有从 request 里面获取参数的动作。

这个方法访问起来是这样的一个情况:

从 age2、name2 输出上看,虽然 request 传入到异步线程里面了,但是还是能从里面获取到对应的参数,没有看出来有什么毛病。

但是接下来,匪夷所思的事情就要出现了。

还记得我们前面的 getParams 接口吗?

我再把它拿过来给你看一眼:

你说,就这个接口,我用下面这个链接去访问,在我的认知里面是完全不可能有任何问题的,对吧?

http://127.0.0.1:8080/getParams?a=1&b=2

但是,这玩意还真的就打破了我的认知:

在访问 postTest 方法之后,再次访问 getParams 方法,getParams 方法居然抛出异常了?

抛出的异常是说我调用的时候没有传递 b 这个参数。

但是我的链接里面明明就是有 b=2 的啊?

这玩意上哪里说理去?

上面就是那位老哥提供的可复现的 Demo 的主要部分。

但是我前面说了,这个 Demo 有点臃肿,具有一点点迷惑性。

首先如果我再加一个输出语句,那么在一个短暂的 sleep 之后, age2 和 name2 就没了:

虽然还是感觉有点神奇吧,但是也没有刚刚那个操作让我感到震惊。

因为从输出 null 这个结果,我至少可以知道程序在这个地方就出现问题了,把问题的范围限定在了一次请求中。

刚刚那个操作,好家伙,表现出来到情况是这样的:

  • 先发起一个 post 请求,看起来是正常的。

  • 然后再发起一个 get 请求,这个 get 请求挂了。

  • 但是这个 get 请求从发起的角度来看找不到任何毛病。

你要基于上面这个情况去分析问题的话,就不好找问题了,毕竟要发起两个毫不相干的请求才能触发问题。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值