[譯] 為 Go 語言開發者介紹 NATS
2018上海KubeCon
Kubernetes的全球盛會KubeCon將於11月13日~11月15日在中國上海隆重舉行,此論壇彙集了眾多在開源和雲原生領域有卓越貢獻的應用人員和技術專家。大會吸引了超過5000名行業精英前來參會,大家齊聚一堂相互分享經驗,聚焦創新,並討論雲原生計算的未來。KubeCon + CloudNativeCon中國論壇將召開100多個分組會議,包括技術會議、深度學習、案例研究等。現在通過容器時代專屬報名通道報名可以享受超大折扣哦,詳情請戳此處連結: ofollow,noindex">【容器時代粉絲專屬福利】KubeCon + CloudNativeCon門票驚喜折扣
寫在前面
本文將為準備構建分散式系統和微服務的 Go 語言開發者介紹 NATS 訊息系統。當你構建分散式應用時,訊息系統對應用間通訊而言非常關鍵,尤其是事件驅動架構的非同步通訊方式。為了構建現代分散式系統而誕生了很多分散式佇列和訊息系統。像 Kafka, NATS, NSQ, RabbitMQ, ActiveMQ 這類的開源技術, 以及像 Google Cloud Pub/Sub, Amazon SQS, Amazon SNS Topic, Azure Service Bus PaaS 雲平臺,上面這些都為訊息中介軟體和分散式系統提供了不同的能力和模式。前面提及的技術中的 NATS 和 NSQ 均由 Go 語言編寫,藉助於像微服務這樣的現代方式,大大簡化了構建分散式系統的過程。因為構建分散式系統本身就很複雜,如果使用複雜的訊息系統則會使你的應用更加複雜。現代的訊息系統應該在各種環境規模如內部伺服器、雲平臺、容器上都能夠輕鬆執行和擴充套件。
NATS介紹
NATS是一個開源、輕量級、高效能的雲原生訊息系統。它是實現了具有更高級別擴充套件性的釋出-訂閱訊息系統。即使NATS是基於釋出-訂閱分發模型,你同樣可以通過訂閱伺服器佇列組實現分散式系統。NATS創建於2010年,原是服務於Cloud Foundary平臺的訊息系統。NATS最開始是由Ruby語言實現的,但隨後NATS團隊使用Go語言進行了重寫。
NATS在兩個互相操作的模組中使用:核心NATS平臺-NATS伺服器(其可執行檔案的名字為gnatsd)簡稱為NATS;NATS流(其可執行檔案的名字為nats-streaming-server)是一個事件流服務,用於NATS新增事件流、釋出保障及再現歷史資料。NATS伺服器是面向高效能現代分散式系統架構而設計的,並不能進行訊息持久化。因此,如果你的系統是離線狀態,將不會接收到訊息。如果你想要實現持續訊息傳遞和釋出保障,可以使用NATS流代替核心NATS平臺,NATS流建立在核心NATS平臺基礎之上。本文我將專注於基礎的NATS 伺服器的介紹,至於NATS流我將在後續的文章中介紹。
NATS伺服器(gnatsd)是最高效能的分散式訊息系統,可以達到每秒鐘傳送1.5千萬-1.8千萬條訊息。NATS平臺易於使用和擴充套件,NATS的簡潔性和高效能性質使得它對於構建現代雲原生分散式系統及微服務而言是個不錯的選擇。我過去使用過許多訊息系統,因為NATS的效能和簡潔性,所以我強烈推薦它。
( 來源: bravenewgeek.com/dissecting-message-queues )
訊息模式
當NATS作為釋出-訂閱引擎時,它提供了三種訊息傳遞模式:
-
釋出-訂閱
-
佇列
-
請求-響應
訊息架構元件
NATS訊息基礎結構的主要構成有:
-
訊息:訊息是資料交換單元,用於應用間交換資料的有效載荷。
-
主體:主體明確訊息的目的。
-
生產者:生產者向NATS伺服器傳送訊息。
-
消費者:消費者從NATS伺服器中接收訊息。
-
訊息伺服器:NATS伺服器從生產者到消費者間分配訊息。
安裝伺服器和客戶端
以下是下載 NATS 伺服器的各種發行版:
http://nats.io/download/nats-io/gnatsd/
同樣可以使用Go語言工具安裝NATS伺服器:
go get github.com/nats-io/gnatsd
通過執行可執行檔案gnatsd來啟動NATS伺服器:
gnatsd
你也可以使用下面的Go語言工具安裝NATS客戶端:
go get github.com/nats-io/go-nats
在Go語言中使用NATS
讓我們通過Go語言編寫的一個分散式應用的例子探索下NATS。在這個例子中,我們使用請求-應答和釋出-訂閱訊息傳遞模式。在釋出-訂閱模式中,將使用訂閱者佇列組進行排隊。這個例子NATS將使用Protocol Buffers來發送和接收訊息。
清單1:緩衝協議中的訊息型別
message ServiceDiscovery { string order_service_uri = 1; } message EventStore { string aggregate_id = 1; string aggregate_type = 2; string event_id = 3; string event_type = 4; string event_data = 5; }
訊息型別ServiceDiscovery使用請求-應答通訊,型別EventStore使用的是釋出-訂閱模式。
NATS的請求應答示例
請求-應答訊息模式的工作方式類似於正常的請求-響應通訊,其中釋出請求操作釋出帶有回覆主題的訊息,同時等等對該回復主題的響應。這裡我們使用這個格式用於找出服務端點的簡單發現。值得注意的是我此處使用NATS的請求-應答進行訊息傳遞僅僅是為了舉個例子。NATS的請求-應答模式與RPC/">gRPC的簡單RPC非常類似,因此我一貫的使用gRPC的API介面代替NATS請求-應答訊息傳遞。但對於以事件驅動的Go語言微服務結構,我通常使用NATS用於pub/sub通訊。
這是一段程式碼塊,在專案“Discovery.OrderService”中傳送一個請求以得到服務端點。
清單2:作用於NATS請求-應答訊息傳遞的請求:
func main() { // Create NATS server connection natsConnection, _ := nats.Connect(nats.DefaultURL) log.Println("Connected to " + nats.DefaultURL) msg, err := natsConnection.Request("Discovery.OrderService", nil, 1000*time.Millisecond) if err == nil && msg != nil { orderServiceDiscovery := pb.ServiceDiscovery{} err := proto.Unmarshal(msg.Data, &orderServiceDiscovery)if err != nil { log.Fatalf("Error on unmarshal: %v", err) } address := orderServiceDiscovery.OrderServiceUri log.Println("OrderService endpoint found at:", address) //Set up a connection to the gRPC server. conn, err := grpc.Dial(address, grpc.WithInsecure()) } }
Go語言NATS客戶端的庫被匯入的專案中:
import "github.com/nats-io/go-nats"
函式nats.Connect嘗試連線NATS系統,預設的NATS伺服器運在“nats://localhost:4222”。這裡我們使用預設的URL連線伺服器。
natsConnection, _ := nats.Connect(nats.DefaultURL)
NATS通常在專案中傳送一個名為“Discovery.OrderService”的請求以得到應答。
msg, err := natsConnection.Request("Discovery.OrderService", nil, 1000*time.Millisecond)
當對某個專案傳送請求時,你可以傳遞請求資料和超時。這裡我們不提供任何資料,向這個專案傳送請求只是為了接收應答。我們採用Protocol Buffers進行傳送和接收訊息,響應資料被編碼在Go語言結構值中。
orderServiceDiscovery := pb.ServiceDiscovery{} err := proto.Unmarshal(msg.Data, &orderServiceDiscovery )if err != nil { log.Fatalf("Error on unmarshal: %v", err) } address := orderServiceDiscovery.OrderServiceUri log.Println("OrderService endpoint found at:", address)
這是另一個應用的程式碼塊,訂閱專案 “Discovery.OrderService” 提供了對應請求的應答。
清單3:NATS的請求-應答訊息傳遞的響應
var orderServiceUri string orderServiceUri = viper.GetString("discovery.orderservice") func main() { // Create server connection natsConnection, _ := nats.Connect(nats.DefaultURL) log.Println("Connected to " + nats.DefaultURL) natsConnection.Subscribe("Discovery.OrderService", func(m *nats.Msg) { orderServiceDiscovery := pb.ServiceDiscovery{OrderServiceUri: orderServiceUri} data, err := proto.Marshal(&orderServiceDiscovery) if err == nil { natsConnection.Publish(m.Reply, data) } }) // Keep the connection alive runtime.Goexit() }
專案 “Discovery.OrderService”參與傳送響應,這裡通過編碼到Protocol Buers以傳送響應資料。
NATS釋出訂閱示例
我極力推薦NATS用於釋出訂閱引擎的pub/sub訊息模型,構建企業級訊息佇列及Go語言搭建的分散式系統。NATS釋出訂閱是一對多通訊系統,專案中一個釋出者傳送一個訊息,專案中所有活躍的訂閱者接收這個訊息。此通訊模型是典型的非同步方式,釋出的訊息被分發到訂閱訊息的處理者。如果沒有處理者,訂閱以非同步模型工作,客戶端有可能被阻塞直到訊息被處理。大多數真實情況下,你可能不需要通訊的非同步方式用於pub/sub通訊。
當建立訂閱者時,你可以同時為它註冊一個佇列名稱。所有具有相同的佇列名稱的訂閱者構成一個佇列組。隨著訊息在被註冊的專案中釋出,佇列組中被隨機選擇的一個訂閱者用於接收訊息。雖然佇列組中有多個訂閱者,但每個訊息只能被一個訂閱者接收,且只能接收一次。當建立訂閱者時,你可以選擇是否註冊佇列名。在佇列組中的訂閱者們,其中一個訂閱者接收訊息,而那些沒有佇列組的訂閱者們,所有訂閱者共同接受這條訊息。有意思的是NATS本身提供佇列甚至它本身也是基於訊息釋出-訂閱模式。
在本文的訊息釋出-訂閱模式,我們建立一個無佇列的訂閱者及一個名“Order.OrdersCreatedQueue”含有多個訂閱者的訂閱組。因此佇列組中的一個訂閱者及其他的訂閱者(無佇列組的)可以接收訊息。釋出者的客戶端是一個gRPC伺服器,當專案“Order.OrderCreated”中建立一個命令時,釋出者將釋出一個訊息,詳見下面的程式碼塊:
清單4:用於NATS釋出訂閱訊息傳遞的釋出者客戶端
const ( aggregate = "Order" event= "OrderCreated") // publishOrderCreated publish an event via NATS server func publishOrderCreated(order *pb.Order) { // Connect to NATS server natsConnection, _ := nats.Connect(nats.DefaultURL) log.Println("Connected to " + nats.DefaultURL) defer natsConnection.Close() eventData, _ := json.Marshal(order) event := pb.EventStore{ AggregateId:order.OrderId, AggregateType: aggregate, EventId:uuid.NewV4().String(), EventType:event, EventData:string(eventData), } subject := "Order.OrderCreated" data, _ := proto.Marshal(&event) // Publish message on subject natsConnection.Publish(subject, data) log.Println("Published message on subject " + subject) }
訊息模式
NATS客戶端的Publish功能,向給定的專案中釋出一個訊息。這裡訊息被整理到Protocol Buffers中,當資訊從釋出者客戶端釋出到專案中時,我們建立一個訂閱者接收訊息。
這段程式碼塊來自於訂閱者客戶端用於訂閱訊息。
清單5:用於NATS釋出訂閱訊息傳遞的訂閱者客戶端
const subject = "Order.>" func main() { // Create server connection natsConnection, _ := nats.Connect(nats.DefaultURL) log.Println("Connected to " + nats.DefaultURL) // Subscribe to subject natsConnection.Subscribe(subject, func(msg *nats.Msg) { eventStore := pb.EventStore{} err := proto.Unmarshal(msg.Data, &eventStore) if err == nil { // Handle the message log.Printf("Received message in EventStore service: %+v\n", eventStore) store := store.EventStore{} store.CreateEvent(&eventStore) log.Println("Inserted event into Event Store") } }) // Keep the connection alive runtime.Goexit() }
使用萬用字元專案“Order.>”訂閱訊息,NATS支援在專案訂閱中使用萬用字元,支援星號字元(*)和大於號(>),也被認為所有的萬用字元被用於通配專案訂閱。萬用字元Order.> will 被匹配為Order.Created,Order.Shipped, Order.Delivered, Order.Delivered.Returned等等。萬用字元Order.* 將被匹配為 Order.Created, Order.Shipped, Order.Delivered等等,而不是 Order.Delivered.Returned。
NATS客戶端的Subscribe功能,當訊息被給定的專案釋出時,訂閱訊息處理非同步接收訊息。 由於訊息由Protocol Buffers編碼所釋出,接收訊息則是通過proto.Unmarshal解碼到Go語言結構體值中。
讓我們新增訂閱者到一個佇列組中,這是訂閱者客戶端從專案中訂閱訊息的程式碼塊:
清單6:訂閱者客戶端佇列組的NATS釋出-訂閱訊息傳遞
const ( queue= "Order.OrdersCreatedQueue" subject = "Order.OrderCreated" ) func main() { // Create server connection natsConnection, _ := nats.Connect(nats.DefaultURL) log.Println("Connected to " + nats.DefaultURL) // Subscribe to subject natsConnection.QueueSubscribe(subject, queue, func(msg *nats.Msg) { eventStore := pb.EventStore{} err := proto.Unmarshal(msg.Data, &eventStore) if err == nil { // Handle the message log.Printf("Subscribed message in Worker 1: %+v\n", eventStore) } }) // Keep the connection alive runtime.Goexit() }
主題“Order.OrderCreated”的訊息使用名為“Order.OrdersCreatedQueue"的佇列通過QueueSubscribe功能被訂閱。當我們使用同一個佇列名建立多個訂閱者,它們被建立在一個佇列組中,隨機選擇一個訂閱者用來接收訊息。如果只是想通過NATS僅僅用於排隊,可以只通過一個佇列組建立訂閱者。
本文我僅展示了核心NATS平臺的基礎能力,後續我將寫另一篇文章講述NATS流伺服器。
本文中的原始碼可見:https://github.com/shijuvar/gokit/tree/master/examples/grpc-nats
你也可以在twitte r ( @shijucv) 上關注我
原文連結
原文作者:Shiju Varghese
原文連結: https://medium.com/@shijuvar/introducing-nats-to-go-developers-3cfcb98c21d0
容器時代志願者招募
如果你對技術懵懵懂懂,想要入門卻不知從何下手;
如果你求知若渴,想要學習更多技術、思想;
如果你對於技術有著一種狂熱的喜愛並且熱愛開源,以其為信仰。
快來加入我們吧
加入我們可以接觸前沿技術
加入我們可以兼顧學習與分享
加入我們可以與大牛一對一交流
志願者計劃 JOIN US
容器時代志願編輯
志願內容
-
公眾號運營 —— 比如晨讀文章推薦、周推薦等; ( 特別歡迎在校大學生)
-
翻譯 —— 容器生態圈相關教程、文章、資訊等的翻譯;
你
來
啊
點選 閱讀原文 即可加入,加入之後還有 神祕福利 等著你呦~
編輯:立堯