RabbitMQ 概述:
RabbitMQ是实现了高级消息队列协议(AMQP
)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ服务器是用Erlang语言编写的,而群集和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。
RabbitMQ中的几个主要概念:
-
Producer (生产者) : 消息的生产者,投递方
-
Consumer (消费者) : 消息的消费者
-
RabbitMQ Broker (RabbitMQ 代理) : RabbitMQ 服务节点(单机情况中,就是代表RabbitMQ服务器)
-
Queue (队列) : 在RabbitMQ中Queue是存储消息数据的唯一形式
-
Binding (绑定) : RabbitMQ中绑定(Binding)是交换机(exchange)将消息(message)路由给队列(queue)所需遵循的规则。如果要指示交换机“E”将消息路由给队列“Q”,那么“Q”就需要与“E”进行绑定。绑定操作需要定义一个可选的路由键(routing key)属性给某些类型的交换机。路由键的意义在于从发送给交换机的众多消息中选择出某些消息,将其路由给绑定的队列。
-
RoutingKey (路由键) : 消息投递给交换器,通常会指定一个
RoutingKey
,通过这个路由键来明确消息的路由规则
RabbitMQ大致的工作流程:
1.客户端连接到消息队列服务器,打开一个channel。
2.户端声明一个exchange,并设置相关属性。
3.客户端声明一个queue,并设置相关属性。
4.户端使用routing key,在exchange和queue之间建立好绑定关系。
5.客户端投递消息到exchange。
具体流程详见:Go语言与RabbitMQ - 简书 (jianshu.com)
扇型交换机:(订阅/发布)
扇型交换机(funout exchange)将消息路由给绑定到它身上的所有队列,而不理会绑定的路由键。如果N个队列绑定到某个扇型交换机上,当有消息发送给此扇型交换机时,交换机会将消息的拷贝分别发送给这所有的N个队列。扇型用来交换机处理消息的广播路由(broadcast routing)。
因为扇型交换机投递消息的拷贝到所有绑定到它的队列,所以他的应用案例都极其相似:
- 大规模多用户在线(MMO)游戏可以使用它来处理排行榜更新等全局事件
- 体育新闻网站可以用它来近乎实时地将比分更新分发给移动客户端
- 分发系统使用它来广播各种状态和配置更新
- 在群聊的时候,它被用来分发消息给参与群聊的用户。(AMQP没有内置presence的概念,因此XMPP可能会是个更好的选择)
扇形交换机(fanout )没有路由键,会直接把接收到的message发送给所有绑定到的所有队列中
废话不说,上代码
我这里实现的是给多个用户,发送同一条消息,在数据库中记录消息信息以及用户id和消息id,数据可在postman中测试
1.首先封装连接RebbitMQ的方法
安装RabbitMQ扩展包
go get github.com/streadway/amqp
package lib
import (
"github.com/streadway/amqp"
"log"
)
func RabbitMQConn() (conn *amqp.Connection, err error) {
//RabbitMq分配的用户名
user := "guest"
//RabbitMq分配的密码
password := "guest"
//RabbitMq Broker ip地址
host := "127.0.0.1"
//RabbitMq Broker 监听端口号
port := "5672"
//连接地址
url := "amqp://" + user + ":" + password + "@" + host + ":" + port + "/"
//新建一个连接
conn, err = amqp.Dial(url)
return conn, err
}
func failOnError(err error, msg string) {
if err != nil {
log.Fatalf("%s: %s", msg, err)
}
}
2.发布
// Fan 发布
func Fan(content string, uids string) error {
//建立连接
conn, err := RabbitMQConn()
if err != nil {
return err
}
defer conn.Close()
ch, err := conn.Channel()
failOnError(err, "Failed to open a channel")
defer ch.Close()
err = ch.ExchangeDeclare(
"message", // name
"fanout", // type
true, // durable
false, // auto-deleted
false, // internal
false, // no-wait
nil, // arguments
)
failOnError(err, "Failed to declare an exchange")
//拼接messageid + 用户ids 28;1,3,5,7;
//bodyFrom(content) 得到返回消息id 但是这里用于拼接字符串所以strconv.Itoa()把int转string
body := strconv.Itoa(bodyFrom(content)) + ";" + uids + ";"
err = ch.Publish(
"message", // exchange
"", // routing key
false, // mandatory
false, // immediate
amqp.Publishing{
ContentType: "text/plain",
Body: []byte(body),
})
failOnError(err, "Failed to publish a message")
log.Printf(" [x] Sent %s", body)
return err
}
func bodyFrom(args string) int {
//拿到消息体入库,返回添加入库后的id
id, _ := models.SendMessage(args)
return id
}
ExchangeDeclare()参数讲解:
name
交换机名称
type
交换机类型
durable
持久化标识
autoDelete
是否自动删除
internal
是否是内置交换机
noWait
是否等待服务器确认
args
其它配置
3.订阅
// Take 订阅
func Take() error {
//建立连接
conn, err := RabbitMQConn()
if err != nil {
return err
}
defer conn.Close()
ch, err := conn.Channel()
failOnError(err, "Failed to open a channel")
defer ch.Close()
err = ch.ExchangeDeclare(
"message", // name
"fanout", // type
true, // durable
false, // auto-deleted
false, // internal
false, // no-wait
nil, // arguments
)
failOnError(err, "Failed to declare an exchange")
q, err := ch.QueueDeclare(
"MessageDo", // name
false, // durable
false, // delete when usused
true, // exclusive
false, // no-wait
nil, // arguments
)
failOnError(err, "Failed to declare a queue")
err = ch.QueueBind(
q.Name, // queue name
"", // routing key
"message", // exchange
false,
nil,
)
failOnError(err, "Failed to bind a queue")
msgs, err := ch.Consume(
q.Name, // queue
"", // consumer
true, // auto-ack
false, // exclusive
false, // no-local
false, // no-wait
nil, // args
)
forever := make(chan bool)
go func() {
for d := range msgs {
log.Printf(" [x] %s", d.Body)
//字节传字符串
s := string(d.Body[:])
MessageDo(s)
}
}()
<-forever
return err
}
func MessageDo(message string) {
// 28;1,3,5,7;
arr := strings.Split(message, ";")
//[0:1]是messageId arr[1:2]是userid
s := strings.Join(arr[0:1], " ")
messageId, _ := strconv.Atoi(s)
uids := strings.Join(arr[1:2], ",")
ids := strings.Split(uids, ",")
for _, v := range ids {
i, _ := strconv.Atoi(v)
models.MessageRecordDo(i, messageId)
}
}
生产者需要bee run 一次,消费者也需要bee run一次,需要分成两个main.go来写
资源:
RabbitMQ官网:
RabbitMQ中文文档:
RabbitMQ中文文档 · RabbitMQ in Chinese (mr-ping.com)
这篇文章部分转自: