RabbitMQ官方教程三 Publish/Subscribe(GOLANG语言实现)
RabbitMQ官方教程三 Publish/Subscribe(GOLANG语言实现)
在上一个教程中,我们创建了一个工作队列。 工作队列背后的假设是,每个任务都恰好交付给一个worker处理。 在这一部分中,我们将做一些完全不同的事情-我们将消息传达给多个消费者。 这种模式称为“发布/订阅”。
为了说明这种模式,我们将构建一个简单的日志记录系统。 它由两个程序组成-第一个程序将发出日志消息,第二个程序将接收并打印它们。
在我们的日志系统中,消费者每个运行副本都将获得消息。 这样,我们将能够运行一个消费者并将日志写入到磁盘。 同时我们将能够运行另一个消费者并在屏幕上查看日志。
本质上,已发布的日志消息将被广播到所有接收者。
交换器
在本教程的前面部分中,我们向队列发送消息和从队列接收消息。 现在是时候在Rabbit中引入完整的消息传递模型了。
让我们快速回顾一下先前教程中介绍的内容:
- 生产者是发送消息的用户应用程序。
- 队列是存储消息的缓冲区。
- 使用者是接收消息的用户应用程序。
RabbitMQ消息传递模型中的核心思想是生产者从不将任何消息直接发送到队列。 实际上,生产者经常甚至根本不知道是否将消息传递到任何队列。
实际上,生产者只能将消息发送到交换器。 交换器的功能非常简单。 一方面,它接收来自生产者的消息,另一方面,将它们推入队列。 交换器必须确切知道如何处理收到的消息。 是否应将其附加到特定队列? 是否应该将其附加到许多队列中? 还是应该丢弃它。 这些规则由交换类型定义。
有几种交换类型可用:direct, topic, headers and fanout。 我们将集中讨论最后一个-fanout。 让我们创建这种类型的交换,并将其称为日志:
err = ch.ExchangeDeclare(
"logs", // name
"fanout", // type
true, // durable
false, // auto-deleted
false, // internal
false, // no-wait
nil, // arguments
)
fanout非常简单。 正如您可能从名称中猜测的那样,它只是将接收到的所有消息广播到它知道的所有队列中。 而这正是我们日志记录所需要的。
交换器列表
要列出服务器上的交换器,您可以运行rabbitmqctl命令:
sudo rabbitmqctl list_exchanges
在此列表中,将有一些amq。*交换和默认(未命名)交换。 这些是默认创建的,但是您现在不太可能需要使用它们。
默认交换器
在本教程的前面部分中,我们对交换器一无所知,但仍然能够将消息发送到队列。 之所以可能,是因为我们使用的是默认交换器,该交换器由空字符串("")标识。
临时队列
您可能还记得以前我们使用的是具有特定名称的队列(还记得hello和task_queue吗?)。 能够命名队列对我们至关重要-我们需要将worker指向同一队列。 当您想在生产者和消费者之间共享队列时,给队列命名很重要。
但这不是我们的记录器的情况。 我们希望听到所有日志消息,而不仅仅是它们的一部分。 我们也只对当前正在发送的消息感兴趣,而对旧消息不感兴趣。 为了解决这个问题,我们需要两件事。
1.首先,无论何时连接到Rabbit,我们都需要一个全新的空队列。 为此,我们可以创建一个具有随机名称的队列,或者甚至更好-让服务器为我们选择一个随机队列名称。
2.其次,一旦我们断开了消费者的连接,队列将被自动删除。
在amqp客户端中,当我们以空字符串形式提供队列名称时,我们将使用生成的名称创建一个非持久化的队列:
q, err := ch.QueueDeclare(
"", // name
false, // durable
false, // delete when unused
true, // exclusive
false, // no-wait
nil, // arguments
)
当方法返回时,队列实例包含RabbitMQ生成的随机队列名称。 例如,它可能看起来像amq.gen-JzTY20BRgKO-HjmUJj0wLg。
当声明它的连接关闭时,该队列将被删除,因为它被声明为独占。
绑定
我们已经创建了一个fanout交换器和一个队列。 现在我们需要告诉交换器将消息发送到我们的队列。 交换和队列之间的关系称为绑定。
err = ch.QueueBind(
q.Name, // queue name
"", // routing key
"logs", // exchange
false,
nil,
)
从现在开始,日志交换将消息添加到我们的队列中。
监听绑定
您可以使用以下的命令列出现有的绑定
rabbitmqctl list_bindings
所有代码
产生日志消息的生产者与上一教程看起来没有太大不同。 最重要的变化是,我们现在希望将消息发布到日志交换器,而不是无名的消息交换器。 发送时我们需要提供一个routingKey,但是对于扇出交换,它的值将被忽略。 这是emit_log.go脚本的代码:
package main
import (
"log"
"os"
"strings"
"github.com/streadway/amqp"
)
func failOnError(err error, msg string) {
if err != nil {
log.Fatalf("%s: %s", msg, err)
}
}
func main() {
// 连接RabbitMQ服务器
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
failOnError(err, "Failed to connect to RabbitMQ")
defer conn.Close()
// 创建一个channel
ch, err := conn.Channel()
failOnError(err, "Failed to open a channel")
defer ch.Close()
// 声明交换器
err = ch.ExchangeDeclare(
"logs", // name
"fanout", // type
true, // durable
false, // auto-deleted
false, // internal
false, // no-wait
nil, // arguments
)
failOnError(err, "Failed to declare an exchange")
body := bodyFrom(os.Args)
err = ch.Publish(
"logs", // 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)
}
func bodyFrom(args []string) string {
var s string
if (len(args) < 2) || os.Args[1] == "" {
s = "hello"
} else {
s = strings.Join(args[1:], " ")
}
return s
}
如您所见,建立连接后,我们声明了交换器。 由于禁止发布到不存在的交换器,因此此步骤是必需的。
如果没有队列绑定到交换,消息将丢失,但这对我们来说是可以的。 如果没有消费者在听,我们可以安全地丢弃该消息。
receive_logs.go的代码:
package main
import (
"log"
"github.com/streadway/amqp"
)
func failOnError(err error, msg string) {
if err != nil {
log.Fatalf("%s: %s", msg, err)
}
}
func main() {
// 连接RabbitMQ服务器
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
failOnError(err, "Failed to connect to RabbitMQ")
defer conn.Close()
// 创建一个channel
ch, err := conn.Channel()
failOnError(err, "Failed to open a channel")
defer ch.Close()
// 声明交换器
err = ch.ExchangeDeclare(
"logs", // 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(
"", // name
false, // durable
false, // delete when unused
true, // exclusive
false, // no-wait
nil, // arguments
)
failOnError(err, "Failed to declare a queue")
// 绑定交换器
err = ch.QueueBind(
q.Name, // queue name
"", // routing key
"logs", // 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
)
failOnError(err, "Failed to register a consumer")
forever := make(chan bool)
go func() {
for d := range msgs {
log.Printf(" [x] %s", d.Body)
}
}()
log.Printf(" [*] Waiting for logs. To exit press CTRL+C")
<-forever
}
如果要将日志保存到文件,只需打开控制台并键入:
go run receive_logs.go > logs_from_rabbit.log
如果希望在屏幕上查看日志,请生成一个新的终端并运行:
go run receive_logs.go
当然,要发送日志,请输入:
go run emit_log.go
使用rabbitmqctl list_bindings,您可以验证代码是否确实根据需要创建了绑定和队列。 在运行两个receive_logs.go程序后,您应该看到类似以下内容:
sudo rabbitmqctl list_bindings
# => Listing bindings ...
# => logs exchange amq.gen-JzTY20BRgKO-HjmUJj0wLg queue []
# => logs exchange amq.gen-vso0PVvyiRIL2WoV3i48Yg queue []
# => ...done.
结果的解释很简单:交换日志中的数据进入两个具有服务器分配名称的队列。 这正是我们的意图。
RabbitMQ官方教程三 Publish/Subscribe(GOLANG语言实现)的更多相关文章
- RabbitMQ官方教程二 Work Queues(GOLANG语言实现)
RabbitMQ官方教程二 Work Queues(GOLANG语言实现) 在第一个教程中,我们编写了程序来发送和接收来自命名队列的消息. 在这一部分中,我们将创建一个工作队列,该队列将用于在多个wo ...
- RabbitMQ官方教程一Hello World(GOLANG语言实现)
介绍 RabbitMQ是消息中间件:它接受并转发消息. 您可以将其视为邮局系统:将要发送的邮件放在邮箱中时, 可以确保邮递员最终将邮件传递给收件人. 以此类推,RabbitMQ是一个邮箱,一个邮局和一 ...
- RabbitMQ入门教程(三):Hello World
原文:RabbitMQ入门教程(三):Hello World 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog. ...
- 译: 3. RabbitMQ Spring AMQP 之 Publish/Subscribe 发布和订阅
在第一篇教程中,我们展示了如何使用start.spring.io来利用Spring Initializr创建一个具有RabbitMQ starter dependency的项目来创建spring-am ...
- RabbitMQ官方教程五 Topic(GOLANG语言实现)
在上一教程中,我们改进了日志记录系统. 我们没有使用只能进行虚拟广播的fanout交换器,而是使用直接交换器,并有可能选择性地接收日志. 尽管使用直接交换改进了我们的系统,但它仍然存在局限性-它不能基 ...
- RabbitMQ官方教程四 Routing(GOLANG语言实现)
在上一教程中,我们构建了一个简单的日志记录系统. 我们能够向许多消费者广播日志消息. 在本教程中,我们将向其中添加功能-我们将使仅订阅消息的子集成为可能. 例如,我们将只能将严重错误消息定向到日志文件 ...
- 译:3.RabbitMQ Java Client 之 Publish/Subscribe(发布和订阅)
在上篇 RabbitMQ 之Work Queues (工作队列)教程中,我们创建了一个工作队列,工作队列背后的假设是每个任务都交付给一个工作者. 在这一部分,我们将做一些完全不同的事情 - 我们将向多 ...
- RabbitMQ 之 订阅模式 Publish/Subscribe
模型图 我们之前学习的都是一个消息只能被一个消费者消费,那么如果我想发一个消息 能被多个消费者消费,这时候怎么办? 这时候我们就得用到了消息中的发布订阅模型 在前面的教程中,我们创建了一个工作队列,都 ...
- RabbitMQ+PHP教程
RabbitMQ+PHP 教程一(Hello World) RabbitMQ+PHP 教程二(Work Queues) RabbitMQ+PHP 教程三(Publish/Subscribe) Rabb ...
随机推荐
- leetcode解题报告(30):Detect Capital
描述 Given a word, you need to judge whether the usage of capitals in it is right or not. We define th ...
- 30、提高并行度&广播共享数据
一.提高并行度 1.图解 实际上Spark集群的资源并不一定会被充分利用到,所以要尽量设置合理的并行度,来充分地利用集群的资源.才能充分提高Spark应用程序的性能. Spark会自动设置以文件作为输 ...
- (1)Go变量声明、赋值和常量
先简单了解一下控制台输出,在go中,我们使用fmt包中的函数进行在控制台中打印,一般用以下几个函数 fmt.Print() #不换行输出 fmt.Printf() ...
- Java 冒泡排序算法
public class StringSampleDemo { public static void main(String[] args) { int[] arr = {1, 2, -20, 20, ...
- html中a标签的常见用法
html中a标签的常见用法 一.总结 一句话总结: a.页面跳转 b.使用锚点定位 c.下载文件 二.html中<a>标签的用法 转自或参考:html中<a>标签的用法http ...
- Vscode 保存文件就会自动添加注释
Vscode 保存文件就会自动添加注释 原因是:安装了插件造成的.. 文章来源:刘俊涛的博客 欢迎关注公众号.留言.评论,一起学习. _____________________________ ...
- https://pingcap.com/blog-cn/flame-graph/
https://pingcap.com/blog-cn/flame-graph/ 因为 TiKV 是自己内部使用了 jemalloc,并没有用系统的 malloc,所以我们不能直接用 perf 来探查 ...
- python中的raise用法
date ; 2019-08-22 15:10:56 try: s = None if s is None: print("s shi kong de ") raise NameE ...
- Flutter响应式编程 - RxDart
import 'package:flutter/material.dart'; import 'package:rxdart/rxdart.dart'; import 'dart:async'; cl ...
- ISO/IEC 9899:2011 条款6.5.5——乘法操作符
6.5.5 乘法操作符 语法 1.multiplicative-expression: cast-expression multiplicative-expression * cast-e ...