RabbitMQ

  • 在上一节中我们创建了工作队列,并且假设每一个任务都能够准确的到达对应的worker。在本节中我们将介绍如何将一个消息传递到多个消费者,这也就是所说的发布订阅模式
  • 为了验证该模式我们使用两个建立一个简单的打印系统,一个负责发出消息,另一个负责接收并打印。在该系统多个receiver中,其中一个直接将日志写入到硬盘,另一个负责从屏幕上查看日志
  • 在之前的简介中,我们可以作以下简单总结:
    • 生产者负责发送消息
    • 队列是一个存储消息的缓冲区
    • 消费者负责接收消息

RabbitMQ消息传递模型的核心思想是,生产者永远不会将任何消息直接发送到队列,实际上,通常生产者甚至不知道消息是否被传递到某个队列。

相反,生产者只能向交换器发送消息。交换器一边接收来自生产者发布的消息一边将消息放入到队列当中。可以通过exchangeType来设置交换器对消息的处理,比如拼接到指定的队列,或是拼接到多个队列中,或是丢弃。

exchange Type有以下几种:direct,topic,headers,fanout。我们先使用最后一种创建相应的交换器并取名logs:

err = ch.ExchangeDeclare(
"logs", // name
"fanout", // type
true, // durable
false, // auto-deleted
false, // internal
false, // no-wait
nil, // arguments
)

fanout模式就是广播所有接收到的消息到它已知的所有队列当中

使用以下命令可以罗列RabbitMQ中所有的交换器:
sudo rabbitmqctl list_exchanges

在之前的例子中我们没有使用交换器但是依旧可以发送消息到队列当中,说明我们已经使用了默认的交换器,我们可以看下以前的代码:

err = ch.Publish(
"", // exchange
q.Name, // routing key
false, // mandatory
false, // immediate
amqp.Publishing{
ContentType: "text/plain",
Body: []byte(body),
})

在这里我们使用了默认的交换器:消息将被依据routering_key指定的名字路由到队列中.

一旦我们定义好了交换器,则可以在生产者发送消息的时候使用:

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),
})

临时队列

我们想要获取所有日志消息不只是子集,同时我们只对当前的信息流感兴趣,为了解决这个问题我们需要两个东西:

首先,我们需要一个新的空的队列不管我们是否有链接Rabbit,我们可以使用一个随机名字创建一个队列,或是让系统指定给我们

其次,一旦我们断开与消费者的链接,队列必须自动删除。

在amqp客户端中,当我们使用一个空的名字创建一个队列的时候:

q, err := ch.QueueDeclare(
"", // name
false, // durable
false, // delete when usused
true, // exclusive
false, // no-wait
nil, // arguments
)

当我们得到其返回的队列的时候,队列实例将会包含一个由RabbitMQ产生的名字,差不多这个样子:amq.gen-JzTY20BRgKO-HjmUJj0wLg

当我们链接关闭的时候,队列将被删除因为它被声明为exclusive

绑定

在前面我们已经创建了一个fanout类型的交换器和一个队列,接下来我们我们需要让交换器将消息发送到我们队列中,将交换器(exchange)和队列(queue)关联起来称为绑定

err = ch.QueueBind(
q.Name, // 队列名 name
"", // routing key
"logs", // 交换器名
false,
nil
)

经过以上关联之后,logs交换器就会将消息拼接到我们的队列当中。

罗列出所有的绑定:
rabbitmqctl list_bindings

完整代码如下:

emit.go

package main

