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


    一、What

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

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

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

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

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

            But why? what's wrong?

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

     二、When

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

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

             抱着“大胆假设,小心求证”之精神,以“抽丝剥茧”之排除大法,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对象而非创建)

     1、SpringBoot从request获取parameter参数

    RequestParamMethodArgumentResolver#resolveName

    2、tomcat封装解析参数

    request通过Parameters获取parameter参数:

    org.apache.catalina.connector.Request#getParameterValues

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

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

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

    4、请求处理结束,还原

    五、Why

             tomcat机制:

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

     

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

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

     

             本次问题的根本原因则在于线程中引用了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参数值

     

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

    六、How

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

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

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

  • 相关阅读:
    Redis的持久化机制
    关于Gson的TypeToken
    【Python+selenium】自动化生成测试报告
    微信怎么自动添加好友?
    GPT-4o多模态大模型的架构设计
    畅捷通+数环通iPaaS,实现无代码集成上千款应用
    Vue中如何进行跨域处理
    CIO40: 数字化落地最佳实践(16000字)
    是顺流还是逆流?未来物流作业是否将被机器人全面取代?
    Redis入门:Redis持久化策略RDB&AOF简介
  • 原文地址:https://blog.csdn.net/blue225/article/details/126009897