1 問(wèn)題描述
最近一直在搞每月一次的抽獎(jiǎng)活動(dòng)澄干,并發(fā)量也比平時(shí)多了不少逛揩,隨之而來(lái)的柠傍,就是平時(shí)遇不到的一些問(wèn)題。這也是可喜可賀的啊辩稽,有問(wèn)題才能成長(zhǎng)惧笛,沒(méi)有問(wèn)題就是在浪費(fèi)生命。
其中一個(gè)感覺(jué)比較奇葩的問(wèn)題逞泄,就是:Tomcat在接收POST請(qǐng)求時(shí)患整,偶發(fā)性的POST參數(shù)接收不全,這個(gè)比例還很高喷众。如下所示:
45應(yīng)用服務(wù)器正常POST參數(shù)獲雀餮琛:
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應(yīng)用服務(wù)器不正常POST參數(shù)獲取:
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 排查問(wèn)題
排查問(wèn)題之前到千,先理清服務(wù)架構(gòu)昌渤,如圖:
排查問(wèn)題開(kāi)始之前,簡(jiǎn)單說(shuō)下自己排查問(wèn)題的幾個(gè)原則(僅供參考):
問(wèn)題重現(xiàn):一定要先重現(xiàn)問(wèn)題憔四,任何重現(xiàn)不了的問(wèn)題膀息,都不是問(wèn)題。同理了赵,任何存在的問(wèn)題潜支,都必然能再次重現(xiàn)。
由近及遠(yuǎn):先確認(rèn)自己的代碼無(wú)問(wèn)題柿汛,然后再去確認(rèn)外部代碼無(wú)問(wèn)題(如:框架代碼冗酿,第三方代碼等)。
由外到內(nèi):程序就是一個(gè)IPO络断,有輸入Input(如:參數(shù)裁替、環(huán)境等)也有輸出Out(如:結(jié)果、異常等)妓羊,輸出Out是問(wèn)題的表象胯究,先確定外部因素Input無(wú)問(wèn)題,再確認(rèn)程序代碼邏輯無(wú)問(wèn)題躁绸。
由淺入深:其實(shí)就是由易到難、自上向下臣嚣,先從上層應(yīng)用排查問(wèn)題净刮,如:上層API、應(yīng)用層硅则、HTTP傳輸?shù)妊透福缓笤俅_認(rèn)底層應(yīng)用排查問(wèn)題,如:底層API怎虫、網(wǎng)絡(luò)層暑认、系統(tǒng)層困介、字節(jié)碼、JVM等蘸际;
- 確認(rèn)在上面日志輸出的代碼中是否存在并發(fā)問(wèn)題座哩,或邏輯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()) ;
通過(guò)上面的兩條日志輸出,和以上代碼的分析粮彤,可以得出以下結(jié)論:
以上代碼不存在并發(fā)問(wèn)題及邏輯Bug根穷,POST參數(shù)是從原始request.getParameterMap()中獲取,獲取前后未做任何寫(xiě)入导坟、轉(zhuǎn)換處理屿良;
通過(guò)不正常日志輸出和以上代碼邏輯來(lái)看,從request.getParameterMap()中獲取的參數(shù)惫周,"body"這個(gè)Key已經(jīng)丟失尘惧,且對(duì)于的Value也存在部分丟失,ParameterMap中的Key-Val混亂递递;
- 確認(rèn)是否存在tomcat POST/GET參數(shù)長(zhǎng)度限制褥伴,nginx POST/GET參數(shù)長(zhǎng)度限制,導(dǎo)致請(qǐng)求參數(shù)截?cái)啵?/li>
很快就排除了這種可能漾狼,因?yàn)閰?shù)不全是偶發(fā)性產(chǎn)生的重慢,如果是由于參數(shù)長(zhǎng)度限制導(dǎo)致請(qǐng)求參數(shù)截?cái)啵鴳?yīng)該是100%產(chǎn)生參數(shù)不全的問(wèn)題逊躁。
- 根據(jù)上面服務(wù)架構(gòu)圖和由外到內(nèi)的原則似踱,通過(guò)tcpdump和Wireshark來(lái)分析,進(jìn)入前端Nginx服務(wù)器的請(qǐng)求參數(shù)是否存在不全:
可以確認(rèn)稽煤,用戶(hù)的請(qǐng)求從瀏覽器發(fā)出在到達(dá)Nginx之前核芽,POST請(qǐng)求參數(shù)就已經(jīng)丟失;
注意:想要了解更多tcpdump和Wireshark分析使用酵熙,請(qǐng)參考聊聊tcpdump與Wireshark抓包分析轧简。
3 解決問(wèn)題
在上面排查問(wèn)題的過(guò)程中,就已經(jīng)確定了是用戶(hù)前端傳過(guò)來(lái)的參數(shù)問(wèn)題匾二,但從前端代碼排查來(lái)看哮独,其實(shí)也沒(méi)有發(fā)現(xiàn)問(wèn)題,只是在前端做了個(gè)JSON序列化察藐。所以為了避免問(wèn)題再次發(fā)生皮璧,將前端序列化去掉,保持HTTP請(qǐng)求參數(shù)原始傳輸分飞,由前端的代理服務(wù)再進(jìn)行序列化后悴务,提交給后端應(yīng)用。
4 總結(jié)問(wèn)題
其實(shí)對(duì)于問(wèn)題的解決并不重要譬猫,有時(shí)或許只是一個(gè)空格讯檐、一個(gè)微小的配置等羡疗,重要的是在于問(wèn)題的分析過(guò)程、分析思路别洪,怎么樣有個(gè)清晰的思路叨恨,快速的定位問(wèn)題,才是解決問(wèn)題的關(guān)鍵蕉拢。