import (
"fmt"
"log"
"os"
"strings" "github.com/streadway/amqp"
) func failOnError(err error, msg string) {
if err != nil {
log.Fatalf("%s: %s", msg, err)
}
} func main() {
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
failOnError(err, "Failed to connect to RabbitMQ")
defer conn.Close() 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.go

package main

import (
"github.com/streadway/amqp"
"log"
) func main() {
conn,err := amqp.Dial("amqp://guest:guest@localhost:5672/")
DealWithError(err,"Failed to connect to RabbitMQ")
defer conn.Close() ch,err := conn.Channel()
DealWithError(err,"Failed to open a channel")
defer ch.Close()
//声明交换器
ch.ExchangeDeclare(
"logs",
"fanout",
true,
false,
false,
false,
nil,
)
DealWithError(err,"Failed to declare an exchange")
//声明了队列
q,err := ch.QueueDeclare(
"", //队列名字为rabbitMQ自动生成
false,
false,
true,
false,
nil,
)
DealWithError(err,"Failed to declare an exchange")
//交换器跟队列进行绑定,交换器将接收到的消息放进队列中
err = ch.QueueBind(
q.Name,
"",
"logs",
false,
nil,
)
DealWithError(err,"Failed to bind a queue")
msgs,err := ch.Consume(
q.Name,
"",
true,
false,
false,
false,
nil,
)
DealWithError(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
} func DealWithError(err error,msg string) {
if err != nil {
log.Fatalf("%s: %s", msg, err)
}
}

Go RabbitMQ(三)发布订阅模式的更多相关文章

  1. RabbitMQ的发布订阅模式(Publish/Subscribe)

    一.发布/订阅(Publish/Subscribe)模式 发布订阅是我们经常会用到的一种模式,生产者生产消息后,所有订阅者都可以收到.RabbitMQ的发布/订阅模型图如下: 1.该模式下生产者并不是 ...

  2. RabbitMQ入门-发布订阅模式

    兔子的Publish/Subscribe是这样的: 有个生产者P,X代表交换机,交换机绑定队列,消费者从队列中取得消息.每次有消息,先发到交换机中,然后由交换机负责发送到它已知的队列中. 生产者代码: ...

  3. rabbitmq (三) 发布/订阅

    rabbitmq的目的并不是让生产者把消息直接发到队列里面去, 这样不能实现解耦的目的,也不利于程序的扩展. 所以就有交换机(exchanges)的概念. 交换机有几种类型:direct, topic ...

  4. 第三节: List类型的介绍、生产者消费者模式、发布订阅模式

    一. List类型基础 1.介绍 它是一个双向链表,支持左进.左出.右进.右出,所以它即可以充当队列使用,也可以充当栈使用. (1). 队列:先进先出, 可以利用List左进右出,或者右进左出(Lis ...

  5. python使用rabbitMQ介绍三(发布订阅模式)

    一.模式介绍 在前面的例子中,消息直接发送到queue中. 现在介绍的模式,消息发送到exchange中,消费者把队列绑定到exchange上. 发布-订阅模式是把消息广播到每个消费者,每个消费者接收 ...

  6. RabbitMQ学习第三记:发布/订阅模式(Publish/Subscribe)

    工作队列模式是直接在生产者与消费者里声明好一个队列,这种情况下消息只会对应同类型的消费者. 举个用户注册的列子:用户在注册完后一般都会发送消息通知用户注册成功(失败).如果在一个系统中,用户注册信息有 ...

  7. RabbitMQ指南之三:发布/订阅模式(Publish/Subscribe)

    在上一章中,我们创建了一个工作队列,工作队列模式的设想是每一条消息只会被转发给一个消费者.本章将会讲解完全不一样的场景: 我们会把一个消息转发给多个消费者,这种模式称之为发布-订阅模式. 为了阐述这个 ...

  8. RabbitMQ六种队列模式-发布订阅模式

    前言 RabbitMQ六种队列模式-简单队列RabbitMQ六种队列模式-工作队列RabbitMQ六种队列模式-发布订阅 [本文]RabbitMQ六种队列模式-路由模式RabbitMQ六种队列模式-主 ...

  9. NetMQ(三): 发布订阅模式 Publisher-Subscriber

    ZeroMQ系列 之NetMQ 一:zeromq简介 二:NetMQ 请求响应模式 Request-Reply 三:NetMQ 发布订阅模式 Publisher-Subscriber 四:NetMQ ...

随机推荐

  1. Centos7.0安装KVM实践

    1.背景 近日将主要精力放在Linux下测试MySQL Replication,宿主机安装了Centos7.0系统,需要安装三台虚拟机.因此,尝试了一下在Centos7.0上安装虚机机. 2.安装步骤 ...

  2. SQL Server数据类型总结

    1.char char [(n)]存储固定长度的非Unicode字符串数据.n定义字符串长度,并且必须是1到8,000之间的值.存储大小为n个字节. 2.varchar varchar [(n | m ...

  3. Js 事件详解

    1.事件流 1.1 事件流 描述的是在页面中接受事件的顺序 1.2 事件冒泡 由最具体的元素接收,然后逐级向上传播最不具体的元素的节点(文档) 1.3 事件捕获 最不具体的节点先接收事件,而最具体的节 ...

  4. 在VC++中执行VBS代码

    此代码来自https://blog.csdn.net/zhu2695/article/details/13770671 作者: zhu2695   时间:2013年10月31日 13:08:41 #i ...

  5. 配置AndroidStdio的开发环境

    http://blog.csdn.net/siwuxie095/article/details/53431818

  6. linux将指令加入开机启动或加入环境变量

    以mongodb运行指令为例,/usr/local/webserver/mongodb/bin/mongo 1,linux将指令加入环境变量PATH 简单说PATH就是一组路径的字符串变量,当你输入的 ...

  7. Using the JDBC Driver

    Download JDBC Driver This section provides quick start instructions for making a simple connection t ...

  8. [CSS3] 3D桃心

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  9. 【表单验证】基于jQuery的高度灵活的表单验证(无UI)

    表单验证是前端开发过程中常见的一个需求,产品需求.业务逻辑的不同,表单验证的方式方法也有所区别.而最重要的是我们要清楚,表单验证的核心原则是--错误信息提示准确,并且尽可能少的打扰/干扰用户的输入和体 ...

  10. package.json和bower的参数解释

    package.json和bower的参数解释   一.package.json解释: package.json是用来声明项目中使用的模块,这样新的环境部署时,只要在package.json文件所在的 ...