- AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。
AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。 - RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。
macOS中安装RabbitMQ:
- 安装Erlang环境
brew install erlang
- 安装rabbitmq
brew install rabbitmq
- 安装RabbitMQ可视化监控工具:
cd /usr/local/Celler/rabbitmq/3.8.11
sudo sbin/rabbitmq-plugins enable rabbitmq_management
- 配置环境变量
open ~/.zshrc
// 添加以下两行
export RABBIT_HOME = /usr/local/Celler/3.8.11
export PATH = $PATH:RABBIT_HOME/sbin
source ~/.zshrc
- 后台启动rabbitmq
sudo rabbitmq-server -detached
- 停止rabbitMQ
rabbitmqctl stop
在golang中尝试RabbitMQ的几种模式:
i: 安装amqp库
go get github.com/streadway/amqp
ii: 定义url
// url格式:amqp://账号:密码@rabbitmq服务地址/vhost
const MQURL = "amqp://chensir:chensir@127.0.0.1:5672/chen"
iii: 定义RabbitMQ对象结构体
type RabbitMQ struct {
conn *amqp.Connection
channel *amqp.Channel
// 队列名称
QueueName string
// 交换机
Exchange string
// key
Key string
// 链接信息
Mqurl string
}
1. Simple模式
i: 创建RabbitMQ结构体实例
func NewRabbitMQ(queueName string, exchange string, key string) *RabbitMQ {
rabbitmq := &RabbitMQ{
QueueName: queueName,
Exchange: exchange,
Key: key,
Mqurl: MQURL,
}
var err error
// 创建rabbitmq链接
rabbitmq.conn, err = amqp.Dial(rabbitmq.Mqurl)
rabbitmq.failOnError(err, "创建链接错误")
rabbitmq.channel, err = rabbitmq.conn.Channel()
rabbitmq.failOnError(err, "获取channel失败")
return rabbitmq
}
ii: Simple模式下生产者
func (r *RabbitMQ) PublishSimple(message string){
// 1. 申请队列,如果队列不存在,自动创建;如果存在则跳过创建,保证消息能发送到队列中
_, err := r.channel.QueueDeclare(
r.QueueName,
//控制是否持久化
false,
// 是否自动删除
false,
// 是否具有排他性
false,
// 是否阻塞
false,
// 额外属性
nil)
if err != nil {
fmt.Println(err)
}
// 发送消息到队列中
r.channel.Publish(
r.Exchange,
r.QueueName,
// 如果为true和exchage类型和routKey规则,如果无法找到符合条件的队列,那么会把消息返回给发送者
false,
// 如果为true, 当exchange发送消息到队列后发现队列上没有绑定消费者,则会把消息还给发送者
false,
amqp.Publishing{
ContentType: "text/plain",
Body: []byte(message),
})
}
iii: Simple模式下消费者
func (r *RabbitMQ) ConsumeSimple(){
// 尝试申请队列
_, err := r.channel.QueueDeclare(
r.QueueName,
//控制是否持久化
false,
// 是否自动删除
false,
// 是否具有排他性
false,
// 是否阻塞
false,
// 额外属性
nil)
if err != nil {
fmt.Println(err)
}
// 接收消息
msgs, err := r.channel.Consume(
r.QueueName,
// 用来区分多个消费者的key
"",
// 是否自动应到
true,
// 是否具有排他性
false,
// 如果设置为true,表示不能将同一个connection中发送的消息传递给这个connection中的消费者
false,
// 队列消费是否为阻塞
false,
nil)
if err != nil {
fmt.Println(err)
}
// 消费消息
forever := make(chan bool)
go func(){
for d := range msgs {
log.Printf("Receive a message: %s", d.Body)
}
}()
log.Println("[*] waiting for message, To exit press Ctrl + c")
<-forever
}
2. Work模式:
- Work模式的生产消费和Simple模式相同,只是在Simple模式下,可以创建多个消费者对消息队列进行消费。在Work模式下,消息只能被一个消费者消费掉
3. 订阅模式:
i: 订阅模式下创建RabbitMQ实例
func NewRabbitMQPubSub(exchangeName string) *RabbitMQ {
// 指定交换机名称, 而队列名称则为空即可
rabbitmq := NewRabbitMQ("", exchangeName, "")
var err error
rabbitmq.conn, err = amqp.Dial(rabbitmq.Mqurl)
rabbitmq.failOnError(err, "failed to connect rabbitmq")
// 获取channel
rabbitmq.channel, err = rabbitmq.conn.Channel()
rabbitmq.failOnError(err, "failed to open a channel")
return rabbitmq
}
ii: 订阅模式下生产信息
func (r *RabbitMQ) PublishPub(message string){
// 尝试创建交换机
err := r.channel.ExchangeDeclare(
r.Exchange,
// 交换机类型为广播类型
"fanout",
true,
false,
false,
false,
nil)
r.failOnError(err, "failed to declare an exchange")
// 发送消息
err = r.channel.Publish(
r.Exchange,
"",
false,
false,
amqp.Publishing{
ContentType: "text/plain",
Body: []byte(message),
})
r.failOnError(err, "failed to send a message")
}
iii: 订阅模式下接收消息
func (r *RabbitMQ) ReceiveSub(){
err := r.channel.ExchangeDeclare(
r.Exchange,
"fanout",
true,
false,
// YES表示这个exchange不可以被client用来推送消息,仅用来进行exchange和exchange之间的绑定
false,
false,
nil)
r.failOnError(err, "failed to declare an exchange")
// 创建队列
q, err := r.channel.QueueDeclare(
"", // 随机生产队列名称
false,
false,
true,
false,
nil)
// 绑定队列到exchange中
r.failOnError(err, "failed to declare a queue")
err = r.channel.QueueBind(
q.Name,
// 在Pub/Sub模式下,这里的key要为空
"",
r.Exchange,
false,
nil)
// 消费消息
message, err := r.channel.Consume(
q.Name,
"",
true,
false,
false,
false,
nil)
forever := make(chan bool)
go func(){
for d := range message {
log.Printf("Receive a message: %s", d.Body)
}
}()
log.Println("退出请按: Ctrl + c")
<-forever
}
- 订阅模式下,是通过交换机将生产的消息广播到绑定到交换机上的多个队列中,需要指定创建的交换机类型为“fanout”类型
4. Routing模式:
- Routing模式和订阅模式的代码也类似,不同指出在与:
- 创建RabbitMQ实例时需要指定RoutingKey
func NewRabbitMQRouting(exchangeName string, routingKey string) *RabbitMQ {
rabbitmq := NewRabbitMQ("", exchangeName, routingKey)
var err error
rabbitmq.conn, err = amqp.Dial(rabbitmq.Mqurl)
rabbitmq.failOnError(err, "failed to connect rabbitmq")
// 获取channel
rabbitmq.channel, err = rabbitmq.conn.Channel()
rabbitmq.failOnError(err, "failed to open a channel")
return rabbitmq
}
- 在生产消息和消费消息时创建交换机的过程中都需要指定交换机类型为“direct"
- 在发送消息时,需要指定消费队列的key, 这个key需要和相应的消费队列一一对应
- Routing模式就是在订阅模式的基础上,明确指定消息的消费队列。
-------本文结束