NATS 基本使用

本文介绍了NATS的基本使用,包括基于主题的消息机制、发布订阅和请求回复模式、队列组以及如何安装和使用NATS服务端和客户端。通过示例展示了如何在Go中进行消息的发布、同步和异步订阅、取消订阅、回复和队列组操作,还演示了如何利用NATS搭建一个简单的聊天室。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

NATS 是一个开源的分布式消息队列系统,基于消息发布订阅机制。

使用NATS我们的程序可以在不同的环境,不同的语言之间进行通信。客户端一般使用一个URL连接到NATS服务端,然后订阅消息或者发布消息。

基于主题的消息机制

NATS的消息基于subject,发送的消息和订阅的消息都有一个subject标记,简单来说,subject就是一串字符串,发布者和订阅者可以使用这些名称来查找对方

在这里插入图片描述

NATS 服务端保留了几个特殊的字符,规范的subject只包括字母数字.,并且区分大小写不能包含空格。

主题层次

使用.我们可以创建一个具有层次的主题,如下:

time.us
time.us.east
time.us.east.atlanta
time.eu.east
time.eu.warsaw

通配符

NATS提供了两种通配符,订阅者可以使用通配符同时订阅多个主题

  • *:匹配单个符号
    • 同一个subject中可以出现多次
    • time.*.east 可以匹配time.us.east也可以匹配time.eu.east,但是不能匹配time.us.xxx.east
  • >:可以匹配多个符号,但是只能出现在主题的最后部分
    • 同一个subject中只能出现一次
    • time.us.>可以匹配time.us.east,也可以匹配time.us.east.atlanta

发布订阅模式

NATS实现了一对多通信的发布订阅消息分发模型,publisher在一个主题上发布一条信息之后,所有的subscriber都可以接收到对应的消息。

在这里插入图片描述

请求回复模式

Request-Reply是现代分发系统中的常见模式。请求发送之后,应用或者在超时时间里等待消息的到来,或者异步接收响应。

NATS支持多个响应,其中第一个响应被使用,后面的消息系统会有效的忽略掉,这能够让拥有多个响应器的复杂系统减少响应时延和抖动。

在这里插入图片描述

队列组

NATS提供了内置的负载均衡–分布式队列。使用队列订阅者可以在一组订阅者之间平衡的消耗消息,提供应用程序容错和扩展工作负载处理。

创建一个队列订阅的时候,订阅者注册一个队列名称,所有有相同队列名称的订阅者组成了一个队列组,当注册的主题消息发布的时候,队列组中的仅有一个成员会接收到消息。

使用

服务端安装

NATS server 安装十分简单,我们有多种方式安装,下面提供几种方式:

二进制文件

Release 界面下载对应系统下的zip文件,解压之后运行即可。

Docker安装

使用下面的命令可以运行一个NATS server

docker pull nats:latest
docker run -p 4222:4222 -it nats:latest

客户端

首先我们需要下载对应的包,NATS客户端支持多门语言,比如Java,Ruby,Rust等等,这里以Go为例子

go get github.com/nats-io/nats.go/

安装之后,我们可以连接到服务端,需要服务端已经开启

// 默认情况下,我们连接到`nats://localhost:4222`即可
// 我们也可以指定地址  nats.Connect("nats://demo.nats.io:4222")
// 或者 nats.Connect("demo.nats.io")
nc, err := nats.Connect(nats.DefaultURL)

if err != nil {
    log.Fatal(err)
}
defer nc.Close()

我们也可以连接到一个cluster里面

servers := []string{"nats://127.0.0.1:1222", "nats://127.0.0.1:1223", "nats://127.0.0.1:1224"}

nc, err := nats.Connect(strings.Join(servers, ","))

然后我们就可以通过server进行消息的发布和订阅了,为了能够更加容易的看到结果,我们可以下载对应的 tools

发布一个消息

打开另一个终端,使用go-nats-examples中提供的nats-sub订阅一个subject,使用下面的代码发布一个消息


err = nc.Publish("demo.test", []byte("Hello nats !"))
if err != nil {
  log.Fatalln("Publish message err: ", err)
}
log.Println("Publish message success")
同步订阅消息

