手機‧電玩
Design Pattern: Observer / Subscribers - Push vs Pull
相信一般入行IT不久的朋友,都會知道IT系統更新時,有推和拉(push、pull)兩種方式。特別是Programer,對於觀察者模式又或者是訂閱者模式(Observer / Subscriber )會有更多的使用經驗,例如:OS programing要處理event bus,Mobile App要做的推送通知(Push Notification)。
但一般來說,很少人討論推和拉(push、pull)的問題,筆者就著一些踩過的坑來說說差異。
首先,在一個通訊相對穩定的系統中,Push、Pull都很好用。例如同一個OS內,它的socket或pipe可以看作很穩定,可以假設那些要廣播的消息可以正常傳遞出好。但好用歸好用,這個模式對於越來越複雜的交互系統都有一個無法明確處理的問題:怎樣去處理觀察者/訂閱者自己的操作失敗問題。
對於非IT行業的讀者來說,只要你接觸過手機即時聊天程式(IM,如whatsapp, wechat, facebook messenger)應該都會遇到一個問題就是:你收到OS提示通知,但打開聊天程式卻看不到新的對話內容;又者是你連續收到多個同一個內容的提示通知,那怕你已經讀過了。這些都代表了,手機端當初時沒有好好即時回應是否已經操作成功,不需要重複通知的問題。有可能是手機當時掛了,也有可能是網絡不太好。
上述的例子,對一般人來說,可能影響不太。因為重複收到訊息,又或是漏了訊息,也不會怎樣。但對於業務系統,例如定期收費,多收一次又或是少收一次,都會引起某部份關係者的不滿,即使事後有退費機制,但有些匯率問題,始終會有差異。在傳統架構上,有規模的公司系統都可能會使用內部的中央資料庫等做交易(transaction)管理,整個過程,都要嚴謹地記錄廣擴是否成功、觀察者自己的操作是否成功。
在近代,分散式系統又或是微服務的出現,令上述的中央資料庫無法實行。如何好好地重新定義好Transaction管理,就是一大挑戰。筆者最近亦實作了一個要在微服務的上廣播的觀察者模式,但雪上加霜的是,在互聯網的環境下,廣播的消息沒法保證可以正常傳遞出好。觀察者/訂閱者可能已經正常收到消息,也做了相應的操作,只是來不及回應,網路就斷了。這令重複發送信號的可能增加了。
如果說,要以平民的方式去實作這類廣播,Pull會比較有大的容錯。廣播者只是通知觀察者/訂閱者來拉資料,保證廣播當時的資料量可以盡量地少。廣播者開放盡量大的查閱權,觀察者/訂閱者可以自由決定事後更新要取得的資料量。但這樣每個觀察者/訂閱者都要重做一次同步機制,不過好處是,主動權在於他們自己手上。
相對地,Push的容錯就低一點,但要付出的成本也跟Pull差不多。因為網路環境,大家要重現一個基於TCP/IP而有commit/rollback的難度較大。當網路出現斷線,廣播者無法確定是否需要重做。在重複收到訊號時,最後還是需要觀察者/訂閱者來決定怎樣處理重複記錄。但比Pull好的是,Push可以限制單次訊號的傳送量,也可以確保觀察者/訂閱者一定收到特定的記錄。
上述就是筆者在這一年來遇過的坑,如有什麼不足,很歡迎大家一起來作更多討論。