前沿
前几天在网上冲浪的时候看到一篇技术文章,讲的是他把一个 request 请求传递到了线程池里面,文章还是挺不错的,把发现问题和解决问题都写的很明白了。但是我觉得写的太多了啰嗦,没人想听你到底怎么找到bug的过程, 直接把问题和原因描述出来不就行了,时间都是宝贵的废了半天的力气看了一大堆没用的东西
说到这个“坑”我记得我刚刚入行没两年的也遇到过,我已经不记得自己当时是怎么解决的了,但是我肯定也没有深入的去研究。因为那个时候遇到问题,就去网上费尽心思的查,粘一个方案过来看能不能用。 如果不能用的话,心里暗骂一句,然后接着找。直到找到一个可以用的。至于为什么能用?管它呢,研究这玩意干啥
前几天突然又遇到了,又废了一番功夫把问题解决了, 这次我要记录下来以后在遇到就能迎刃而解了…
问题原因
简单描述下:
Java Servlet 设计规范里说了: 每个 request 对象只在 servlet 的服务方法的范围内有效,或者在过滤器的 doFilter 方法的范围内有效。规范里面是建议了容器里面实现 request 的时候尽量复用,而不是回收,目的是节约性能。 程序员朋友们必须要意识到,我不建议在上述范围之外维护 request 的引用,因为它可能会产生不确定的结果。
而我们创建一个线程在线程中使用request 那么就脱离了servlet了, 会导致request 脱离了原本的生命周期从而导致使用完毕后没有执行request内清除资源的动作,导致其他的接口请求复用request 的时候出现了问题,因为还保存上次request的值,那么其他的接口在解析request 的时候就会跳过参数解析,继续复用上次的值, 然后就会导致接口请求失败参数映射不上去,或者请求后的值不对的问题
解决办法
正确使用request传入异步中的方式
@GetMapping(value = "/testRequest")
@SemaphoreDoc(key = "testRequest")
public void testRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
AsyncContext asyncContext = request.startAsync(request, response);
new Thread(()->{
String name = request.getParameter("name");
System.out.println(name);
//手动释放request资源
asyncContext.complete();
}).start();
}