使用nats-pub发布一个消息,下面的代码可以接收到对应的消息

// 订阅主题
sub, err := nc.SubscribeSync("demo.test")
if err != nil {
  log.Fatal(err)
}

// 等待消息,并且设置超时时间
msg, err := sub.NextMsg(10 * time.Second)
if err != nil {
  log.Fatal(err)
}

log.Printf("Reply: %s", msg.Data)
异步订阅消息
// 用来等待消息到来
wg := sync.WaitGroup{}
wg.Add(1)

// 异步订阅
if _, err := nc.Subscribe("demo.test", func(m *nats.Msg) {
    wg.Done()
}); err != nil {
    log.Fatal(err)
}

wg.Wait()
取消订阅

如果我们对一个主题不在感兴趣,那么我们可以取消订阅

sub, err := nc.SubscribeSync("demo.test")
if err != nil {
    log.Fatal(err)
}
// 同步订阅取消
if err := sub.Unsubscribe(); err != nil {
    log.Fatal(err)
}

sub, err = nc.Subscribe("demo.test", func(_ *nats.Msg) {})
if err != nil {
    log.Fatal(err)
}
// 异步订阅取消
if err := sub.Unsubscribe(); err != nil {
    log.Fatal(err)
}

我们也可以在接收到特定数量的消息之后取消订阅

n := 10 
if err := sub.AutoUnsubscribe(n); err != nil {
    log.Fatal(err)
}
回复消息

接收到消息之后,我们还可以对该消息进行回复


timeAsBytes := []byte(time.Now().String())

// 将时间作为响应
msg.Respond(timeAsBytes)
队列组消息

使用队列接收消息,多个subscriber不会接收到同样的消息,使用方式和subscribe类似

queue, err := nc.QueueSubscribeSync("demo.test", "queue")
if err != nil {
  log.Fatal(err)
}
msg, err := queue.NextMsg(time.Second * 10)
if err != nil {
  log.Fatal(err)
}
log.Printf("Msg: %s", msg.Data)
响应式发布

我们发布消息的时候,有时候需要用户接收到之后进行回复

nc.PublishRequest("demo.test.request", "demo.test.response", []byte("PublishRequest"))

// 然后订阅者接收到消息之后,进行Response
sub, err := nc.SubscribeSync("demo.test.request")
if err != nil {
  log.Fatalln(err)
}

msg, err := sub.NextMsg(time.Second)

msg.Respond([]byte("response"))

基于nats搭建一个聊天室

聊天室中,我们需要接收一个房间中所有用户的消息,nats的发布订阅功能正好满足我们的需求,下面是一个简单的聊天程序

package main

import (
	"bufio"
	"fmt"
	"log"
	"os"

	"github.com/nats-io/nats.go"
)


func main() {
	nc, err := nats.Connect(nats.DefaultURL)
	if err != nil {
		log.Fatalln(err)
	}
	defer nc.Close()
	reader := bufio.NewReader(os.Stdin)
	fmt.Print("Input your username: ")
	username, err := reader.ReadString('\n')
	if err != nil {
		log.Fatalln(err)
	}

  // 发布用户加入房间
	err = nc.Publish("chatroom.join", []byte(fmt.Sprintf("welcome, %s", username)))
	if err != nil {
		log.Fatalln(err)
	}
	nc.Flush()

  // 订阅用户加入房间
	nc.Subscribe("chatroom.join", func(msg *nats.Msg) {
		fmt.Printf("%s", msg.Data)
	})

  // 订阅用户发送消息
	nc.Subscribe("chatroom.msg", func(msg *nats.Msg) {
		fmt.Printf("recv: %s", msg.Data)
	})

	for {
    // 读取用户发送的消息
		msg, err := reader.ReadBytes('\n')
		if err != nil {
			log.Fatalln(err)
		}

    // 发送消息
		nc.Publish("chatroom.msg", msg)
		nc.Flush()
	}
}

上面的代码虽然能够实现一个简单的聊天功能,但是还有很多不足之处,比如自己也会收到同样的消息,比如提示内容,不过基本的聊天还是可以的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值