此文已由做者肖凡受權網易雲社區發佈。
html
歡迎訪問網易雲社區,瞭解更多網易技術產品運營經驗。數據庫
最近接了一個看上去很小,很容易實現的需求,然而在作的過程當中發現有些問題若是思考不全就很容易致使問題,這裏給你們分享下。緩存
需求背景安全
考拉的訂單風控主要是對接杭研的反垃圾服務,訂單支付完成後會異步抄送給反垃圾那邊。若是訂單的支付方式是網易寶支付,則網易寶也會作風控檢測。那麼就須要一個地方來聚合反垃圾和網易寶的風控結果,當兩方都認爲經過時才通知主站訂單放行,不然要等待另外一方的通知。併發
需求描述異步
聚合反垃圾和網易寶風控結果,將最終結果通知主站。分佈式
這個需求看上去是很是簡單的,不就是聚合下兩邊的通知麼,找個地方存下來不就行了嘛,so easy!而後開始碼代碼了。首先想到要在當前的風控系統的通知接口上增長兩個字段,一個用來表示調用方,1表示反垃圾,2表示網易寶;一個是用來表示支付方式,1表示網易寶支付,2表示非網易寶支付,而後就是要須要將這個通知存下來,緩存確定是不行的,數據可能丟掉,那就放在rds裏面,新建一張表,包括訂單id、反垃圾通知狀態、網易寶通知狀態、是否已通知主站等字段,而後風控通知來了以後就去表裏面看看是否另外一方的通知已經來過,若是沒有就新加一條記錄,最後根據邏輯決定通知主站訂單放心/攔截/關單。整個代碼邏輯大概就是這樣了,可是這裏面會有不少問題,下面我深刻這個需求給你們分享下會碰到的問題。單元測試
問題1:若是兩個通知同時到來怎麼辦?測試
初始的代碼邏輯是這樣:來了一個通知以後,根據訂單id去表裏查數據,若是不存在,則增長一條;若是存在,則判斷另外一方的通知是否已經到達,已到達則更新數據,標記該訂單已通知主站。這裏很明顯有一個競態條件:若是兩個通知併發的到達,都會發現數據庫裏面沒有數據,都會去插入數據,而訂單id是惟一索引,那麼這裏就會報錯了。如何解決呢?常見的方式有分佈式鎖、額外的引入隊列,簡單點的可使用nkv的原子操做。考慮到這裏併發的可能性不大,直接catch了重複插入的exception,而後catch塊裏面進行更新操做。.net
問題2:若是有一個通知一直不來怎麼辦?或者兩個通知都不來呢?
任何第三方系統對於咱們來講都是不可靠的,設計代碼邏輯的時候是必定須要考慮第三方系統異常的狀況的。針對這種狀況,須要根據訂單支付時的落庫信息去定時反查反垃圾的訂單狀態而後通知主站。這裏引入的定時任務也是帶來額外的風險,好比如何保證定時任務必定執行?目前是使用考拉定時任務中間件kschedule來保證的。
問題3:若是通知重複了怎麼辦?
好比同一個訂單,反垃圾風控結果是要關單,若是併發的通知了兩次,程序會不會出問題?這裏就須要考慮接口自己的冪等性。若是一個訂單已經關單,再次調用關單時,必須返回關單成功,返回的結果和首次關單時的結果嚴格一致,不然可能就會致使各類數據不一致狀況出現
問題4:若是消息亂序了怎麼辦?
好比正常狀況是,一筆訂單先通知攔截,訂單進入審覈狀態,再通知訂單審覈經過,訂單放行。假如如今收到的通知順序是先收到審覈經過,再收到攔截,那怎麼處理呢?這裏可使用狀態機來保證狀態的流轉,像前面提到的這種狀況,預先定義好已審覈經過的訂單,再次收到攔截請求時不予處理。
問題5:數據庫的操做和dubbo接口的調用怎麼保證事務完整性?
當考拉風控系統收到反垃圾通知時,須要通知主站,同時要更新數據庫,表示該訂單已通知,那如何保證通知主站和數據庫的更新在一個事務裏面呢?這裏可使用考拉的分佈式事務中間件DTS,不過有點過重了。考慮到第三方的通知是會有重試的,而且咱們的接口都是冪等的,能夠先通知主站,再更新數據庫,若是都成功則返回反垃圾一個正確的code,不然有任何異常就返回一個error值。反垃圾或網易寶那邊發現是error的就重試,這樣就能夠達到最終一致狀態。
問題6:要是反垃圾一直掛了咋辦?要是反垃圾瘋狂的調用你的接口怎麼辦?怎麼快速發現第三方系統的問題?
代碼邏輯是寫完了,可是若是反垃圾因爲本身的bug掛了,致使有風險的訂單一直沒法關閉,如何快速發現呢?那就要加監控了。除了監控異常的狀況之外,正常的調用量也須要監控。什麼意思呢?咱們通常監控會覆蓋到一些程序中的異常,好比調用主站關單接口失敗了,會在catch裏面捕獲到以後給哨兵發一個監控信息,可是這樣會漏掉一些狀況,好比反垃圾掛了,一成天一個通知都沒有,或者反垃圾邏輯出錯,瘋狂的關閉大量的訂單,這些調用量的異常也須要及時的發現。進一步的,接口要作好保護,防止被第三方調掛了。
問題7:如何平滑的上線?
這是一個兼容性問題,也是比較容易忽視的一個問題。舉個例子:關單接口是以前就存在的,一個http接口,提供給反垃圾使用,接口的參數包括訂單id和關單緣由等。如今該接口加了一個參數payType用來表示支付類型,Integer類型。根據約定,這個參數是必傳的,因此代碼裏面會判斷,若是該字段爲空,則直接返回錯誤碼。這麼作是沒有什麼問題的,也是一種保護接口的方式,可是這裏就有一個兼容性問題了:當你的代碼先上線後,該字段確定是空的,這會致使全部的關單所有異常,這一點不能忽視了。
一個小需求,看似很簡單,可是想要作到沒啥問題仍是須要深刻的思考的,業務邏輯代碼只是一部分。特別是和第三方有交互的時候,必定要記住:全部的第三方都是不可控的,它們有可能掛掉、有可能不停的調用、有可能不按照約定來,任什麼時候候你都要保證本身的系統處於一個安全的環境中。這些邏輯有些是能夠在統一網關中解決掉,有些只能靠本身的接口解決。
3天后。。。
說了這麼多,然而並無什麼卵用,仍是出了問題。。。
什麼問題呢?反垃圾調用關單接口失敗。如何致使的呢?不是思考了這麼多麼,爲什麼毛用沒有?那隻能說明我思考的還不夠多。。
這個問題致使的緣由後來也查清楚了,是反垃圾在獲取payType這個參數的時候報了空指針異常,致使請求根本就發到我這邊。考拉這邊的商戶類型有新增,而反垃圾那邊沒有同步最新的商戶類型。此次作payType映射的時候,碰到新的商戶類型就報錯了。以前沒問題是由於沒有使用到這個payType。那說好的監控呢?爲毛沒監控到?額,這是由於此次上線是反垃圾先上線,咱們尚未上線。。。
那麼最後再加一個思考:對於接口修改的內容,雙方必定要多溝通肯定清楚,任何一個小問題均可能會致使線上事故。聯調的時候須要覆蓋儘量多的場景,而後這始終是會有遺漏的,因此單元測試趕忙跟上吧。
網易雲免費體驗館,0成本體驗20+款雲產品!
更多網易技術、產品、運營經驗分享請點擊。
相關文章:
【推薦】 知物由學 | 廣告欺詐:如何應對數字廣告裏分羹者?
【推薦】 從DevOps到CloudNative,應用上雲姿式全解鎖
【推薦】 一個內部增加案例的分享