mac环境 go nsq的安装和使用示例

34 阅读8分钟

测试环境macOS26 macOS 15

1 安装nsp:

brew install nsq

2 启动nsp&测试

2.1.在第一个shell中,启动nsqlookupd

nsqlookupd

2.2.在第二个shell中,启动nsqd

nsqd --lookupd-tcp-address=127.0.0.1:4160

2.3.在第三个shell中,启动nsqadmin

nsqadmin --lookupd-http-address=127.0.0.1:4161

2.4. 在第四个shell中,发布第一个消息(同时创建topic)

curl -d 'hello world 1' 'http://127.0.0.1:4151/pub?topic=test'
  • 通过 HTTP API 发布消息(不推荐用于生产环境,HTTP 端口(4151)),生产环境应该直接使用TCP,4150端口
  • 指定topic为test; Producer 只需要指定 Topic

2.5.在第五个shell中,使用nsq_to_file启动一个client来接收消息

nsq_to_file --topic=test --output-dir=/tmp --lookupd-http-address=127.0.0.1:4161
说明
  • nsq_to_file 是 NSQ 工具集中的一个命令行工具,用于将指定 topic 的消息保存到本地文件。
    • 参数说明
      • --topic=test
        • 订阅的 topic 名称
        • 只接收 test 的消息
      • --output-dir=/tmp
        • 输出目录
        • 消息会写入 /tmp 下的日志文件
        • 文件名格式通常为 topic名.时间戳.log(例如 test.1234567890.log)
      • --lookupd-http-address=127.0.0.1:4161
        • NSQ Lookupd 的 HTTP 地址
        • 127.0.0.1:4161 是本地默认端口
        • 通过 Lookupd 发现可用的 nsqd 节点,而不是直连 nsqd
    • 工作原理
      1. 连接到 127.0.0.1:4161 的 nsqlookupd
      2. 查询 test topic 的 nsqd 节点
      3. 订阅并消费消息
      4. 将消息写入 /tmp 目录的日志文件

2.6.在第四个shell中,向nsqd发布更多的消息

curl -d 'hello world 2' 'http://127.0.0.1:4151/pub?topic=test'
curl -d 'hello world 3' 'http://127.0.0.1:4151/pub?topic=test'

2.7 怎么验证我们的搭建NSQ实时分布式消息平台已经正常工作了?有两种方式:

第一种方式:在浏览器中输入网址http://127.0.0.1:4171,打开nsqadmin的UI界面,查看统计数据。

第二种方式:转到/tmp目录(nsq_to_file命令的--output-dir参数值),查看“名称符合test.*.log模式”的文件内容。

img_v3_02to_b88c67ab-629a-476c-ae95-c15ccb84f6ag.jpg

3 nsq默认端口解释

3.1 在nsq中4150 4151 4160 4161 端口,分别对应什么服务?

  • 4150 端口 - nsqd 的 TCP 端口

    • 用途:客户端(Producer 和 Consumer)与 nsqd 通信
    • 客户端(Producer/Consumer)连接端口
  • 4151 端口 - nsqd 的 HTTP 端口

    • 用途:nsqd 的 HTTP API(REST API),不是可视化 Web 界面
    • 功能:通过 HTTP 请求查看统计、创建 topic/channel、发布消息等
    • 例如:http://localhost:4151/stats 查看统计信息
  • 4160 端口 - nsqlookupd 的 TCP 端口

    • 用途:nsqd 向 nsqlookupd 注册,客户端查询 topic 信息
  • 4161 端口 - nsqlookupd 的 HTTP 端口

    • 用途:nsqlookupd 的 HTTP API(REST API),不是可视化 Web 界面
    • 功能:通过 HTTP 请求查询注册的 nsqd 节点、topic 信息等
    • nsqadmin 通过此端口连接 nsqlookupd 获取集群信息
  • 4171 端口 - nsqadmin 的 Web 管理界面

  • 关键点: *Producer 只需要指定 Topic(如 topic_demo)

    • Consumer 需要指定 Topic 和 Channel(如 topic_demo + channel_demo)
    • 同一个 Topic 可以有多个 Channel,实现消息的多路分发
    • 同一个 Channel 可以有多个 Consumer,实现负载均衡

NSQ 中的 Channel 概念

在 NSQ 中,Channel(通道) 是消费者(Consumer)订阅 Topic 时使用的概念。

基本关系

Topic(主题) └── Channel(通道) └── Consumer(消费者)

   ### 详细说明
  1. Topic(主题)
  • 消息的分类/队列名称
  • 生产者向 Topic 发布消息
  • 你的代码中:topic_demo
  1. Channel(通道)
  • 属于某个 Topic
  • 多个 Channel 可以订阅同一个 Topic
  • 每个 Channel 会收到该 Topic 的所有消息副本
  • 用于实现不同的消费逻辑或不同的消费者组
  1. Consumer(消费者)
  • 连接到某个 Channel
  • 从 Channel 中消费消息
  • 同一个 Channel 可以有多个消费者(负载均衡)

示例场景

假设你有一个 order Topic: Topic: order ├── Channel: email_notification │ └── Consumer: 发送邮件服务 ├── Channel: sms_notification
│ └── Consumer: 发送短信服务 └── Channel: inventory_update └── Consumer: 库存更新服务

  • 一条订单消息发布到 order Topic
  • 三个 Channel 都会收到这条消息
  • 每个 Channel 的消费者独立处理

