go 語言實現發佈訂閱模式(tendermint 源碼閱讀)

tendermint 中用到了訂閱發佈模式,這種模式大家都不會陌生,比如你打開你的微信訂閱號,你訂閱的作者發佈的文章,會廣播給每個訂閱者。在這個場景裡,微信公眾號就是一個Pulisher,而你就是一個Subscriber,你收到的文章就是一個Message。

今天不過多討論 tendermint 的業務流程,主要和大家聊一下如何使用 go 語言寫一個訂閱發佈模式。下面這個圖是 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語言之美)。


分享到:


相關文章: