【问题】SpringBoot之GET请求参数偶发性丢失问题

一、What

        最近又偶遇一诡异棘手之问题!

        一个用于获取公钥数据的接口,不定期偶尔频频出现“参数不存在”的问题!

        一度怀疑这是前端的锅,虽然前端同学再三以人格担保!

        经过非仔细观察,发现每每出现问题时,“再点一下就好了”!

        错误信息简单明确,是大家熟知的参数缺失异常:Required request parameter 'xxx' for method parameter type String is not present

【问题】SpringBoot之GET请求参数偶发性丢失问题_第1张图片

        But why? what's wrong?

        Anyway,这是再普通不过的一个GET接口!

【问题】SpringBoot之GET请求参数偶发性丢失问题_第2张图片

 二、When

        作为专业人士,咱自不能流于表象,当寻根溯源,探寻问题背后之本质!唯有寻得问题背后之根由,方可得正确解决问题之道!

        Well, firstly, let's look look, what the hell a http go?

【问题】SpringBoot之GET请求参数偶发性丢失问题_第3张图片

         抱着“大胆假设,小心求证”之精神,以“抽丝剥茧”之排除大法,when the 参数 lost?!

  • HTTP请求:传参正确,排除
  • Nginx:接收正常,排除
  • Gateway:url & queryString正常
  • Controller:参数没了。。

        So,What's up? 

二、Where

        种种迹象表明:问题当发生在服务内部而非外部网络或请求转发丢失!

        进一步分析,发现在问题请求中request.queryString()正常,而request.getParameter()值却是没有获取到

        众所周知的,SpringBoot默认内置tomcat容器,SpringMVC则通过request.getParameter方法获取并绑定Controller接口参数!

        因此,初步判断在tomcat获取parameter参数时出现了不明原因问题

        Well,parameter参数的获取过程是怎样的?

  • SpringMVC框架通过DispatcherServlet实现
  • Tomcat接收到外部请求,将由connector通过Processor受理http请求
  • SpringMVC通过request.getParameter获取并绑定Controller接口参数
  • request.getParameter方法在请求处理过程中仅在第一次调用时通过解析queryString获取parameters参数值,并设置didQueryParameter=true标识已解析处理
  • Http请求处理完成,processor通过release方法释放连接重置参数属性,request.recycle方法重置request参数属性(注意:这里连接器及request对象并不会被销毁,connector再次受理新的请求时,将复用连接器、processor及request对象而非创建)

【问题】SpringBoot之GET请求参数偶发性丢失问题_第4张图片

 1、SpringBoot从request获取parameter参数

RequestParamMethodArgumentResolver#resolveName

【问题】SpringBoot之GET请求参数偶发性丢失问题_第5张图片

2、tomcat封装解析参数

request通过Parameters获取parameter参数:

org.apache.catalina.connector.Request#getParameterValues

【问题】SpringBoot之GET请求参数偶发性丢失问题_第6张图片

【问题】SpringBoot之GET请求参数偶发性丢失问题_第7张图片

 3、Parameters从queryString解析封装parameter参数

org.apache.tomcat.util.http.Parameters#handleQueryParameters

可以发现,参数在解析处理后会设置didQueryParameters参数为true,

【问题】SpringBoot之GET请求参数偶发性丢失问题_第8张图片

4、请求处理结束,还原

【问题】SpringBoot之GET请求参数偶发性丢失问题_第9张图片

五、Why

         tomcat机制:

  • tomcat可支持多个service实例(这不是重点)
  • 每个service实例维护了一个connector池
  • 当service接收到一个http请求时,则从连接池中获取connector进行响应处理

【问题】SpringBoot之GET请求参数偶发性丢失问题_第10张图片

 

        连接器Connector通过Processor对应http请求进行响应处理!

【问题】SpringBoot之GET请求参数偶发性丢失问题_第11张图片

        Processor封装了request与response对象:在请求处理开始时进行初始化封装(仅封装参数属性,并不创建对象),请求处理完成后则进行释放重置(仅重置参数属性,并不销毁对象)!

【问题】SpringBoot之GET请求参数偶发性丢失问题_第12张图片 

         本次问题的根本原因则在于线程中引用了request对象,并在线程中调用了request.getParameter()方法使参数属性didQuerParameter错误而导致http请求无法正确获取参数值!

  • 假设第一次受理http请求的连接器为connector1
  • 请求request在子线程thread1中被引用
  • connector1完成http请求并执行release释放连接,这时request.didQueryParameters值为false
  • 如果子线程thread1处理任务的时间较长,调用了getParameter方法,这时request.didQueryParameters值将再次被更新为true
  • 当tomcat再次通过connector1受理新的http请求时,由于request.didQueryParameters=true,这时新请求调用getParameter方法将不会再解析queryString,因而无法正确获取parameter参数值

【问题】SpringBoot之GET请求参数偶发性丢失问题_第13张图片

 

         测试验证问题,问题重现与预期一致!

【问题】SpringBoot之GET请求参数偶发性丢失问题_第14张图片

六、How

        修复线程中引用request相关代码,问题解决!

  • 牢记tomcat复用connector这一机制,在编码过程中应特别注意!
  • 不要在线程中引用request等任意tomcat相关组件或属性!

参考:spring boot偶发性丢失POST请求参数问题的解决 - checkboxMan的个人空间 - OSCHINA - 中文开源技术交流社区

你可能感兴趣的:(Question,Spring,Tomcat,tomcat,java,servlet)