tendermint 中用到了訂閱發布模式,這種模式大家都不會陌生,比如你打開你的微信訂閱號,你訂閱的作者發布的文章,會廣播給每個訂閱者。在這個場景里,微信公眾號就是一個Pulisher,而你就是一個Subscriber,你收到的文章就是一個Message。
今天不過多討論 tendermint 的業務流程,主要和大家聊一下如何使用 go 語言寫一個訂閱發布模式。下面這個圖是 tendermint 中發布訂閱模式的草圖:

發布訂閱模式
接下來我再介紹幾個重要的數據結構:
type state struct { // query string -> client -> subscription subscriptions map[string]map[string]*Subscription // query string -> queryPlusRefCount queries map[string]*queryPlusRefCount }
state 是用來維護所有 subscriber,當需要 publish 時,遍歷這個對象中的兩個 map,找到需要通知的 Subscription,然后通知。我們接著看其他數據結構:
type Server struct { cmn.BaseService ? cmds chan cmd cmdsCap int // check if we have subscription before // subscribing or unsubscribing mtx sync.RWMutex subscriptions map[string]map[string]struct{} // subscriber -> query (string) -> empty struct }
這個結構中我們主要看 cmds 這個 channel,當我們有新的訂閱時,我們向這個 channel 中發送一個數據。subscriptions 這個結構也維護了所有的訂閱者的信息,有新的訂閱時,首先檢查一下之前是否已經訂閱過,如果有,就返回對應錯誤信息。
type cmd struct { op operation ? // subscribe, unsubscribe query Query subscription *Subscription clientID string ? // publish msg interface{} tags map[string]string }
上面這個是 cmds 的結構,在 channel 中發送這個類型的數據, query:
type Query interface { Matches(tags map[string]string) bool String() string }
這個里面有兩個接口,一個是返回 string,另外一個就是判斷是否相同。這里她的作用就是類似于 tag,我們訂閱時,需要指定我們關注的類型,可以理解為 tag,發布時也是如此,發布對應 tag 的消息。其中 Matches 中,傳入一個 map,根據其算法實現匹配,這里我們不深追這個算法,我們可以理解為,訂閱時,我們生成了一個 query 的對象,發布時要說明發布的消息的類型,或者可以說是具有某種 tag 的消息,然后對比 query 對象,匹配上就發送消息。
到這里我們訂閱模式的各種結構基本上都聊了一下,還有 EventBus 部分沒有細說,這部分主要就是和業務相關的了,所以我們這里留在后面的文章寫,還有 tendermint 是如何使用上面的發布訂閱模式也在下一篇文章中和大家分享。
最后啰嗦幾句,我一直都是非常推薦大家平時多閱讀源碼的,很多東西雖然我們通過理論可以學習到,但是動手實現就是另一回事了,尤其是很多時候我們自己動手實現的東西都是為了理解這個內容而實現的,很多是不能夠在項目中應用的,這時候我們能夠閱讀著名項目的源碼,就事半功倍了。
后續會寫更過關于源碼的分享,希望大家持續關注,你的在看是我碼字的動力(所有文章均是個人一個字一個字敲出來的,如有失誤之處,敬請大家諒解,更多內容可以關注公眾號:Go語言之美)。