关键点: Producer 只需要指定 Topic(如 topic_demo) Consumer 需要指定 Topic 和 Channel(如 topic_demo + channel_demo) 同一个 Topic 可以有多个 Channel,实现消息的多路分发 同一个 Channel 可以有多个 Consumer,实现负载均衡

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 := "192.168.10.13:4150"
	err := initProducer(nsqAddress)
	if err != nil {
		fmt.Printf("init producer failed, err:%v\n", err)
		return
	}

	fmt.Printf("init producer success")

	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
		}
	}
}

消费者(使用lookupd查询)

// 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: "收到了消息:",
	}
	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", "192.168.10.13: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                              // 阻塞
}

为什么生产者发布使用TCP端口(4150),而消费者使用HTTP端口(4161)?

关键区别

  1. 生产者(Producer)- 直接连接 nsqd

    nsqAddress := "192.168.10.13:4150" err := initProducer(nsqAddress)

    producer, err = nsq.NewProducer(str, config)

    生产者直接连接到 nsqd 的 TCP 端口(4150) 不需要通过 lookupd,因为生产者通常已知 nsqd 地址 使用 TCP 协议发布消息,性能更高

  2. 消费者(Consumer)- 通过 lookupd 发现服务

    if err := c.ConnectToNSQLookupd(address); err != nil { // 通过lookupd查询

    err := initConsumer("topic_demo", "first", "192.168.10.13:4161")

    消费者连接到 nsqlookupd 的 HTTP 端口(4161) 通过 lookupd 查询可用的 nsqd 节点 查询到 nsqd 地址后,消费者会通过 TCP 连接到 nsqd 消费消息

为什么这样设计?

生产者使用 TCP(直接连接 nsqd):

  • 性能:TCP 二进制协议,开销更小
  • 简单:生产者通常知道 nsqd 地址,无需服务发现
  • 实时:直接连接,延迟更低

消费者使用 HTTP(通过 lookupd):

  • 服务发现:需要动态发现可用的 nsqd 节点
  • 高可用:lookupd 维护多个 nsqd 节点列表,支持故障转移
  • 负载均衡:可以从多个 nsqd 节点消费消息

完整流程

生产者:

Producer → TCP(4150) → nsqd (直接发布消息)

消费者:

Consumer → HTTP(4161) → nsqlookupd (查询可用节点)
         ↓
Consumer → TCP(4150) → nsqd (实际消费消息)

总结

  • 生产者:直接连接 nsqd(TCP 4150),发布消息

  • 消费者:先通过 lookupd(HTTP 4161)发现 nsqd,再通过 TCP 连接 nsqd 消费消息

  • 消费者最终也是通过 TCP 连接 nsqd 消费消息,只是先通过 lookupd 找到 nsqd 的地址。这是 NSQ 的分布式架构设计:生产者直接连接,消费者通过服务发现动态连接。

消费者2(直接连NSQD查询)

// nsq_consumer/main.go
package main

import (
	"fmt"
	"os"
	"os/signal"
	"syscall"

	"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 // 直接连接NSQD时不需要此配置
	c, err := nsq.NewConsumer(topic, channel, config)
	if err != nil {
		fmt.Printf("create consumer failed, err:%v\n", err)
		return
	}
	consumer := &MyHandler{
		Title: "收到了消息:",
	}
	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() {
      // 使用ConnectToNSQLookupd 端口是 4161; 使用 ConnectToNSQD端口是 4150
	err := initConsumer("topic_demo", "first", "192.168.10.13:4150")
	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                              // 阻塞
}

两种连接方式的区别

方式连接地址端口适用场景
直接连接nsqd 地址4150 (TCP)单节点、已知 nsqd 地址
通过 lookupdlookupd 地址4161 (HTTP)多节点、需要服务发现、高可用

现在消费者会直接连接到 nsqd 的 TCP 端口 4150,不再通过 lookupd 进行服务发现。注意:直接连接方式适用于单节点或已知 nsqd 地址的场景。如果 nsqd 节点故障,消费者无法自动切换到其他节点。

使用 ConnectToNSQD端口为什么是4150

NSQ 端口说明:

服务TCP 端口HTTP 端口用途
nsqd41504151消息队列节点(实际存储和传输消息)
nsqlookupd41604161服务发现(管理 nsqd 节点列表)

ConnectToNSQD() 需要连接到 nsqd 节点,而不是 lookupd

nsqd 的 TCP 端口是 4150,用于消息传输

如果使用 4160(lookupd 的 TCP 端口),连接会失败,因为: ConnectToNSQD() 期望连接到 nsqd 协议

lookupd 使用不同的协议和端口

注意:

  • 消费者无法使用 NSQLookupd 的 4160 TCP 端口,
  • ConnectToNSQLookupd 方法只支持 HTTP 协议,必须使用 HTTP 端口(4161)
  • 4160 是 TCP 端口,用于 NSQD 注册,不是给客户端查询用的

参考文档: www.cnblogs.com/you-men/p/1… blog.csdn.net/chinawangfe… blog.csdn.net/u012189747/… cloud.tencent.com/developer/a… zhuanlan.zhihu.com/p/115368450