NSQ是一个基于go语言开发的一个比较流行的分布式的消息队列。
参照下图利用消息队列把业务流程中的非关键流程异步化,从而显著降低业务请求的响应时间。
通过使用消息队列将不同的业务逻辑解耦,降低系统间的耦合,提高系统的健壮性。后续有其他业务要使用订单数据可直接订阅消息队列,提高系统的灵活性。
类似秒杀(大秒)等场景下,某一时间可能会产生大量的请求,使用消息队列能够为后端处理请求提供一定的缓冲区,保证后端服务的稳定性。
nsqlookupd
管理nsqd节点拓扑信息并提供最终一致性的发现服务的守护进程。
nsqlookupd是维护所有nsqd状态、提供服务发现的守护进程。它能为消费者查找特定topic下的nsqd提供了运行时的自动发现服务。 它不维持持久状态,也不需要与任何其他nsqlookupd实例协调以满足查询。因此根据你系统的冗余要求尽可能多地部署nsqlookupd节点。它们小豪的资源很少,可以与其他服务共存。我们的建议是为每个数据中心运行至少3个集群。
nsqd
负责接收、排队、转发消息到客户端的守护进程。它可以独立运行,并且定时向nsqlookupd服务发送心跳。
启动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
。
nsqadmin
nsq的web统计界面,可实时查看集群的统计数据和执行一些管理任务。
一个实时监控集群状态、执行各种管理任务的Web管理平台。 启动nsqadmin
,指定nsqlookupd
地址:
./nsqadmin -lookupd-http-address=127.0.0.1:4161
使用浏览器打开http://127.0.0.1:4271
访问管理界面。
每个nsqd
实例旨在一次处理多个数据流。这些数据流称为“topics”
,一个topic
具有1个或多个“channels”
。每个channel
都会收到topic
所有消息的副本,实际上下游的服务是通过对应的channel
来消费topic
消息。
topic
和channel
不是预先配置的。topic
在首次使用时创建,方法是将其发布到指定topic
,或者订阅指定topic
上的channel
。channel
是通过订阅指定的channel
在第一次使用时创建的。
topic
和channel
都相互独立地缓冲数据,防止缓慢的消费者导致其他channel
的积压(同样适用于topic
级别)。
channel
可以并且通常会连接多个客户端。假设所有连接的客户端都处于准备接收消息的状态,则每条消息将被传递到随机客户端。例如:
总而言之,消息是从topic -> channel
(每个channel
接收该topic
的所有消息的副本)多播的,但是从channel -> consumers
均匀分布(每个消费者接收该channel
的一部分信息)。
go get -u github.com/nsqio/go-nsq
//生产者
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
。
//消费者
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