▶關於作者:張帆(Zachary,個人微訊號:Zachary-ZF)。堅持用心打磨每一篇高質量原創。 歡迎 訂閱我的 原創 微信公眾號(跨界架構師) 喲~ 和我一起嘮嘮嗑~
定期發表原創內容:架構設計丨分散式系統丨產品丨運營丨一些思考。
分散式系統關注點(19)——深入淺出「非同步」
如果第二次看到我的文章,歡迎「左側導航欄」或「文末」掃碼訂閱我個人的公眾號(跨界架構師)喲~
每週五早8點 按時送達到公眾號。當然了,也會時不時加個餐~
Z哥在前面的三篇文章裡和你一起聊了「高效能」主題下與「快取」相關的內容。這次和你來聊聊提高效能的另一個大招——「非同步」。
如果你已經對「非同步」有所瞭解的話,這次可以讓你有更深刻的理解。如果你對「非同步」的瞭解比較模糊的話,這次可以帶你一次性深入淺出。
「非同步」有啥用?
不管我們的思維模式也好,還是平時寫的最習慣的程式碼,其實都是以「同步」的方式在進行的。所以,「同步」方式用著也挺好,為啥要「非同步」呢?拿你平時去買奶茶、買咖啡的例子來說說你就明白了。
你應該有注意到,一般奶茶店都會分“點單區”和“取餐區”。
然後你去消費的時候都是在“點單區”選擇飲料然後付錢,在“取餐區”拿做好的飲料。其實這個過程就是「非同步」的,因為當營業員在做飲料的時候,你是可以去幹其它事的,比如在邊上開一局王者榮耀或者吃雞。而他們也可以繼續接受後面顧客的點單。
如果是「同步」會怎樣呢?就是你在點單區點好飲料之後,繼續排著隊乾等著營業員做好,直到營業員把飲料做好交給你之後,你就可以走人了,他再繼續服務後面的顧客。
很明顯,如果一個店鋪裡有2個或者2個以上的營業員,用這種「非同步」的方式“吞吐量“更高。
因為來買飲料的人時間是不規律的,可能有時候一下子來十幾個,可能有時候半小時都不來一個。那麼透過這種「非同步」的方式,雖然不能縮短製作飲料的時間,但是可以縮短人流量大的時候顧客的等待點單時間,讓顧客可以去做其它事。
其實軟體系統也是如此,如今我們程式所在的伺服器幾乎全部是多核多執行緒的。既然有多個“營業員”在,那麼透過「非同步」的方式儘可能的發揮多執行緒的作用,才是物盡其用的辦法,還能提升整體的效率。
不過,這事在軟體系統中要稍微複雜一些,要多考慮一下。因為執行緒的建立、銷燬、切換成本在很多時候甚至比獲得的收益還要高。所以, 只有 將「非同步」運用於「等待處理的時間」>「建立、銷燬、切換執行緒的時間」的場景下才有價值 。
要滿足這種場景的話,一般就是涉及到「I/O」處理的地方。比如磁碟I/O、網路I/O。
比如,一旦涉及到資料庫查詢或者RPC呼叫的時候,如果使用「同步」的方式通訊,發起一個呼叫後,呼叫方會阻塞自己並等待整個操作的完成(想象一下執行一條耗時10秒鐘的sql )。如果使用「非同步」通訊的話,呼叫方不需要等待操作完成就可以返回,甚至可能不需要關心整個操作完成與否。
特別對於如今的行動網路環境下,透過非同步的方式可以在很大程度上保證當網路很卡的時候APP上的操作依然是流暢的,不會出現卡機。
同步vs非同步
任何事物都是有利有弊的。「同步」可以立馬知道到底成功與否(比如做奶茶的時候營業員發現珍珠沒了,馬上就可以告訴你),而「非同步」不行(這個時候你可能去別的地方溜達了)。這也導致了在「非同步」環境下做「事務」的成本更高。
而且,在分散式系統中遍佈著RPC呼叫,如果是「同步」呼叫的話,還可以配合「短連結」做到對連線資源的用完即放。而「非同步」的話連線保持的時間要更長一些,至少要等到回撥觸發完成後才能釋放。
而且Z哥還要提醒你,在使用「非同步」的時候,有兩點特別容易被忽略。
-
發起請求的執行緒往往和接收響應的執行緒不是同一個,所以「執行緒上下文」是不連續的。( 當然可以透過做一些額外的編碼工作達到類似的效果 )
-
雖然請求的順序是由客戶端控制的,但是回撥的時候可能就不一定是按照請求時的順序進行的,像下圖這樣。
這麼看來,「同步」和「非同步」都可以透過「請求/響應」模型來完成。 但是,「非同步」在跨程式通訊中更合適抽象成「事件」來進行協作 。
透過「事件」進行「非同步」協作的話,客戶端不是發起請求,而是釋出一個「事件」,然後期待其他的協作者接收到該訊息,並且知道該怎麼處理它,客戶端不用關心其他協作者做了什麼,甚至也無需知道有哪些協作者存在。
基於「事件」的協作方式耦合度很低。客戶端釋出一個「事件」,但並不需要知道誰或者什麼會對此作出響應,這也意味著,你可以在不影響客戶端的情況下對該「事件」新增新的訂閱者。
總的來說,非同步雖然能提升效率,但是還是無法在所有場景使用它。在實際工作中,往往我們會同時運用「同步」和「非同步」,所以瞭解清楚它們之間的區別和優缺點是很有必要。
怎麼做非同步?
我們以一個電商APP中的“下單”場景來舉個例子。
在電商的業務場景中,下單最常見的就是以下幾個操作(順序隨便排的)。
-
扣減庫存
-
核銷優惠券
-
生成訂單
-
生成電子發票
這些操作都是由使用者在APP中點選“提交訂單”按鈕之後觸發的。
那麼首先來看APP這邊。一般我們的APP僅僅負責UI層面的展示控制,業務邏輯部分都是下沉到後端的API去做的。而APP和API之間大多都是以Http或者Tcp協議的形式進行通訊的,那麼在APP層面,我們只要 藉助一些非同步程式設計的類庫即可 (這方面不是特別專業,就不多BB了)。
然後到API層面,先給所有接收請求的Action加上非同步支援 ,java的話可以在註解處增加asyncSupported = true,.net的話增加aysnc關鍵字。如此一來,就是告訴程式所在的宿主(web server或者service)“我這個方法是支援非同步的,你接收到請求之後就不要阻塞了,去忙別的吧”。
接下來就輪到處理上面提到的電商下單場景中的4個操作了。理論上,這4個操作可以全部按「請求+回撥」的非同步模式進行,完全可行。這個過程其實有點像「並行」的意思,最終的處理完成時間是由最晚完成回撥的那個操作決定的。
但是,為了避免個別程式的意外情況導致最晚回撥的時間被拉的很長,我們就需要來考慮一下,那些無需即時知道甚至無需關心返回結果的操作可以透過「事件」的形式進行「非同步」。
比如,像“生成電子發票”這種操作,對當前這個業務場景來說並不需要實時知道它的返回結果。
雖然我們知道它的業務邏輯相比生成訂單這些更簡單,處理起來很快,但是一旦服務出現問題,那就不好說了。
題外話 :網路是不可信的,因為它容易受到攻擊、不穩定,所以在分散式系統中這些“意外情況”格外常見。多一個硬性的依賴,就多一份出錯的可能性。
如果沒有做好前面一些文章中提到的「高可用」保障(文末放傳送門,感興趣的可以看完這篇再去看)的話,一旦所依賴的服務出現問題就會被拖累,導致接收到最晚回撥的時間拉長,甚至由於未能及時回撥回來導致當前的處理無法繼續下去。
那像這樣的業務點,我們就可以透過「事件」的形式進行「非同步」處理,比如在生成完訂單之後發出一個“訂單被建立”的「事件」,然後由訂閱該「事件」的“生成電子發票服務“接收該「事件」並進行處理。如此一來,即提高了“提交訂單”時的處理效率,還使得“電子發票服務“的任何波動都不會影響到“提交訂單”操作的正常進行。
對這個「事件」的處理,你可以在程式中建立一個單獨的方法進行,它的入參是一個「事件」基類,返回值是void。具體的「事件」資料你可以選擇持久化到DB,也可以選擇投遞到MQ中。大致是下面這樣的程式碼
void SendEvent(BaseEvent event){ //投送到DB或者MQ; } class BaseEvent{ DateTime OccurredTime; } class OrderCreated extend BaseEvent{ Order order; Invoice invoice; ... }
可能你會問事件處理失敗了怎麼辦?甚至做持久化和投遞到MQ的s以後就異常了咋辦?可以轉去看之前的文章《 分散式系統關注點——「共識」的兄弟「事務」 》,以及文末的高可用系列文章。
最後,當你在使用非同步的時候,還有一項工作要做,雖然是輔助性的,但是很重要。
就是需要引入一個全域性唯一標識將整個非同步的請求鏈路“串“起來,否則排查問題的時候夠你頭疼的,完全分不清楚哪是哪 。如果條件允許,可以再引入一個日誌聚合系統。比如ELK全家桶,讓你可以更高效的篩選日誌資訊。
總結
好了,我們一起總結一下。
這次呢,Z哥先和你聊了下「非同步」的意義,以及它是如何來提升效能的。
然後和你聊了一下「非同步」的一些弊端和常見的運用方式。
最後以一個電商下單的例子梳理了一下做「非同步」的思路。
希望對你有所啟發。
相關文章:
作者:
Zachary
出處: https://www.cnblogs.com/Zachary-Fan/p/async.html
如果你喜歡這篇文章,可以點一下左下角的「 大拇指 」。
這樣可以給我一點反饋。: )
謝謝你的舉手之勞。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31544142/viewspace-2644681/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 分散式系統關注點(20)——阻塞與非阻塞有什麼區別?分散式
- 分散式系統關注點——初識「高可用」分散式
- 分散式系統關注點——想通關「限流」?只要這一篇分散式
- 分散式系統關注點——如何去實施「負載均衡」?分散式負載
- 分散式系統關注點——360°全方位解讀「快取」分散式快取
- 分散式系統關注點——先寫DB還是「快取」?分散式快取
- 分散式系統關注點——「高內聚低耦合」詳解分散式
- 分散式系統關注點(22)——360°的全方位監控分散式
- [分散式]Nginx系列文章---深入淺出Nginx分散式Nginx
- [分散式]分散式計算系統淺析分散式
- 深入淺出Websocket(二)分散式Websocket叢集Web分散式
- 深入理解分散式系統分散式
- 分散式 - 分散式系統的特點分散式
- 分散式系統關注點(18)——「快取穿透」和「快取雪崩」到底啥區別?分散式快取穿透
- 得物技術淺談深入淺出的Redis分散式鎖Redis分散式
- 分散式系統關注點——僅需這一篇,吃透「負載均衡」妥妥的分散式負載
- 分散式系統關注點——99%的人都能看懂的「熔斷」以及最佳實踐分散式
- 19種分散式系統設計模式 - Nishant分散式設計模式
- 【深入淺出ES6】非同步Promise非同步Promise
- 你也許不知道的Vuejs – 深入淺出響應式系統VueJS
- 對於同步、非同步、阻塞、非阻塞的幾點淺薄理解非同步
- 什麼是分散式系統!以及分散式系統架構的優缺點!分散式架構
- 短影片直播系統為什麼需要分散式部署,淺談分散式部署分散式
- 深入淺出百億請求高可用Redis(codis)分散式叢集揭祕Redis分散式
- 【深入淺出ES6】函式函式
- 深入淺出FE(十四)深入淺出websocketWeb
- [譯] 深入淺出 JavaScript 關鍵詞 -- thisJavaScript
- 分散式系統分散式
- 【深入淺出 Yarn 架構與實現】6-3 NodeManager 分散式快取Yarn架構分散式快取
- 分散式系統2:分散式系統中的時鐘分散式
- [分散式][分散式鎖]淺談分散式鎖分散式
- 分散式系統:系統模型分散式模型
- 在分散式系統中使用非同步管道建立實體分散式非同步
- 分散式系統–>(關於系統應用的基本概念)分散式
- 淺談大型分散式Web系統的架構演進分散式Web架構
- 分散式系統(三)——分散式事務分散式
- JavaScript深入淺出非同步程式設計二、promise原理JavaScript非同步程式設計Promise
- JavaScript深入淺出非同步程式設計三、async、awaitJavaScript非同步程式設計AI