下單穩(wěn)定性治理
1、為什么寫這篇文章
在工作期間,筆者有幸參與了下單鏈路的開發(fā)、維護(hù)工作,在這期間有經(jīng)歷下單從0到1的搭建,也有隨著業(yè)務(wù)發(fā)展不得不進(jìn)行系統(tǒng)重構(gòu)的經(jīng)驗(yàn)?!疤峤挥唵巍边@一詞大家應(yīng)該都是再熟悉不過了,不管你是不是軟件研發(fā)人員,還是普通使用電商APP購買商品的用戶,只要你在購買商品時(shí)必然會(huì)遇到。既然“提交訂單”這么頻繁的被使用到,作為任何電商APP來說,那么它的穩(wěn)定性就尤為重要。
那么站在技術(shù)視角看下單鏈路,會(huì)發(fā)現(xiàn)幾個(gè)特點(diǎn)
- 高QPS/TPS,流量大
- 訂單數(shù)據(jù)正確性要求極高
- 監(jiān)控告警時(shí)快速定位能力
- 結(jié)算頁到訂單創(chuàng)建成功的所見即所得
- 易被惡意流量刷單
- 依賴下游服務(wù)非常之多
- 業(yè)務(wù)邏輯很復(fù)雜
本篇文章就挑幾個(gè)在日常研發(fā)中可能會(huì)遇到比較明顯的問題,以及是怎么進(jìn)行應(yīng)對(duì)的。
2、可能遇到的問題
2.1 線上告警頻繁,精準(zhǔn)定位問題耗時(shí)較長
告警機(jī)制,這個(gè)大家最熟悉不過的了,作為技術(shù)人的對(duì)這可以說是又愛又恨吧。即討厭線上頻繁告警的打擾,又擔(dān)心真正發(fā)生告警時(shí)的定位難。常見的主流監(jiān)控,Zabbix、Promethues、Open-Falcon等主要監(jiān)控的指標(biāo)還是以應(yīng)用維度為主,主要監(jiān)控指標(biāo)如下。
- Dubbo接口:請(qǐng)求量、耗時(shí)、異常量。
- JVM :GC次數(shù)、GC耗時(shí)、各個(gè)內(nèi)存區(qū)域的大小、當(dāng)前線程數(shù)、死鎖線程數(shù)。
- 線程池:活躍線程數(shù)、任務(wù)隊(duì)列大小、任務(wù)執(zhí)行耗時(shí)、拒絕任務(wù)數(shù)。
如圖,類似于這種告警應(yīng)該是比較熟悉的。那么這里的問題也很明顯,下游接口異常到底影響的是哪個(gè)鏈路呢?針對(duì)這種特定業(yè)務(wù)場景,如訂單結(jié)算頁、提交訂單,這類接口級(jí)別的監(jiān)控又該怎么做呢?那首先簡單介紹下在一次下單請(qǐng)求中可能遇到的問題
- 下游接口調(diào)用告警
- 強(qiáng)依賴接口和業(yè)務(wù)可降級(jí)接口,怎么進(jìn)行區(qū)分?
- 當(dāng)告警來了,怎么確認(rèn)是下單鏈路所依賴的接口呢?
- 下游接口告警了,是預(yù)期內(nèi)的業(yè)務(wù)異常還是非預(yù)期內(nèi)的呢?
- 接口rt&接口QPS抖動(dòng)告警
!由于熱門商品、大促等活動(dòng)節(jié)日的存在,所以下單鏈路會(huì)經(jīng)常出現(xiàn)這類告警
- AVG RT的下降,怎么識(shí)別是否正常?
- QPS的突然升高,升高的原因是啥呢?到底是下單鏈路阻塞了導(dǎo)致用戶一直重試,還是發(fā)生了搶購呢?
- 依賴的中間件發(fā)生抖動(dòng)告警
- 怎么快速感知是MQ、Redis、DB等的異常?
- 應(yīng)用自身出現(xiàn)異常告警
- 普通業(yè)務(wù)異常:例如當(dāng)前APP版本不支持XXX新業(yè)務(wù),非法請(qǐng)求核心參數(shù)缺失
- 非預(yù)期異常:新上線的業(yè)務(wù)代碼整出了異常導(dǎo)致下單阻斷
- 怎么區(qū)分普通業(yè)務(wù)異常和非預(yù)期異常?
普通業(yè)務(wù)異常:例如當(dāng)前APP版本不支持XXX新業(yè)務(wù),非法請(qǐng)求核心參數(shù)缺失
非預(yù)期異常:新上線的業(yè)務(wù)代碼整出了異常導(dǎo)致下單阻斷
2.2 當(dāng)購買期間商品信息發(fā)生變更,怎么保障用戶的購買體驗(yàn)?zāi)?/h3>
在用戶購買東西時(shí),首先會(huì)看到訂單結(jié)算頁面,這個(gè)上面會(huì)展示商品價(jià)格,售后保障,到貨時(shí)效,優(yōu)惠信息等,這時(shí)用戶在確認(rèn)條款后會(huì)提交訂單,那么在訂單生成后訂單詳情看到的理論是需要和在結(jié)算頁看到的信息是完全一致的。但是由于結(jié)算頁和提交訂單是分開的請(qǐng)求,那么這個(gè)時(shí)間GAP以及實(shí)現(xiàn)差異終究可能會(huì)帶來不一致的情況發(fā)生。如果是普通庫存的話,給用戶直接重新展示訂單結(jié)算頁也還行,要是搶購商品的話,那這個(gè)體驗(yàn)就會(huì)有比較大的影響。
2.3 依賴方數(shù)據(jù)返回不合法,該如何及時(shí)感知
訂單的數(shù)據(jù)是相當(dāng)復(fù)雜的,需要依賴商品、庫存、營銷、商家等數(shù)據(jù)信息,不同的業(yè)務(wù)場景對(duì)生成的訂單數(shù)據(jù)就會(huì)存在一定的要求。
那么這件事情的必要性,就在于可以在系統(tǒng)上線之前,通過回歸測試及流量回放驗(yàn)證來及時(shí)發(fā)現(xiàn)是依賴方接口導(dǎo)致的問題還是自身系統(tǒng)代碼bug帶來的影響。
3、解決方案
那么問題來了,既然決定好好治理,那么怎么治理呢?怎么以最小的人力、技術(shù)成本實(shí)現(xiàn)這些治理呢?這個(gè)時(shí)候大量參考了現(xiàn)在同行業(yè)內(nèi)針對(duì)下單場景穩(wěn)定性相關(guān)的方案?,F(xiàn)在就逐一介紹以上問題最終選擇的解決方案。
3.1 自定義實(shí)現(xiàn)告警機(jī)制的基礎(chǔ)日志數(shù)據(jù)埋點(diǎn)
針對(duì)接口級(jí)的定制化告警,采用了自定義日志埋點(diǎn)的方式,格式如下:
{current_time}|{trace_id}|{span_id}| {function_name}|{rt}|{error_code}|{error_message}|{user_id}
- function_name:用來具體區(qū)分哪個(gè)接口
- error_code:接口錯(cuò)誤碼,用來唯一標(biāo)識(shí)接口異常原因,重點(diǎn)就是這個(gè),這個(gè)指標(biāo)數(shù)據(jù)輸出的精細(xì)程度決定了定位問題的速度
- rt:接口響應(yīng)時(shí)間
這里簡單畫個(gè)圖,直觀的體現(xiàn)下需要關(guān)注下單鏈路中哪些指標(biāo)
現(xiàn)在介紹一下每個(gè)指標(biāo)的作用:
- 網(wǎng)關(guān)QPS:觀察C端的實(shí)時(shí)入口流量
- 自身服務(wù)QPS:觀察到達(dá)服務(wù)本身的流量
網(wǎng)關(guān)QPS > 自身QPS,可以考慮是否網(wǎng)關(guān)側(cè)發(fā)生了限流
當(dāng)自身QPS下降過高
網(wǎng)關(guān)QPS沒什么波動(dòng),那么這個(gè)時(shí)候考慮網(wǎng)關(guān)問題
網(wǎng)關(guān)QPS也同步下降,前置導(dǎo)購鏈路流量問題,如商詳/購買浮層 是否發(fā)生阻斷性異常
- 自身業(yè)務(wù)異常:輸出下單阻斷的業(yè)務(wù)原因,又稱為預(yù)期內(nèi)異常
- 自身其它運(yùn)行時(shí)異常:如NPE,稱為非預(yù)期內(nèi)異常,此時(shí)錯(cuò)誤碼會(huì)統(tǒng)一輸出SYS_ERROR,一般此類會(huì)重點(diǎn)關(guān)注
- 下游接口RPC異常:此時(shí)會(huì)輸出是下游哪個(gè)接口導(dǎo)致的阻斷,如
- 商品查詢接口超時(shí) -> QEURY_SKU(RPC_TIMEOUT)
- 用戶接口查詢網(wǎng)絡(luò)異常 -> QUERY_USER(NETWORK_EXCEPTION)
- 下游接口業(yè)務(wù)異常:如
- 優(yōu)惠已失效 -> CONSUME_DISCOUNT(INVALID),這里會(huì)通過識(shí)別下游接口返回的code碼來區(qū)分不同的業(yè)務(wù)異常,所以在日常需求中要求下游接口提供方確保返回碼的含義就是這個(gè)原因
- 返回了未約定的code碼,統(tǒng)一會(huì)返回如XXX(BIZ_ERR),看到此類錯(cuò)誤碼的時(shí)候,就會(huì)及時(shí)反饋給下游服務(wù)Owner去跟進(jìn)這個(gè)問題
- 中間件訪問異常:
- SQL執(zhí)行異常
- 網(wǎng)絡(luò)連接RST異常
- 自身服務(wù)接口AVG RT/SUCCESS RT
- 這里主要說一下SUCCESS RT,這個(gè)指標(biāo)是可以最準(zhǔn)確的反饋出最近RT是否存在波動(dòng)
- 自身服務(wù)接口AVG QPS/SUCCESS QPS
- 這里的success qps很重要,當(dāng)發(fā)生搶購的時(shí)候,整體QPS會(huì)大幅上升,這個(gè)時(shí)候可以SUCCESS QPS來判斷當(dāng)前成單量是不是穩(wěn)定
如果是淺庫存搶購,這個(gè)指標(biāo)不會(huì)有太大波動(dòng)
接口被刷了,這個(gè)指標(biāo)也不會(huì)有太大波動(dòng),且會(huì)出現(xiàn)OPERATION_TOO_FREQUENTLY頻次限流錯(cuò)誤碼
!通過將接口每次請(qǐng)求的埋點(diǎn)日志輸出到指定文件中,后續(xù)經(jīng)過監(jiān)控組采集以及分析得到了如下幾個(gè)主要的大盤:
1)確認(rèn)訂單&創(chuàng)建訂單錯(cuò)誤碼大盤
從圖中可很直觀的發(fā)現(xiàn)當(dāng)前有哪些原因?qū)е碌南聠问?,如版本過低限制、庫存售罄、下單頻次過高等原因,這樣就能很直觀的發(fā)現(xiàn)
- 從異常名可以看出是有很明顯業(yè)務(wù)語義的,這樣便于大家理解
- 針對(duì)下游接口調(diào)用,會(huì)輸出具體某個(gè)接口(也可以給對(duì)應(yīng)接口定義別名)的某個(gè)類型錯(cuò)誤,如優(yōu)惠核銷的超時(shí)、優(yōu)惠已失效、優(yōu)惠已使用
另外還設(shè)計(jì)了基于機(jī)器IP的過濾,這種做法的好處是,在發(fā)布過程中,如果下單出現(xiàn)了任何阻塞性異常,都可以很快的感知到,從而可以快速做到SOP響應(yīng)處理。
對(duì)于鏈路中的業(yè)務(wù)弱依賴接口,這里不會(huì)有錯(cuò)誤碼體現(xiàn),這里依然還是借助于監(jiān)告警機(jī)制。
2) QPS&RT指標(biāo)數(shù)據(jù)
這里主要日常監(jiān)控觀察主要會(huì)注重成功量QPS,特別是發(fā)布期間完全可以依賴于這些指標(biāo)數(shù)據(jù)。例如發(fā)布期間這個(gè)時(shí)候在搶購,有了這個(gè)就能做到心中有數(shù)了。這里簡單說明一下成功量就是接口業(yè)務(wù)執(zhí)行成功的含義。
3)告警機(jī)制
有了如上的這些指標(biāo)數(shù)據(jù),那么基于這些做告警機(jī)制就成了順理成章的事情啦,目前已經(jīng)有如下指標(biāo)告警:
- 錯(cuò)誤碼環(huán)比漲幅超指定閾值
- 接口RT環(huán)比漲幅超指定閾值
- 接口成功量QPS環(huán)比下跌超過指定閾值
然后再將這些告警機(jī)制接入飛書、短信等通知,那么哪怕是在周末外出游玩的時(shí)間,有任何下單鏈路的異常告警,只需要打開手機(jī)看一眼就能快速定位到問題的根因所在了,豈不美哉?
以上就是針對(duì)下單告警機(jī)制的精細(xì)化處理了,除此之外,有了這些數(shù)據(jù)后,也對(duì)其它一些指標(biāo)數(shù)據(jù)也進(jìn)行了完善,如:
- 高頻訪問用戶
- 不同入口的實(shí)時(shí)下單量
- 當(dāng)前熱門購買商品
3.2 基于版本號(hào)的商品信息&數(shù)據(jù)一致性校驗(yàn)
1) 商品價(jià)格變更
商品改價(jià)這個(gè)在電商中應(yīng)該是比較常見的,那么如果是在秒殺時(shí)改價(jià),那么此時(shí)提示用戶“商品價(jià)格”變更可能對(duì)用戶的體感就沒那么好。針對(duì)這類問題可以采用商品信息+版本號(hào)機(jī)制。
用戶在訂單結(jié)算頁看到的商品數(shù)據(jù)版本會(huì)交由客戶端攜帶至提交訂單,此時(shí)提交訂單可以校驗(yàn)該版本的生效時(shí)間是XX秒內(nèi),確保這個(gè)時(shí)間內(nèi)訂單提交不受改價(jià)影響,這樣可以給到用戶一個(gè)較好的購買體驗(yàn)。這個(gè)XX時(shí)間就需要業(yè)務(wù)來進(jìn)行權(quán)衡了。
2)數(shù)據(jù)一致性校驗(yàn)
通過以上的UML圖可以看到,由于確認(rèn)訂單和創(chuàng)單是兩次請(qǐng)求,那么保證數(shù)據(jù)防篡改是第一要求,而且有了這個(gè)驗(yàn)簽機(jī)制后,用戶自己通過簡單傳參刷創(chuàng)單接口就變得更加困難了。對(duì)于迭代版本中新增生成sign的參數(shù),這邊主要采用version版本的方式,不同的version對(duì)應(yīng)參與生成version的參數(shù)有所不同。
- version1,參數(shù) a、b、c
- version2,參數(shù)a、b、c、d
有了防篡改的保障后,那么接下來就只需要在下單資源扣減之前,針對(duì)這些核心數(shù)據(jù)進(jìn)行一致性校驗(yàn)即可,如訂單金額、展示給用戶的售后標(biāo)簽等等。這樣的話在出現(xiàn)不一致時(shí)可以給到用戶友好的提示,并且對(duì)可以及時(shí)進(jìn)行告警通知。
3.3 訂單數(shù)據(jù)正確性校驗(yàn)&及時(shí)告警機(jī)制
一致性校驗(yàn)節(jié)點(diǎn)旨在創(chuàng)單落庫節(jié)點(diǎn)前給恒久不變的規(guī)則(如:訂單支付金額 = 應(yīng)收金額 - 優(yōu)惠 )提供下單前的兜底校驗(yàn)及可選告警措施。不太適合落地多變的規(guī)則。如果是多變規(guī)則需要寫到對(duì)應(yīng)業(yè)務(wù)模塊以異常形式告出。大家自行判斷所屬業(yè)務(wù)屬于哪一種。
訂單數(shù)據(jù)完整性校驗(yàn)致力于保障訂單在整個(gè)生命周期中數(shù)據(jù)的正確性。為用戶打造一站式的校驗(yàn)、預(yù)警解決方案。提供以下能力:
- 可插拔式接入
- 場景定制化
- 動(dòng)態(tài)降級(jí)
- 規(guī)則、預(yù)警可擴(kuò)展
- 統(tǒng)一流程處理
適用的場景:
- 商家地址返回手機(jī)號(hào)存在掩碼問題,必要數(shù)據(jù)缺失
- 優(yōu)惠接口在某種特定業(yè)務(wù)場景下未返回對(duì)應(yīng)的優(yōu)惠信息
- 訂單金額計(jì)算是否一致與用戶看到的一致
4、雨過天晴后的??
1)基于錯(cuò)誤碼大盤及監(jiān)控機(jī)制的問題快速定位
- 核心接口全局監(jiān)控,高靈敏度感知任何阻塞下單的問題
- 監(jiān)控機(jī)制實(shí)時(shí)告警
2)下單鏈路一致性機(jī)制保障,所見即所得
3)創(chuàng)單數(shù)據(jù)正確性兜底校驗(yàn)
5、總結(jié)
在下單的穩(wěn)定性治理過程中,從面對(duì)線上告警的盲目無措,逐漸演進(jìn)到面對(duì)日常迭代變更、突發(fā)流量場景的鎮(zhèn)定自若。在日常工作中,持續(xù)關(guān)注、發(fā)現(xiàn)線上潛在的問題以及不合理的設(shè)計(jì),然后盡量通過合理機(jī)制&實(shí)現(xiàn)來進(jìn)行保障。作為一名研發(fā)人員,不能確保不犯錯(cuò),但能盡最大努力及時(shí)發(fā)現(xiàn)錯(cuò)誤,敬畏生產(chǎn)。幾套打完收工,可以手握小茶壺,靜看風(fēng)波了。