Go Micro(4)——基于消息隊列NATS構建微服務
這篇文章我們會討論基于 NATS 使用 Micro。討論包括了服務發(fā)現(xiàn),同步通信和異步通信。
NATS是什么?
NATS 是一個開源的消息系統(tǒng),或者說消息隊列。NATS 的作者是 Derek Collison, Apcera 的作者。它起源于 VMWare,最開始是一個 ruby 的系統(tǒng)。后來使用 golang 進行重寫,逐步的成為了一個高擴展性的高性能消息系統(tǒng)。
為什么是NATS?
為什么不是呢?過去我使用過很多消息隊列,很明顯 NATS 是鶴立雞群的,在過去消息系統(tǒng)似乎成了企業(yè)的靈丹妙藥了,結果是每個人參與的每個系統(tǒng)都離不開消息系統(tǒng)。導致了很多無效的承諾、高昂的研發(fā)投入,制造出比它解決的更多的麻煩。
NATS,相比之下,就十分專注,在難以置信的簡潔之下,解決了性能問題,解決了高可用行問題。它的口號是『always on and available』,使用了一種『fire and forget』的消息模式。它簡單、專注、輕量的特性使它在微服務的候選中脫穎而出。我們相信,在服務間的消息傳遞領域,它很快會變成主要的候選者。
NATS能提供什么?
- 高性能、高擴展
- 高可用
- 極致的輕量級
- 一次部署
NATS不支持什么?
- 持久化
- 事務
- Enhanced delivery modes
- Enterprise queueing
NATS 是否適合 Micro 呢?我們接著討論
Micro on NATS
Micro 采用插件化的架構設計,用戶可以替換底層的實現(xiàn),而不更改任何底層的代碼。每個 Go-Micro 框架的底層模塊定義了相應的接口,registry 是作為服務發(fā)現(xiàn),transport 作為同步通信,broker 作為異步通信。
type Transport interface {
Dial(addr string, opts ...DialOption) (Client, error)
Listen(addr string, opts ...ListenOption) (Listener, error)
String() string
}
type Registry interface {
Register(*Service, ...RegisterOption) error
Deregister(*Service) error
GetService(string) ([]*Service, error)
ListServices() ([]*Service, error)
Watch() (Watcher, error)
String() string
}
type Broker interface {
Options() Options
Address() string
Connect() error
Disconnect() error
Init(...Option) error
Publish(string, *Message, ...PublishOption) error
Subscribe(string, Handler, ...SubscribeOption) (Subscriber, error)
String() string
}
為每個組件創(chuàng)建一個插件很簡單,只需要實現(xiàn)這些組件的接口即可。我們會在未來的文章里詳細討論,怎樣寫插件,如果你想詳細了解 NATS 的插件或者其他的類似 etcd 作為服務發(fā)現(xiàn),kafka 作異步通信,rabbitmq 作同步通信,在這里可以看到: github.com/micro/go-plugins
Micro on NATS 本質上是實現(xiàn)一系列的插件,將 NATS 消息系統(tǒng)整合到 Micro 的框架中來。通過為 Go Micro 提供不同的插件,我們可以創(chuàng)造出很多靈活的組合。
在實踐過程中,不能一套系統(tǒng)打天下,Micro on NATS 的可以靈活的定義各種模型。
下面我們會討論一下 NATS 插件怎樣在 transport 、 broker 和 registry 下工作。
Transport
[圖片上傳失敗...(image-a3bc83-1513577247790)]
transport 是 go-micro 定義的同步通信接口,它使用了泛型的語法描述,類似其他 golang 代碼,比如 Liesten,Dial,Accept。這些理念和模式在類似 TCP 和 HTTP 這樣的同步通信中很容易理解,但是在消息系統(tǒng)中要怎樣兼容呢?通信過程中,連接是與消息隊列建立的,而不是服務本身。為了達到目的,我們使用消息系統(tǒng)中的 topics 和 channels 來創(chuàng)造虛連接。
它是怎樣工作起來的?
一個服務使用 transport.Listen 來監(jiān)聽消息,這會與 NATS 之間建立連接。當 transport.Accept 被調用時,一個唯一的 topic 會被創(chuàng)建并訂閱。這個唯一的 topic 地址會作為服務的地址,注冊到 go-micro 的注冊器中。每個收到的消息會被虛連接處理,如果一個連接已經(jīng)存在,而且回復的地址是一樣的,我們會簡單的把消息積壓在連接上。
客戶端通過 transport.Dial 來連接服務端,其實它是建立了與 NATS 的連接,然后創(chuàng)建了唯一的 topic 并且訂閱這個 topic。這個 topic 用于接收服務端的返回。任何時候客戶端發(fā)送消息到服務端時,它都會把回復地址設置成這個 topic。(譯注:服務端的地址是固定的,而客戶端的每個請求,都會生成一個客戶端地址,唯一的請求與唯一的地址實現(xiàn)一一關聯(lián))
當任何一邊想要關閉連接時,簡單的調用 transport.Close 就會斷開與 NATS 的連接。
[圖片上傳失敗...(image-a17b4e-1513577247790)]
使用 transport 插件
引用插件
import _ "github.com/micro/go-plugins/transport/nats"
啟動時傳入?yún)?shù)
go run main.go --transport=nats --transport_address=127.0.0.1:4222
或者直接在代碼中創(chuàng)建
transport := nats.NewTransport()
go-micro 的 transport 接口:
type Transport interface {
Dial(addr string, opts ...DialOption) (Client, error)
Listen(addr string, opts ...ListenOption) (Listener, error)
String() string
}
Broker
[圖片上傳失敗...(image-14ecc9-1513577247790)]
broke 是 go-micro 的異步通信接口,它定義了泛型的、高等級的通用接口。NATS 本身作為消息系統(tǒng),是可以作為消息 broker 的。這里只有一個警告:nats 并不持久化消息。雖然在某些場景有風險,但我們相信 NATS 可以也應該用于 go-micro 的 broker。持久化并不是必須的,它提供了高擴展的發(fā)布和訂閱架構。
NATS 通過 Topics 和 Channels 的理念,非常直接的實現(xiàn)了發(fā)布和訂閱機制。這里并沒有其他工作需要做。消息可以被發(fā)布到任何異步的 fire and forget manner。通過 NATS 的 Queue Group,訂閱方可以通過 channel 的名字就行訂閱。實現(xiàn)消息自動的分發(fā)的多個訂閱者。
[圖片上傳失敗...(image-be955d-1513577247790)]
使用 broker 插件
引入包
import _ "github.com/micro/go-plugins/broker/nats"
(譯注:注意此時引用的是 broker 中的 nats 包,上面引用的是 transport 中的 nats 包)
通過參數(shù)啟動
go run main.go --broker=nats --broker_address=127.0.0.1:4222
也可以直接啟動
broker := nats.NewBroker()
go-micro 的 broker 需要實現(xiàn)的接口:
type Broker interface {
Options() Options
Address() string
Connect() error
Disconnect() error
Init(...Option) error
Publish(string, *Message, ...PublishOption) error
Subscribe(string, Handler, ...SubscribeOption) (Subscriber, error)
String() string
}
Register
[圖片上傳失敗...(image-f5229e-1513577247790)]
Register 是 go-micro 定義的服務發(fā)現(xiàn)接口,你也許想過。用消息系統(tǒng)做服務發(fā)現(xiàn)?這能工作嗎?事實上它不僅能工作,還工作的很好。許多人在服務發(fā)現(xiàn)中使用消息系統(tǒng),避免了不一致的發(fā)現(xiàn)機制。這是因為消息隊列通過 topics 和 channels 實現(xiàn)了路由,Topics 定義為服務的名字可以作為路由的 key,訂閱 topics 的服務自動實現(xiàn)了負載均衡。
Go-micro 把服務發(fā)現(xiàn)和傳輸機制看做兩個不同的領域,任何時候,客戶端向服務發(fā)起請求,都會首先在注冊器中根據(jù)服務的名字查詢到正在運行的服務節(jié)點,然后客戶端通過 transport 與節(jié)點進行通信。
一般來說,最常見的存儲服務發(fā)現(xiàn)信息的方式是通過分布式 key-value store,比如 zookeeper、etcd 等等。正如你了解到的,NATS 不是一個分布式的 KV store,我們將做一下改動。。。
廣播查詢!
廣播查詢正如你想象的那樣,服務都會監(jiān)聽一個特點的 topic,任何需要服務發(fā)現(xiàn)信息的都需要訂閱這個 topic,然后對這個 topic 做一個反饋。
因為我們并不知道有多少服務正在運行,我們對響應時間設置一個上限。這是一個粗糙的服務發(fā)現(xiàn)機制,但因為 NATS 本身的高擴展性和高性能,它運行的反而非常好。它也間接的提高了過濾服務節(jié)點的響應時間。未來我們會試著提高底層的實現(xiàn)。
我們總結一下它是怎么工作的:
- 創(chuàng)建一個
reply topic并訂閱 - 向廣播
topic發(fā)起查詢,附帶上reply topic - 監(jiān)聽回復,超過限制時間就注銷訂閱
- 聚合響應并返回結果
[圖片上傳失敗...(image-9adecd-1513577247790)]
使用 register 插件
引入包
import _ "github.com/micro/go-plugins/registry/nats"
通過參數(shù)啟動
go run main.go --registry=nats --registry_address=127.0.0.1:4222
或者直接在代碼中設置
registry := nats.NewRegistry()
go-micro 中的 register 接口
type Registry interface {
Register(*Service, ...RegisterOption) error
Deregister(*Service) error
GetService(string) ([]*Service, error)
ListServices() ([]*Service, error)
Watch() (Watcher, error)
String() string
}
大規(guī)模在 Micro 中使用 NATS
在上面的例子中,我們只是用了單個的 NATS 服務,但是在實際情況下,我們一般都是使用 NATS 集群來實現(xiàn)高可用和容錯。你可以在這里看看更多 NATS 集群的知識。
Micro 在啟動時可以接收一系列的參數(shù),如環(huán)境變量等。如果直接使用 client 的庫,也可以在創(chuàng)建時指定參數(shù)。
在目前云計算的架構下,我們過去的經(jīng)驗是,每個 AZ 都應該有集群。大部分的云計算公司,不同的 AZ 之間的延遲大概是3到5毫秒,集群的通信是沒有任何問題的。當我們運行一個高可用的配置,你的系統(tǒng)必須要能在 AZ 宕機、甚至整個 region 崩潰時還能提供服務。我們不建議跨 region 部署集群。理想的高等級工具應該是用于管理多集群和多 region 系統(tǒng)。
Micro 是一個極其靈活的微服務系統(tǒng),它本身就被設計成運行在任何配置的任何地方。整個 Micro 世界是通過服務發(fā)現(xiàn)進行指導,服務的集群可以運行在機器集群中,AZ 或者 regions。再考慮到 NATS 集群,它可以讓你構建高可用的服務。
[圖片上傳失敗...(image-92f6b7-1513577247790)]
總結
NATS 是一個高可用和高性能的消息系統(tǒng),在我們的微服務生態(tài)系統(tǒng)中運行的很好。它與 Micro 配合的非常的好,我們可以把它用在 register,transport,broker 中,我們也已經(jīng)全部實現(xiàn)了這些插件。
NATS 在 Micro 中的使用只是一個示例,它展示了 Micro 高度的插件化架構,每個 go-micro 的包都可以被替換,底層不需要改動代碼,上層也只需要改動非常少的代碼。在未來我們還會看到更多的 Micro on X,下一個可能是 Micro on kubernetes。
希望這可以鼓勵你來使用 Micro on NATS,或者編寫自己的插件并回饋給社區(qū)。
在這里看更多的NATS插件 github.com/micro/go-plugins