当前位置: 首页 > 后端技术 > Java

请求异步传输

时间:2023-04-01 17:26:29 Java

在一些业务场景中,可能希望使用线程池异步处理请求,或者新开一个线程做一些计算。在这些异步环境中,你希望从源请求信息或请求参数的请求对象中得到一些headers。一个常用的方法是在启动异步任务之前从主线程获取请求的ServletRequestAttributes对象,并阻止其进入子线程,例如:@GetMapping("asyncRequest")@SneakyThrowspublicvoidtest(){//主线程请求传递finalServletRequestAttributesattributes=(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();newThread(()->{RequestContextHolder.setRequestAttributes(attributes);for(inti=0;i<10;i++){System.out.println(((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest().getRequestURI());try{Thread.sleep(1000);}catch(InterruptedExceptione){thrownewRuntimeException(e);}}}).start();线程.睡眠(5000);这段代码比较简单。从主线程获取request属性,设置到子线程。每秒一次,打印十次,主线程结束,5秒后返回。正常来说,效果应该很简单,打印10次请求路径就可以了。但是实际效果有点出乎意料:可以看到,打印了6次之后,后面突然变成了null。为什么?因为我用的是spring-boot的内嵌tomcat容器,所以玄机就在CoyoteAdapter这个类。打开源码,在service()方法的finally底部有这么一段话:req.getRequestProcessor().setWorkerThreadName(null);req.clearRequestThread();//回收包装请求和响应if(!async){updateWrapperErrorCount(request,response);请求.回收();响应.回收();}我们的请求不是异步的,所以我们会通过下面的recycle方法。毫无疑问,this是用来销毁和回收request对象的,之前的request通过RequestContextHolder.setRequestAttributes(attributes)设置为当前线程的requestAttributes对象。查看源码可以看出,它只是将requestAttributes放在了子线程的ThreadLocal中。当源对象被销毁时,这里的引用只会指向一个空壳,不会报npe,但是也拿不到数据。所以,如果要将主线程的请求传递给子线程,要么花大价钱克隆一个,要么把你用的拿出来,手动传递。