开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第15天,点击查看活动详情
今天接着来学习下RabbitMQ中另一个类型的交换器Direct,它只会将消息路由到那些 BindingKey 和 RoutingKey 完全匹配的队列中,结构如下所示:
绑定
err = ch.QueueBind(
q.Name, // queue name
"", // routing key
"logs", // exchange
false,
nil)
绑定是交换器和队列之间的关系,换句话说队列对这个交换器的信息比较感兴趣,绑定可以使用额外的routing key参数,为了避免与Publish参数混淆,这里可叫做binding key
err = ch.QueueBind(
q.Name, // queue name
"warn", // binding key
"logs", // exchange
false,
nil)
直连交换器
上一篇文章中提到的fanout类型交换器是将所有的消息广播到所有消费者,这次的direct交换器可以根据消息的严重性来过滤消息,希望日志信息写入文件只接收严重错误,不会接收warning或者info信息,使用fanout就不能做到这个事情
在这个模式中,使用warn路由键发布到交换器的消息会被路由到Q1,路由键debug,info消息将发送到Q2,所有其他键的消息将会被丢弃
多重绑定
使用相同的binding key绑定多个队列是完全可行,可以使用warn键在交换器和队列中进行绑定,这种情况类似于fanout类型的交换器,可以将信息广播到所有匹配的队列,带有warn的路由键将消息同时发送给Q1和Q2
发送日志
我们将在日志系统中使用这个模型。我们将发送消息到direct交换器,而不是fanout,通过日志等级来作为路由键,消费者可以根据需求来接受的日志等级
首先创建一个交换器
err = ch.ExchangeDeclare(
"logs", // name
"direct", // type
true, // durable
false, // auto-deleted
false, // internal
false, // no-wait
nil, // arguments
)
send.go代码
func getParam(args []string) (res string) {
if (len(args) < 3) || os.Args[2] == "" {
res = "YYQQ"
} else {
res = strings.Join(args[2:], "")
}
return
}
func getFrom(args []string) (res string) {
if (len(args) < 2) || os.Args[1] == "" {
res = "YYQQ info"
} else {
res = os.Args[1]
}
return
}
func main() {
conn, err := amqp.Dial("amqp://admin:admin@localhost:5672/")
DoError(err, "Failed connect to RabbitMQ!")
defer conn.Close()
ch, err := conn.Channel()
DoError(err, "Failed to open a channel")
defer ch.Close()
err = ch.ExchangeDeclare(
"logs_direct",
"direct",
true,
false,
false,
false,
nil)
DoError(err, "Failed to declare a exchange")
content := getParam(os.Args) //从命令行中返回数据
err = ch.Publish(
"logs_direct",
getFrom(os.Args),
false,
false,
amqp.Publishing{
ContentType: "text/plain",
Body: []byte(content),
})
DoError(err, "Failed to publish a message")
}
receive.go代码
func main() {
conn, err := amqp.Dial("amqp://admin:admin@localhost:5672/")
DoError(err, "Failed to connect to RabbitMQ")
defer conn.Close()
ch, err := conn.Channel()
DoError(err, "Failed to open a channel")
defer ch.Close()
err = ch.ExchangeDeclare(
"logs_direct",
"direct",
true,
false,
false,
false,
nil)
DoError(err, "Failed to declare a exchange")
queue, err := ch.QueueDeclare(
"", // name
false, // durable
false, // delete when unused
true, // exclusive
false, // no-wait
nil, // arguments
)
DoError(err, "Failed to declare a queue")
fmt.Println("queue.name: ", queue.Name)
if len(os.Args) < 2 {
log.Printf("Usage: %s [info] [warning] [error]", os.Args[0])
os.Exit(0)
}
//建议多个绑定关系
for _, bindkey := range os.Args[1:] {
log.Printf("Binding queue %s to exchange %s with routing key %s",
queue.Name, "logs_direct", bindkey)
err = ch.QueueBind(
queue.Name, // queue name
bindkey, // routing key
"logs_direct", // exchange
false,
nil)
DoError(err, "Failed to bind a queue")
}
content, err := ch.Consume(
queue.Name, // queue
"", // consumer
true, // auto-ack
false, // exclusive
false, // no-local
false, // no-wait
nil, // args
)
DoError(err, "Failed to register a consumer")
var wait chan struct{}
go func() {
for msg := range content {
log.Printf("Received a message %s", msg.Body)
}
}()
<-wait
}
如果你只想将warning和err(而不是info)级别的日志消息保存到文件中,只需打开控制台并输入:
go run receive.go warning error > logs_direct.log
如果要在控制台上输出Info的日志消息,请打开一个新终端并执行以下操作:
go run send.go info "this is a info message"
go run receive.go info
结果如图所示:
例如,要发出error和warning日志消息,只需输入:
go run send.go error warning "this is a error and warning message."
go run receive.go warning error
结果如图所示:
绑定交换机与队列的关系---绑定时,明确绑定键 BindingKey
总结
今天浅谈了Go与RabbitMQ(四),还有很多相关的知识后面会继续深入,对于刚入门go语言的我来说,还有许多地方需要学习,有错误的地方欢迎大家指出,共同进步!!