多线程中RequestContextHolder的请求信息获取问题

问题简述

最近遇到一个问题,在开启多线程执行任务时,子线程中访问需要的鉴权的方法时会丢失request请求信息,导致方法没有正确执行,于是写下这篇博客记录一下排查错误并解决问题的过程。


问题复现

在这里插入图片描述
当我执行这段代码,希望使用多线程的方式向数据库中插入数据时,发现当方法执行结束后,数据库并没有正确添加数据,并且控制台日志信息打印出 “非http请求” 的信息。


带着疑问,开始debug,发现添加数据时需要先拿到用户数据。
在这里插入图片描述


继续往里跟,发现拿取用户信息前有个获取token的操作。
在这里插入图片描述


继续,发现这个方法中有一个获取请求的操作。
在这里插入图片描述


点进去看,原来是子线程的请求属性servletRequestAttributes为null导致数据无法添加,并且打印的日志信息与前面的日志信息也完全符合。
在这里插入图片描述


到此,确定了问题出现的原因,是因为在使用多线程执行任务时,开启的子线程并没有存储主线程请求信息servletRequestAttributes的上下文,导致它为空进而获取用户信息失败,最后无法添加数据。

那就是说只要在任务开启之前,对每个子线程设置一个与主线程相同的servletRequestAttributes即可,说干就干。


问题解决

要拿到当前线程的servletRequestAttributes,想到可以使用RequestContextHolder这个类,这个类提供了获取和设置servletRequestAttributes的方法

// 获取当前线程的请求信息
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

对之前的代码进行改造,对每个子线程设置请求信息,如下所示:

在这里插入图片描述


果然,经过改造后重新执行这个方法,成功使用多线程向数据库添加数据。
在这里插入图片描述


问题原因

但是,还有一个问题,就是为什么使用多线程时子线程不会存储主线程的servletRequestAttribute呢?

于是我点开RequestContextHolder源码:

在这里插入图片描述
在这里插入图片描述
原来请求信息是通过ThreadLocal存储的,当请求到达Spring容器后,Spring会把该请求Request实例通过setRequestAttributes方法,把Request实例放入该请求线程内ThreadLocalMap中,然后就可以通过静态方法取到,但ThreadLocal不能让子线程继承ThreadLocalMap信息,于是子线程中的请求信息就为null。

SpringBoot 默认使用ThreadLocal把Request设置进请求线程中,这样如果在请求方法里面另起一个子线程然后再通过getRequestAttributes方法获取,是获取不到的。

至此,我们就完全搞明白了这个问题出现的原因,收获良多。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值