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 总结问题
其实对于问题的解决并不重要,有时或许只是一个空格、一个微小的配置等,重要的是在于问题的分析过程、分析思路,怎么样有个清晰的思路,快速的定位问题,才是解决问题的关键。