Go操作NSQ

BigSun丶發表於2024-03-18

目錄
  • 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的優勢有以下優勢:
    1. NSQ提倡分散式和分散的拓撲,沒有單點故障,支援容錯和高可用性,並提供可靠的訊息交付保證
    2. NSQ支援橫向擴充套件,沒有任何集中式代理。
    3. 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工作模式

nsq4.png

2. Topic和Channel

  • 每個nsqd例項旨在一次處理多個資料流。這些資料流稱為“topics”,一個topic具有1個或多個“channels”。每個channel都會收到topic所有訊息的副本,實際上下游的服務是透過對應的channel來消費topic訊息。

  • topicchannel不是預先配置的。topic在首次使用時建立,方法是將其釋出到指定topic,或者訂閱指定topic上的channel

  • channel是透過訂閱指定的channel在第一次使用時建立的。

  • topicchannel都相互獨立地緩衝資料,防止緩慢的消費者導致其他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接收和傳送訊息流程

nsq6.png

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
		}
	}
}
  • 將上面的程式碼編譯執行,然後在終端輸入兩條資料123456
$ ./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這個頁面我們能夠很方便的檢視當前接入lookupdnsqd節點。

  • 這個/counter頁面顯示了處理的訊息數量,因為我們沒有接入消費者,所以處理的訊息數量為0。

  • /lookup介面支援建立topicchannel

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 := &amp;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的官方文件

相關文章