1 问题描述
最近一直在搞每月一次的抽奖活动, 并发量也比平时多了不少, 随之而来的, 就是平时遇不到的一些问题. 这也是可喜可贺的啊, 有问题才能成长, 没有问题就是在浪费生命.
其中一个感觉比较奇葩的问题, 就是: Tomcat 在接收 POST 请求时, 偶发性的 POST 参数接收不全, 这个比例还很高. 如下所示:
45 应用服务器正常 POST 参数获取:
2016-05-19 15:45:15 INFO :request param : body=[{"appDevice":{"qdPlatform":"weixin","qdVersion":"1.4.1"},"activityId":80,"curPlanId":381,"memberId":"ff808081547ef0b401549f64e6cb2ecb","projectId":"31605061701144","prizeIds":[437,436,435,440,439,438],"planIds":[370,371,372,373,374,375,376,377,378,379,380,381,382]}]
45 应用服务器不正常 POST 参数获取:
2016-05-19 16:03:16 INFO :request param : 7D,"activityId":80,"curPlanId":381,"memberId":"ff80808150f0fdd401510f83cae413a9","projectId":"708","prizeIds":[437,436,435,440,439,438][370,371,372,373,374,375,376,377,378,379,380,381,382][370,371,372,373,374,375,376,377,378,379,380,381,382]=[]
2 排查问题
排查问题之前, 先理清服务架构, 如图:
排查问题开始之前, 简单说下自己排查问题的几个原则 (仅供参考):
问题重现: 一定要先重现问题, 任何重现不了的问题, 都不是问题. 同理, 任何存在的问题, 都必然能再次重现.
由近及远: 先确认自己的代码无问题, 然后再去确认外部代码无问题 (如: 框架代码, 第三方代码等).
由外到内: 程序就是一个 IPO, 有输入 Input(如: 参数, 环境等) 也有输出 Out(如: 结果, 异常等), 输出 Out 是问题的表象, 先确定外部因素 Input 无问题, 再确认程序代码逻辑无问题.
由浅入深: 其实就是由易到难, 自上向下, 先从上层应用排查问题, 如: 上层 API, 应用层, HTTP 传输等, 然后再确认底层应用排查问题, 如: 底层 API, 网络层, 系统层, 字节码, JVM 等;
确认在上面日志输出的代码中是否存在并发问题, 或逻辑 Bug:
Map<String, String[]> parameterMap = request.getParameterMap();
Set<String> keySet = parameterMap.keySet();
StringBuilder sb = new StringBuilder();
for(String key : keySet) {
sb.append(key).append("=").append(Arrays.toString(parameterMap.get(key))).append(",");
}
log.info("request param :" + sb.toString()) ;
通过上面的两条日志输出, 和以上代码的分析, 可以得出以下结论:
以上代码不存在并发问题及逻辑 Bug,POST 参数是从原始 request.getParameterMap() 中获取, 获取前后未做任何写入, 转换处理;
通过不正常日志输出和以上代码逻辑来看, 从 request.getParameterMap() 中获取的参数,"body" 这个 Key 已经丢失, 且对于的 Value 也存在部分丢失, ParameterMap 中的 Key-Val 混乱;
确认是否存在 tomcat POST/GET 参数长度限制, nginx POST/GET 参数长度限制, 导致请求参数截断:
很快就排除了这种可能, 因为参数不全是偶发性产生的, 如果是由于参数长度限制导致请求参数截断, 而应该是 100% 产生参数不全的问题.
根据上面服务架构图和由外到内的原则, 通过 tcpdump 和 Wireshark 来分析, 进入前端 Nginx 服务器的请求参数是否存在不全:
可以确认, 用户的请求从浏览器发出在到达 Nginx 之前, POST 请求参数就已经丢失;
注意: 想要了解更多 tcpdump 和 Wireshark 分析使用, 请参考 聊聊 tcpdump 与 Wireshark 抓包分析 .
3 解决问题
在上面排查问题的过程中, 就已经确定了是用户前端传过来的参数问题, 但从前端代码排查来看, 其实也没有发现问题, 只是在前端做了个 JSON 序列化. 所以为了避免问题再次发生, 将前端序列化去掉, 保持 HTTP 请求参数原始传输, 由前端的代理服务再进行序列化后, 提交给后端应用.
4 总结问题
其实对于问题的解决并不重要, 有时或许只是一个空格, 一个微小的配置等, 重要的是在于问题的分析过程, 分析思路, 怎么样有个清晰的思路, 快速的定位问题, 才是解决问题的关键.
来源: https://juejin.im/post/5a6ec05a518825734217271c