<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
有後端同事反饋在非同步執行緒中獲取了request中的引數,然後下一個請求是get請求的話,發現會偶爾出現引數丟失的問題.
範例程式碼:
@GetMapping("/getParams") public String getParams(String a, int b) { return "get success"; } @PostMapping("/postTest") public String postTest(HttpServletRequest request,String age, String name) { new Thread(new Runnable() { @Override public void run() { String age2 = request.getParameter("age"); String name2 = request.getParameter("name"); try { Thread.sleep(10); } catch (InterruptedException e) { throw new RuntimeException(e); } String age3 = request.getParameter("age"); String name3 = request.getParameter("name"); System.out.println("age1: " + age + " , name1: " + name + " , age2: " + age2 + " , name2: " + name2 + " , age3: " + age3 + " , name3: " + name3); } }).start(); return "post success"; }
異常資訊如下
java.lang.IllegalStateException:
Optional int parameter 'b' is present but cannot be translated into a null value due to being declared as a primitive type.
Consider declaring it as object wrapper for the corresponding primitive type
看到這裡大家可以猜一下是為什麼.
我的第一反應是不可能,肯定是前端同學寫的程式碼有問題,這麼簡單的一個介面怎麼可能有問題,然而等同事復現後就只能默默debug了.
大概追了一下原始碼,發現
spring 在做引數解析的時候沒有獲取到引數,方法如下:
org.springframework.web.method.annotation.RequestParamMethodArgumentResolver#resolveName
而且很奇怪,queryString 不是null ,獲取到了正確的引數, 但是 parameterMap 卻是空的.
正常來說 parameterMap 裡面應該存放有 queryString 解析後的引數.
如圖:
搜尋了一下,發現有人碰到過類似的情況
偶現的MissingServletRequestParameterException,誰動了我的引數?
由於Tomcat中,Request以及Response物件都是會被迴圈使用的,因此這個時候也是整個Request被重置的時候。 所以根本原因是,在Parameter被重置了之後,didQueryParameters又被置成了true,導致新的請求引數沒有被正確解析,就報錯了(此時的parameterMap已經被重置,為空)。 而didQueryParameters只有在一種情況下才會被置為true,也就是handleQueryParameters方法被呼叫時。 而handleQueryParameters會在多個場景中被呼叫,其中一個就是getParameterValues,獲取請求引數的值。
大概就是說 tomcat 會複用Request物件,在非同步中使用request中的引數可能會影響下一次 請求的引數解析過程.
最後文章作者的結論就是
不要將HttpServletRequest傳遞到任何非同步方法中!
看到這裡我還是有點不信,心想tomcat不會這麼拉吧,非同步都不支援,不可能吧...
於是我就去 tomcat的 bugzilla 搜了一下,居然沒搜尋到相關的問題.
然後我還是有點不甘心,tomcat 沒有 ,spring框架出來這麼久難道就沒人碰到過這種問題提出疑問嗎?
又去 spring的 issue 裡面去搜,可能是我的關鍵詞沒搜對,還是沒找到什麼有用資訊.
這時我就有點洩氣了,官方都沒解決這個問題我咋個辦?
不過我又突然想到既然引數解析的時候 queryString 裡面有引數,那豈不是自己再解析一次不就完美了嗎?
那這個時候我們只要
這裡有兩個問題需要注意就是 :
解析get請求 url中引數的是 useDefaultResolution 屬性值為 true 的那一個.
spring原始碼對應位置:
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter#getDefaultInitBinderArgumentResolvers private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() { List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>(20); // Annotation-based argument resolution resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); resolvers.add(new RequestParamMapMethodArgumentResolver()); resolvers.add(new PathVariableMethodArgumentResolver()); resolvers.add(new PathVariableMapMethodArgumentResolver()); resolvers.add(new MatrixVariableMethodArgumentResolver()); resolvers.add(new MatrixVariableMapMethodArgumentResolver()); resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory())); resolvers.add(new SessionAttributeMethodArgumentResolver()); resolvers.add(new RequestAttributeMethodArgumentResolver()); // Type-based argument resolution resolvers.add(new ServletRequestMethodArgumentResolver()); resolvers.add(new ServletResponseMethodArgumentResolver()); // Custom arguments if (getCustomArgumentResolvers() != null) { resolvers.addAll(getCustomArgumentResolvers()); } // Catch-all resolvers.add(new PrincipalMethodArgumentResolver()); resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true)); return resolvers; }
這個方案實現以後給專案組上的同事整合後看起來是沒什麼問題了.
引數也能獲取到了,業務也跑通了,也不會報錯了.
但是其實這是一個治標不治本的方案
還存在一些問題:
這個時候我已經沒什麼好的辦法了,於是給spring 提了一個issue:
等待回覆是痛苦的,issue提了以後
等了三天,開發者叫我提交一個復現的 demo (大家也可以嘗試復現一下).
又等了兩天,我想著這樣等也不是個辦法
主要是我看到 issue 還有 1.2k,輪到我的時候估計都猴年馬月了
而且就算修復了估計也是新版本, 在專案上升級 springboot 版本 估計也不太現實(版本不相容)
於是我開始看原始碼.直到我看到了一個
org.apache.coyote.Request#setHook
它裡面有個 ActionCode,是一個列舉型別,其中有一個列舉值是
ASYNC_START
這玩意看著就和非同步有關.於是開始搜尋相關資料
最後終於在
RequestLoggingFilter: afterRequest is executed before Async servlet finishes
中找到答案.
結合我的程式碼改造如下
@PostMapping("/postTest") public String postTest(HttpServletRequest request, HttpServletResponse response, String age, String name) { AsyncContext asyncContext = request.isAsyncStarted() ? request.getAsyncContext() : request.startAsync(request, response); asyncContext.start(new Runnable() { @Override public void run() { String age2 = request.getParameter("age"); String name2 = request.getParameter("name"); try { Thread.sleep(10); } catch (InterruptedException e) { throw new RuntimeException(e); } String age3 = request.getParameter("age"); String name3 = request.getParameter("name"); System.out.println("age1: " + age + " , name1: " + name + " , age2: " + age2 + " , name2: " + name2 + " , age3: " + age3 + " , name3: " + name3); asyncContext.complete(); } }); return "post success"; }
ps: 此處應該用執行緒池提交任務,不想改了
壓測一把發現沒啥問題
springboot 中如何正確的在非同步執行緒中使用request
到此這篇關於springboot 中如何正確的在非同步執行緒中使用request的文章就介紹到這了,更多相關springboot 非同步執行緒使用request內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45