nginx負(fù)載下站點(diǎn)錯(cuò)誤響應(yīng)會(huì)導(dǎo)致其他節(jié)點(diǎn)重復(fù)響應(yīng)問(wèn)題的解決過(guò)程

前言:

這是我上周工作過(guò)程中的一次解決問(wèn)題的過(guò)程。解決的是nginx負(fù)載下站點(diǎn)錯(cuò)誤響應(yīng)導(dǎo)致其他節(jié)點(diǎn)重復(fù)響應(yīng)。 我在整理這個(gè)記敘文時(shí),在給這個(gè)文檔命名時(shí)思考了一段時(shí)間。 從業(yè)務(wù)角度說(shuō),應(yīng)該是“一次短信重復(fù)發(fā)送問(wèn)題解決過(guò)程”,如果這樣命名,過(guò)于土氣;從技術(shù)角度說(shuō),應(yīng)該是“nginx負(fù)載下站點(diǎn)錯(cuò)誤響應(yīng)會(huì)導(dǎo)致其他節(jié)點(diǎn)重復(fù)響應(yīng)”,如果這樣命名,可能沒人會(huì)注意了;最后,我懶得再費(fèi)腦汁了,從人類智慧的角度我把它命名為“復(fù)雜的問(wèn)題,簡(jiǎn)單的解,小處不謹(jǐn)慎,”。

問(wèn)題來(lái)了

3月16日,有客服、銷售、運(yùn)營(yíng)人員反映,客戶在saas預(yù)定機(jī)票完成后,會(huì)連續(xù)收到3條重復(fù)的支付提醒短信。 ?很多客戶都投訴了這樣的問(wèn)題,一直以來(lái)的正常情況是僅發(fā)1條的。

以下是短信平臺(tái)上的截圖:

前一天的3月15日是上線日,當(dāng)晚saas上過(guò)線。

迫于壓力,當(dāng)天下午不得不回滾3月15日的上線?;貪L之后,問(wèn)題不再重現(xiàn)。

3月15日saas的上線包括錦如和劉濤對(duì)saas所做的代碼改動(dòng)。劉濤涉及到的代碼是審批接口,稱可能不會(huì)導(dǎo)致這樣的問(wèn)題,錦如也肯定地表示不涉及到這塊。 我讓劉濤來(lái)排查原因,他在測(cè)試環(huán)境做了幾次測(cè)試,并未發(fā)現(xiàn)這樣的問(wèn)題。我呢,由于最近忙于另一個(gè)項(xiàng)目,一直沒來(lái)得及親自和他一起查找原因。

問(wèn)題又來(lái)了

3月17日是周四,上線日。要發(fā)布中新融創(chuàng)的對(duì)接程序。 晚上上線后,立即在線上訂票,發(fā)現(xiàn)訂單支付提醒短信還是會(huì)重復(fù)發(fā)3遍。

當(dāng)時(shí)已經(jīng)是晚上21點(diǎn),我決定放下手頭的活兒,來(lái)跟劉濤一起排查原因。

問(wèn)題分析

客戶在saas站點(diǎn)里預(yù)定機(jī)票,涉及到審批。審批是獨(dú)立的一個(gè)站點(diǎn)。

訂單的審批邏輯是這樣的:客戶在saas訂票成功后,會(huì)跳轉(zhuǎn)到統(tǒng)一審批頁(yè),提交審批后,統(tǒng)一審批系統(tǒng)會(huì)把審批狀態(tài)通知給saas,然后重定向頁(yè)面到訂單流的下一個(gè)頁(yè)面(由saas提供)。

Saas提供了一個(gè)由一般處理程序(文件是HandlerAirOrderExamineStatus.ashx)實(shí)現(xiàn)的接口,用來(lái)接收審批狀態(tài)。內(nèi)部大致邏輯是,修改訂單的審批狀態(tài),然后會(huì)觸發(fā)相應(yīng)的對(duì)客提醒短信。 客戶重復(fù)收到的短信是支付提醒短信,那么,意味著訂單不需要審批。不需要審批在系統(tǒng)里表現(xiàn)在這些企業(yè)配置審批流。 根據(jù)現(xiàn)有代碼邏輯,從填單頁(yè)跳轉(zhuǎn)到統(tǒng)一審批頁(yè),統(tǒng)一審批頁(yè)初始化時(shí)沒有獲取到新審批,則會(huì)自動(dòng)執(zhí)行通知saas和頁(yè)面重定向。

要補(bǔ)充說(shuō)明一下的是,線上的審批站點(diǎn)和saas站點(diǎn)均是通過(guò)nginx做的3臺(tái)服務(wù)器的負(fù)載。

