- Go操作NSQ
- 一、NSQ
- 1. NSQ介紹
- 2. NSQ的應用場景
- (1)非同步處理
- (2)應用解耦
- (3)流量削峰
- 二、安裝
- 三、NSQ元件
- 1. nsqd
- 2. nsqlookupd
- 3. nsqadmin
- 四、NSQ架構
- 1. NSQ工作模式
- 2. Topic和Channel
- 五、NSQ接收和傳送訊息流程
- 1. NSQ特性
- 六、Go操作NSQ
- 1. 安裝
- 2. 生產者
- 3. 消費者
Go操作NSQ
- NSQ是目前比較流行的一個分散式的訊息佇列,本文主要介紹了NSQ及Go語言如何操作NSQ。
一、NSQ
1. NSQ介紹
- NSQ是Go語言編寫的一個開源的實時分散式記憶體訊息佇列,其效能十分優異。 NSQ的優勢有以下優勢:
- NSQ提倡分散式和分散的拓撲,沒有單點故障,支援容錯和高可用性,並提供可靠的訊息交付保證
- NSQ支援橫向擴充套件,沒有任何集中式代理。
- NSQ易於配置和部署,並且內建了管理介面。
2. NSQ的應用場景
- 通常來說,訊息佇列都適用以下場景。
(1)非同步處理
- 參照下圖利用訊息佇列把業務流程中的非關鍵流程非同步化,從而顯著降低業務請求的響應時間。
(2)應用解耦
- 透過使用訊息佇列將不同的業務邏輯解耦,降低系統間的耦合,提高系統的健壯性。後續有其他業務要使用訂單資料可直接訂閱訊息佇列,提高系統的靈活性。
(3)流量削峰
- 類似秒殺(大秒)等場景下,某一時間可能會產生大量的請求,使用訊息佇列能夠為後端處理請求提供一定的緩衝區,保證後端服務的穩定性。
二、安裝
- 官方下載頁面根據自己的平臺下載並解壓即可。
三、NSQ元件
1. nsqd
- nsqd 是一個程序監聽了http,tcp兩種協議, 用來建立topic,channel, 分發訊息給消費者,向nsqlooup 註冊自己的後設資料資訊(topic、channel、consumer),自己的服務資訊,最核心模組
- nsqd 是一個守護程序,負責接收,排隊,投遞訊息給客戶端。也就是說這個服務是幹活的。它可以獨立執行,不過通常它是由 nsqlookupd 例項所在叢集配置的
/*
特性:
1. 對訂閱了同一個topic,同一個channel的消費者使用負載均衡策略(不是輪詢)
2. 只要channel存在,即使沒有該channel的消費者,也會將生產者的message快取到佇列中(注意訊息的過期處理)
3. 保證佇列中的message至少會被消費一次,即使nsqd退出,也會將佇列中的訊息暫存磁碟上(結束程序等意外情況除外)
4. 限定記憶體佔用,能夠配置nsqd中每個channel佇列在記憶體中快取的message數量,一旦超出,message將被快取到磁碟中
5. topic,channel一旦建立,將會一直存在,要及時在管理臺或者用程式碼清除無效的topic和channel,避免資源的浪費
*/
- 啟動
nsqd
,指定-broadcast-address=127.0.0.1
來配置廣播地址
./nsqd -broadcast-address=127.0.0.1
- 如果是在搭配
nsqlookupd
使用的模式下需要還指定nsqlookupd
地址:
./nsqd -broadcast-address=127.0.0.1 -lookupd-tcp-address=127.0.0.1:4160
-
如果是部署了多個
nsqlookupd
節點的叢集,那還可以指定多個-lookupd-tcp-address
。 -
nsqdq
相關配置項如下:
-auth-http-address value
<addr>:<port> to query auth server (may be given multiple times)
-broadcast-address string
address that will be registered with lookupd (defaults to the OS hostname) (default "PROSNAKES.local")
-config string
path to config file
-data-path string
path to store disk-backed messages
-deflate
enable deflate feature negotiation (client compression) (default true)
-e2e-processing-latency-percentile value
message processing time percentiles (as float (0, 1.0]) to track (can be specified multiple times or comma separated '1.0,0.99,0.95', default none)
-e2e-processing-latency-window-time duration
calculate end to end latency quantiles for this duration of time (ie: 60s would only show quantile calculations from the past 60 seconds) (default 10m0s)
-http-address string
<addr>:<port> to listen on for HTTP clients (default "0.0.0.0:4151")
-http-client-connect-timeout duration
timeout for HTTP connect (default 2s)
-http-client-request-timeout duration
timeout for HTTP request (default 5s)
-https-address string
<addr>:<port> to listen on for HTTPS clients (default "0.0.0.0:4152")
-log-prefix string
log message prefix (default "[nsqd] ")
-lookupd-tcp-address value
lookupd TCP address (may be given multiple times)
-max-body-size int
maximum size of a single command body (default 5242880)
-max-bytes-per-file int
number of bytes per diskqueue file before rolling (default 104857600)
-max-deflate-level int
max deflate compression level a client can negotiate (> values == > nsqd CPU usage) (default 6)
-max-heartbeat-interval duration
maximum client configurable duration of time between client heartbeats (default 1m0s)
-max-msg-size int
maximum size of a single message in bytes (default 1048576)
-max-msg-timeout duration
maximum duration before a message will timeout (default 15m0s)
-max-output-buffer-size int
maximum client configurable size (in bytes) for a client output buffer (default 65536)
-max-output-buffer-timeout duration
maximum client configurable duration of time between flushing to a client (default 1s)
-max-rdy-count int
maximum RDY count for a client (default 2500)
-max-req-timeout duration
maximum requeuing timeout for a message (default 1h0m0s)
-mem-queue-size int
number of messages to keep in memory (per topic/channel) (default 10000)
-msg-timeout string
duration to wait before auto-requeing a message (default "1m0s")
-node-id int
unique part for message IDs, (int) in range [0,1024) (default is hash of hostname) (default 616)
-snappy
enable snappy feature negotiation (client compression) (default true)
-statsd-address string
UDP <addr>:<port> of a statsd daemon for pushing stats
-statsd-interval string
duration between pushing to statsd (default "1m0s")
-statsd-mem-stats
toggle sending memory and GC stats to statsd (default true)
-statsd-prefix string
prefix used for keys sent to statsd (%s for host replacement) (default "nsq.%s")
-sync-every int
number of messages per diskqueue fsync (default 2500)
-sync-timeout duration
duration of time per diskqueue fsync (default 2s)
-tcp-address string
<addr>:<port> to listen on for TCP clients (default "0.0.0.0:4150")
-tls-cert string
path to certificate file
-tls-client-auth-policy string
client certificate auth policy ('require' or 'require-verify')
-tls-key string
path to key file
-tls-min-version value
minimum SSL/TLS version acceptable ('ssl3.0', 'tls1.0', 'tls1.1', or 'tls1.2') (default 769)
-tls-required
require TLS for client connections (true, false, tcp-https)
-tls-root-ca-file string
path to certificate authority file
-verbose
enable verbose logging
-version
print version string
-worker-id
do NOT use this, use --node-id
2. nsqlookupd
- nsqlookupd 儲存了nsqd的後設資料和服務資訊(endpoind),向消費者提供服務發現功能, 向nsqadmin提供資料查詢功能.
- nsqlookupd 是維護所有nsqd狀態、提供服務發現的守護程序。它能為消費者查詢特定
topic
下的nsqd提供了執行時的自動發現服務。 它不維持持久狀態,也不需要與任何其他nsqlookupd例項協調以滿足查詢。因此根據你係統的冗餘要求儘可能多地部署nsqlookupd
節點。它們小豪的資源很少,可以與其他服務共存。我們的建議是為每個資料中心執行至少3個叢集。 - nsqlookupd 是守護程序負責管理拓撲資訊。客戶端透過查詢 nsqlookupd 來發現指定話題(topic)的生產者,並且 nsqd 節點廣播話題(topic)和通道(channel)資訊。也就是說nsqlookupd是管理者
/*
特性:
1. 唯一性,在一個Nsq服務中只有一個nsqlookupd服務。當然也可以在叢集中部署多個nsqlookupd,但它們之間是沒有關聯的.
2. 去中心化,即使nsqlookupd崩潰,也會不影響正在執行的nsqd服務
3. 充當nsqd和naqadmin資訊互動的中介軟體
4. 提供一個http查詢服務,給客戶端定時更新nsqd的地址目錄.
*/
nsqlookupd
相關配置項如下:
-broadcast-address string
address of this lookupd node, (default to the OS hostname) (default "PROSNAKES.local")
-config string
path to config file
-http-address string
<addr>:<port> to listen on for HTTP clients (default "0.0.0.0:4161")
-inactive-producer-timeout duration
duration of time a producer will remain in the active list since its last ping (default 5m0s)
-log-prefix string
log message prefix (default "[nsqlookupd] ")
-tcp-address string
<addr>:<port> to listen on for TCP clients (default "0.0.0.0:4160")
-tombstone-lifetime duration
duration of time a producer will remain tombstoned if registration remains (default 45s)
-verbose
enable verbose logging
-version
print version string
3. nsqadmin
- 一個實時監控叢集狀態、執行各種管理任務的Web管理平臺。
- 簡單的管理介面,展示了topic, channel以及channel上的消費者,也可以建立topic,channel
/*
特性:
1. 提供一個對topic和channel統一管理的操作介面以及各種實時監控資料的展示,介面設計的很簡潔,操作也很簡單
2. 展示所有message的數量
3. 能夠在後臺建立topic和channel
4. nsqadmin的所有功能都必須依賴於nsqlookupd,nsqadmin只是向nsqlookupd傳遞使用者操作並展示來自nsqlookupd的資料
*/
- 啟動
nsqadmin
,指定nsqlookupd
地址:
./nsqadmin -lookupd-http-address=127.0.0.1:4161
-
我們可以使用瀏覽器開啟
http://127.0.0.1:4171/
訪問如下管理介面。 -
nsqadmin
相關的配置項如下:
-allow-config-from-cidr string
A CIDR from which to allow HTTP requests to the /config endpoint (default "127.0.0.1/8")
-config string
path to config file
-graphite-url string
graphite HTTP address
-http-address string
<addr>:<port> to listen on for HTTP clients (default "0.0.0.0:4171")
-http-client-connect-timeout duration
timeout for HTTP connect (default 2s)
-http-client-request-timeout duration
timeout for HTTP request (default 5s)
-http-client-tls-cert string
path to certificate file for the HTTP client
-http-client-tls-insecure-skip-verify
configure the HTTP client to skip verification of TLS certificates
-http-client-tls-key string
path to key file for the HTTP client
-http-client-tls-root-ca-file string
path to CA file for the HTTP client
-log-prefix string
log message prefix (default "[nsqadmin] ")
-lookupd-http-address value
lookupd HTTP address (may be given multiple times)
-notification-http-endpoint string
HTTP endpoint (fully qualified) to which POST notifications of admin actions will be sent
-nsqd-http-address value
nsqd HTTP address (may be given multiple times)
-proxy-graphite
proxy HTTP requests to graphite
-statsd-counter-format string
The counter stats key formatting applied by the implementation of statsd. If no formatting is desired, set this to an empty string. (default "stats.counters.%s.count")
-statsd-gauge-format string
The gauge stats key formatting applied by the implementation of statsd. If no formatting is desired, set this to an empty string. (default "stats.gauges.%s")
-statsd-interval duration
time interval nsqd is configured to push to statsd (must match nsqd) (default 1m0s)
-statsd-prefix string
prefix used for keys sent to statsd (%s for host replacement, must match nsqd) (default "nsq.%s")
-version
print version string
四、NSQ架構
1. NSQ工作模式
2. Topic和Channel
-
每個nsqd例項旨在一次處理多個資料流。這些資料流稱為
“topics”
,一個topic
具有1個或多個“channels”
。每個channel
都會收到topic
所有訊息的副本,實際上下游的服務是透過對應的channel
來消費topic
訊息。 -
topic
和channel
不是預先配置的。topic
在首次使用時建立,方法是將其釋出到指定topic
,或者訂閱指定topic
上的channel
。 -
channel
是透過訂閱指定的channel
在第一次使用時建立的。 -
topic
和channel
都相互獨立地緩衝資料,防止緩慢的消費者導致其他chennel
的積壓(同樣適用於topic
級別)。 -
channel
可以並且通常會連線多個客戶端。假設所有連線的客戶端都處於準備接收訊息的狀態,則每條訊息將被傳遞到隨機客戶端。例如:
生產者向某個topic中傳送訊息,如果topic有一個或者多個channle,那麼該訊息會被複制多分傳送到每一個channel中。類似 rabbitmq中的fanout型別,channle類似佇列。 官方說 nsq 是分散式的訊息佇列服務,但是在我看來只有channel到消費者這部分提現出來分散式的感覺,nsqd 這個模組其實就是單點的,nsqd 將 topic、channel、以及訊息都儲存在了本地磁碟,官方還建議一個生產者使用一個 nsqd,這樣不僅浪費資源還沒有資料備份的保障。一旦 nsqd 所在的主機磁損壞,資料都將丟失
- 總而言之,訊息是從
topic -> channel
(每個channel接收該topic的所有訊息的副本)多播的,但是從channel -> consumers
均勻分佈(每個消費者接收該channel的一部分訊息)。
五、NSQ接收和傳送訊息流程
1. NSQ特性
- 訊息預設不持久化,可以配置成持久化模式。nsq採用的方式時記憶體+硬碟的模式,當記憶體到達一定程度時就會將資料持久化到硬碟。
- 如果將
--mem-queue-size
設定為0,所有的訊息將會儲存到磁碟。 - 伺服器重啟時也會將當時在記憶體中的訊息持久化。
- 如果將
- 每條訊息至少傳遞一次。
- 訊息不保證有序。
六、Go操作NSQ
- 官方提供了Go語言版的客戶端:go-nsq,更多客戶端支援請檢視CLIENT LIBRARIES。
1. 安裝
go get -u github.com/nsqio/go-nsq
2. 生產者
- 一個簡單的生產者示例程式碼如下:
// nsq_producer/main.go
package main
import (
"bufio"
"fmt"
"os"
"strings"
"github.com/nsqio/go-nsq"
)
// NSQ Producer Demo
var producer *nsq.Producer
// 初始化生產者
func initProducer(str string) (err error) {
config := nsq.NewConfig()
producer, err = nsq.NewProducer(str, config)
if err != nil {
fmt.Printf("create producer failed, err:%v\n", err)
return err
}
return nil
}
func main() {
nsqAddress := "127.0.0.1:4150"
err := initProducer(nsqAddress)
if err != nil {
fmt.Printf("init producer failed, err:%v\n", err)
return
}
reader := bufio.NewReader(os.Stdin) // 從標準輸入讀取
for {
data, err := reader.ReadString('\n')
if err != nil {
fmt.Printf("read string from stdin failed, err:%v\n", err)
continue
}
data = strings.TrimSpace(data)
if strings.ToUpper(data) == "Q" { // 輸入Q退出
break
}
// 向 'topic_demo' publish 資料
err = producer.Publish("topic_demo", []byte(data))
if err != nil {
fmt.Printf("publish msg to nsq failed, err:%v\n", err)
continue
}
}
}
- 將上面的程式碼編譯執行,然後在終端輸入兩條資料
123
和456
:
$ ./nsq_producer
123
2018/10/22 18:41:20 INF 1 (127.0.0.1:4150) connecting to nsqd
456
-
使用瀏覽器開啟
http://127.0.0.1:4171/
可以檢視到類似下面的頁面: 在下面這個頁面能看到當前的topic
資訊: -
點選頁面上的
topic_demo
就能進入一個展示更多詳細資訊的頁面,在這個頁面上我們可以檢視和管理topic
,同時能夠看到目前在LWZMBP:4151 (127.0.01:4151)
這個nsqd
上有2條message。又因為沒有消費者接入所以暫時沒有建立channel
。 -
在
/nodes
這個頁面我們能夠很方便的檢視當前接入lookupd
的nsqd
節點。
- 這個
/counter
頁面顯示了處理的訊息數量,因為我們沒有接入消費者,所以處理的訊息數量為0。
- 在
/lookup
介面支援建立topic
和channel
。
3. 消費者
- 一個簡單的消費者示例程式碼如下:
// nsq_consumer/main.go
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
"time"
"github.com/nsqio/go-nsq"
)
// NSQ Consumer Demo
// MyHandler 是一個消費者型別
type MyHandler struct {
Title string
}
// HandleMessage 是需要實現的處理訊息的方法
func (m *MyHandler) HandleMessage(msg *nsq.Message) (err error) {
fmt.Printf("%s recv from %v, msg:%v\n", m.Title, msg.NSQDAddress, string(msg.Body))
return
}
// 初始化消費者
func initConsumer(topic string, channel string, address string) (err error) {
config := nsq.NewConfig()
config.LookupdPollInterval = 15 * time.Second
c, err := nsq.NewConsumer(topic, channel, config)
if err != nil {
fmt.Printf("create consumer failed, err:%v\n", err)
return
}
consumer := &MyHandler{
Title: "沙河1號",
}
c.AddHandler(consumer)
// if err := c.ConnectToNSQD(address); err != nil { // 直接連NSQD
if err := c.ConnectToNSQLookupd(address); err != nil { // 透過lookupd查詢
return err
}
return nil
}
func main() {
err := initConsumer("topic_demo", "first", "127.0.0.1:4161")
if err != nil {
fmt.Printf("init consumer failed, err:%v\n", err)
return
}
c := make(chan os.Signal) // 定義一個訊號的通道
signal.Notify(c, syscall.SIGINT) // 轉發鍵盤中斷訊號到c
<-c // 阻塞
}
- 將上面的程式碼儲存之後編譯執行,就能夠獲取之前我們publish的兩條訊息了:
$ ./nsq_consumer
2018/10/22 18:49:06 INF 1 [topic_demo/first] querying nsqlookupd http://127.0.0.1:4161/lookup?topic=topic_demo
2018/10/22 18:49:06 INF 1 [topic_demo/first] (127.0.0.1:4150) connecting to nsqd
沙河1號 recv from 127.0.0.1:4150, msg:123
沙河1號 recv from 127.0.0.1:4150, msg:456
-
同時在nsqadmin的
/counter
頁面檢視到處理的資料數量為2。 -
關於
go-nsq
的更多內容請閱讀go-nsq的官方文件。