微服務中的冪等設計

首發公衆號:二進制社區,轉載聯繫:binary0101@126.com冪等本來是數學運算裏的概念,維基百科對其的定義爲:

  1. 在某二元運算下,冪等元素是指被本身重複運算(或對於函數是爲複合)的結果等於它本身的元素。
  2. 某一元運算爲冪等的時,其做用在任一元素兩次後會和其做用一次的結果相同。例如,高斯符號即是冪等的。

衍生到微服務領域,冪等指的是使用相同的參數屢次調用相同的API,對後端產生的影響是一致的,許多人理解爲屢次調用返回的結果是同樣,這種觀點是錯誤的,連最基本的查詢接口也不多是屢次查詢返回一樣的結果,冪等的側重點是對後端的影響,並不關心返回的數據。冪等是全部客戶端-服務端系統都須要考慮的問題,不管你是C-S仍是B-S模式,但在B-S模式下,這個問題更爲突出,尤爲是近幾年流行的微服務架構下,這是由於在微服務模式下,單個應用簡單,但服務總體更爲複雜,完成一個功能須要走較長的調用鏈,總體鏈路可能由不一樣的語言不一樣框架實現,每一個微服務爲了保證服務的質量,經常會對一些不肯定的因素(如:網絡異常、資源耗盡等)致使的問題進行容錯處理,典型措施就是重試。微服務中的冪等設計上圖是互聯網應用中很是精簡的調用流程,應該說許多公司的鏈路遠比這個複雜,流程中的每一個環節均可能出現重試:前端

  1. 用戶可能會頻繁點擊按鈕
  2. 瀏覽器在網絡不穩定狀況下重發請求
  3. 反向代碼因爲網關響應超時或者網絡異常重發到另一個實例
  4. 網關一樣可能由於超時/出錯自動調用另一個service

若是請求只涉及到資源查詢,顯然調用屢次並不會對後端數據發生更改,因此查詢類的API自然就是冪等的,但若是這是一個創單接口,咱們就不得不考慮冪等的問題,創單過程當中會涉及到庫存扣減、餘額扣減、訂單數據入庫、消息通知等等修改後端資源的操做,爲了防止這類請求重複提交到後端,前端頁面一般會在用戶點擊提交後禁用按鈕,後端成功後清空表單跳轉到提示頁面,對於後端服務來說,一般會採用如下方式實現冪等:java

  1. 數據庫惟一約束:經過業務設計,肯定幾個能夠惟一識別訂單字段設置爲惟一索引,這種方式好處就是簡單,代碼基本不作改動,缺點也很明顯,因此的重複請求要穿透整個鏈路,一直到數據庫才能判重,對鏈路上資源消耗很大,會給數據庫帶來巨大的壓力,即便服務擴容,TPS也很難上去
  2. MVCC:多版本併發控制方式,操做時帶上版本號:update t1 set x=y ,version=version+1 where version=xxx,優勢是提高了併發響應能力,實現也簡單,缺點是隻適用更新接口,仍是會將重複請求達到數據庫,數據庫壓力較大
  3. 狀態機機制,本質上是MVCC方式的變種:訂單有多個業務狀態,每次操做數據會帶上一個狀態,只有在上一個狀態匹配的狀況下會更新數據,優缺點和MVCC大同小異,但這種機制解決了插入的問題,不只僅適用在更新接口
  4. Token機制,這是一種很是高效的冪等機制,在性能和功能上都達到很好的效果,接下來咱們就詳細討論下這種機制的實現

Token機制的核心就是要求客戶端的每次請求裏必須攜帶一個UUID,產生UUID的算法不少,如:雪花算法,ObjectID,經常使用的開發語言也有對應的實現,即便client生成UUID有困難,也能夠調用ID生成器預先生成一批緩存到本地,這裏就不一一展開。有了這個UUID,能夠在多個環節實現攔截,並且這種判斷是很是高效的,幾乎都是O(1)的時間複雜度,遠比數據庫惟一約束判斷快,譬如在nginx裏,咱們可使用lua獲取到請求裏的UUID,將該UUID放入leveldb、redis、memcache,下次請求時判斷該值是否存在,存在直接返回錯誤不然放行,若是不是使用nginx或者實施上述方案有困難,能夠在網關層實現,對於java語言(其餘語言也相似),能夠作到透明化處理:在網關裏註冊Idempotentfilter,該filter提取UUID,一樣經過leveldb、redis、memcache等高速緩存判斷是否能夠放行,顯然token方案能夠儘早發現和攔截訂單,大大下降資源消耗,減小數據庫壓力,對業務也是透明無浸入,但只依賴token機制顯然是有缺陷的:當緩存服務出錯ldb文件丟失,redis數據意外清空,memcache意外重啓等,會致使咱們的UUID標誌丟失,這時就須要數據庫來兜底,數據庫的惟一約束是不可或缺的,當出現這些極端狀況時,即便請求達到DB,也不會形成數據重複。有冪等需求的接口,建議採用token機制實現高效的排除重複請求,固然最終落地式須要結合具體的業務場景.nginx

更多深度文章,關注:二進制社區

相關文章
相關標籤/搜索