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

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