通過(guò)查看線上日志,審批系統(tǒng)正常,即只會(huì)在其中一臺(tái)服務(wù)器節(jié)點(diǎn)來(lái)請(qǐng)求saas接口進(jìn)行審批狀態(tài)的通知,并未發(fā)現(xiàn)異常日志。而saas呢,卻發(fā)現(xiàn)3臺(tái)服務(wù)器都記錄了同樣的日志。

為什么會(huì)出現(xiàn)這樣的情況呢? 審批在通知saas時(shí),nginx接收到請(qǐng)求后應(yīng)該分發(fā)給其中一個(gè)節(jié)點(diǎn)服務(wù)器來(lái)處理請(qǐng)求才對(duì)呀。

困惑

因?yàn)橹跋到y(tǒng)運(yùn)行都是正常的,并未出現(xiàn)這種短信重發(fā)的情況,所以我們暫先不懷疑代碼邏輯。

馬上想到,記得運(yùn)維說(shuō)過(guò),如果一個(gè)服務(wù)器處理時(shí)間超長(zhǎng),會(huì)自動(dòng)分發(fā)給另一個(gè)節(jié)點(diǎn)服務(wù)器。 歷史原因,獲取訂單詳情和觸發(fā)短信通知這兩段代碼執(zhí)行比較慢,那么,我在這兩段代碼外面加了個(gè)Stopwatch來(lái)診斷其執(zhí)行時(shí)長(zhǎng)。

publicvoidProcessRequest(HttpContext context)

{

Stopwatch sw=newStopwatch();

sw.Start();if(context.Request.Params.Count ==0)

{

context.Response.Write("{'returnCode':'2','returnMsg':'no parameters','notifiedSMSContent':'no parameters'}");return;

}stringparametersStr = context.Request.Params[0].AsToString();

SysLogToFile.WriteAuditMessage("dat修改訂單狀態(tài)傳入?yún)?shù)_"+parametersStr);//下面的是200行邏輯代碼,包括獲取訂單詳情和觸發(fā)短信通知...... ...... ...... ...... ...... ...... ...... ......

...... ...... ...... ...... ...... ...... ...... ......

...... ...... ...... ...... ...... ...... ...... ......

sw.Stop();

SysLogToFile.WriteAuditMessage("審批接口響應(yīng)時(shí)間"+ (sw.ElapsedMilliseconds /1000));stringreturncode, returnmsg;if(flag)

{ returncode="1"; returnmsg ="success"; }else{

returncode="2"; returnmsg ="fail";

}

context.Response.Write("{'returnCode':'"+ returncode +"','returnMsg':'"+ returnmsg +"','notifiedSMSContent':'"+ returnmsg +"'}");

}

發(fā)布后經(jīng)測(cè)試,發(fā)現(xiàn)3個(gè)節(jié)點(diǎn)的執(zhí)行時(shí)長(zhǎng)都長(zhǎng)達(dá)7~8秒。

那么,我們接下來(lái)就優(yōu)化程序吧,以期把duration降到最小。20分鐘后,我們把優(yōu)化的代碼再發(fā)布到服務(wù)器。發(fā)現(xiàn)處理時(shí)間已經(jīng)下降到了1~2秒。

然而,響應(yīng)時(shí)間已經(jīng)都優(yōu)化到2秒了,依然是3個(gè)服務(wù)器節(jié)點(diǎn)上都做了處理。這帶給我們的是一個(gè)更大的問(wèn)號(hào)。

轉(zhuǎn)機(jī)

為什么會(huì)有一個(gè)更大的問(wèn)號(hào)呢? 因?yàn)槲覀兙€上的系統(tǒng)都是分布式部署的,一個(gè)流程邏輯會(huì)涉及到多個(gè)系統(tǒng)之間的交互訪問(wèn)。 每個(gè)系統(tǒng)都是通過(guò)nginx做的3個(gè)節(jié)點(diǎn)的負(fù)載,并沒有出現(xiàn)過(guò)這種一個(gè)請(qǐng)求被3個(gè)服務(wù)器節(jié)點(diǎn)同時(shí)響應(yīng)的情況。

在這種情況,我的一貫做法是釜底抽薪。當(dāng)然,這次我依然堅(jiān)持釜底抽薪。

Q:釜底抽薪是什么方式呢?

A:把接口里的代碼都注釋掉。響應(yīng)時(shí)間應(yīng)該接近于0毫秒,難道還會(huì)出現(xiàn)3個(gè)節(jié)點(diǎn)都響應(yīng)的情況?

于是,我們注釋掉所有邏輯代碼,只保留了最后的輸出(context.Response.Write)語(yǔ)句,發(fā)布到服務(wù)器。

由于我們每次的測(cè)試步驟是:登陸saas,選擇航班,然后訂票下單,再看日志,這一系列的操作很耗時(shí)間,同時(shí)給線上系統(tǒng)帶來(lái)了很多無(wú)效訂單(垃圾數(shù)據(jù))。這次呢,我讓劉濤直接訪問(wèn)那個(gè)通知接口地址來(lái)測(cè)試性能。

轉(zhuǎn)機(jī)來(lái)了,在ie里直接訪問(wèn)那個(gè)接口時(shí),頁(yè)面直接拋出了大黃頁(yè),報(bào)“System.FormatException:輸入字符串的格式不正確”,是由主方法里最后的這條語(yǔ)句產(chǎn)生的:

context.Response.Write(string.Format("{'returnCode':'{0}','returnMsg':'{1}','notifiedSMSContent':'{2}'}", returncode, returnmsg, returnmsg));

即在string.Format方法里,你是不能亂用‘{’的~。

于是,修復(fù)后(改成字符串拼接了)再次上線。

再次訪問(wèn)那個(gè)接口,返回正常了。

這時(shí),最大的驚喜是,只有一個(gè)服務(wù)器節(jié)點(diǎn)處理了請(qǐng)求,短信重發(fā)3次的問(wèn)題得到了根治。

這時(shí),我才想起來(lái),nginx在接收到請(qǐng)求后,會(huì)分發(fā)給多個(gè)服務(wù)器節(jié)點(diǎn)中的一個(gè)節(jié)點(diǎn)來(lái)處理請(qǐng)求,但是,當(dāng)出現(xiàn)錯(cuò)誤響應(yīng)時(shí),nginx會(huì)自動(dòng)分開給另一個(gè)節(jié)點(diǎn)來(lái)處理,直到?jīng)]有節(jié)點(diǎn)可供分發(fā),即直到所有可用節(jié)點(diǎn)都被分發(fā)為止。(當(dāng)然,這可能是我們運(yùn)維對(duì)nginx的配置策略)

后續(xù)

我們解決完這個(gè)問(wèn)題,已經(jīng)午夜23:40,如釋重負(fù)的打車回家了。

在回去的路上,我的腦子像過(guò)電影似的過(guò)了一遍我們這一晚的處理過(guò)程。

l? 復(fù)雜的問(wèn)題,在你用心解決時(shí),往往產(chǎn)生自很簡(jiǎn)單的一行代碼。很戲劇性的是,這行代碼可能是你不經(jīng)意的疏忽寫錯(cuò)了,也可能是技能受限寫錯(cuò)的。如果,有做簡(jiǎn)單的測(cè)試,這樣的問(wèn)題完全是可以在開發(fā)時(shí)被發(fā)現(xiàn)的,而不至于花費(fèi)那么長(zhǎng)的時(shí)間來(lái)。

l? 另外,我又想到,審批系統(tǒng)調(diào)用接口時(shí)難道沒有判斷響應(yīng)結(jié)果就做下一步的重定向了?這個(gè)疑問(wèn)在第二天咨詢劉濤時(shí)得到了肯定。這樣的實(shí)現(xiàn)不夠嚴(yán)謹(jǐn),于是,我讓他加上對(duì)結(jié)果的判斷。 如果事先有這個(gè)判斷,那么,這樣的問(wèn)題在審批請(qǐng)求時(shí)就會(huì)被發(fā)現(xiàn)的,何至于經(jīng)歷那么漫長(zhǎng)的排查過(guò)程呢。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,534評(píng)論 19 139
  • 第一章 Nginx簡(jiǎn)介 Nginx是什么 沒有聽過(guò)Nginx?那么一定聽過(guò)它的“同行”Apache吧!Ngi...
    JokerW閱讀 33,018評(píng)論 24 1,002
  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,787評(píng)論 11 349
  • 《老男孩Linux運(yùn)維》筆記 隱藏Nginx軟件版本號(hào) 一般來(lái)說(shuō),軟件的漏洞都和版本有關(guān)。因此要盡量隱藏對(duì)訪問(wèn)用戶...
    Zhang21閱讀 3,882評(píng)論 0 28
  • 丙申中秋,依舊客居江南,本欲邀月同飲,卻逢一場(chǎng)秋雨,作此詞以抒懷。 江南客醉他鄉(xiāng)路,乍寒夜,西風(fēng)舞。 舊庭新顏花簇...
    南宮曉寒閱讀 464評(píng)論 0 1

友情鏈接更多精彩內(nèi)容