工作队列模式是直接在生产者与消费者里声明好一个队列,这种情况下消息只会对应同类型的消费者。

  举个用户注册的列子:用户在注册完后一般都会发送消息通知用户注册成功(失败)。如果在一个系统中,用户注册信息有邮箱、手机号,那么在注册完后会向邮箱和手机号都发送注册完成信息。利用MQ实现业务异步处理,如果是用工作队列的话,就会声明一个注册信息队列。注册完成之后生产者会向队列提交一条注册数据,消费者取出数据同时向邮箱以及手机号发送两条消息。但是实际上邮箱和手机号信息发送实际上是不同的业务逻辑,不应该放在一块处理。这个时候就可以利用发布/订阅模式将消息发送到转换机(EXCHANGE),声明两个不同的队列(邮箱、手机),并绑定到交换机。这样生产者只需要发布一次消息,两个队列都会接收到消息发给对应的消费者。

1、什么是发布/订阅模式(Publish/Subscribe)

  简单解释就是,可以将消息发送给不同类型的消费者。做到发布一次,消费多个。下图取自于官方网站(RabbitMQ)的发布/订阅模式的图例

P:消息的生产者

X:交换机

红色:队列

C1,C2:消息消费者

下面是利用用户注册解释的该模式。(先运行两个消费者,在运行生产者。如果没有提前将队列绑定到交换机,那么直接运行生产者的话,消息是不会发到任何队列里的

2、生产者(Send)代码

public class Send
{
private final static String EXCHANGE_NAME = "test_exchange_fanout"; public static void main(String[] args)
{
try
{
//获取连接
Connection connection = ConnectionUtil.getConnection();
//从连接中获取一个通道
Channel channel = connection.createChannel();
//声明交换机(分发:发布/订阅模式)
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
//发送消息
for (int i = ; i < ; i++)
{
String message = "this is user registe message" + i;
System.out.println("[send]:" + message);
//发送消息
channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("utf-8"));
Thread.sleep( * i);
}
channel.close();
connection.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
} 运行结果:

[send]:this is user registe message0
[send]:this is user registe message1
[send]:this is user registe message2
[send]:this is user registe message3
[send]:this is user registe message4
[send]:this is user registe message5
[send]:this is user registe message6
[send]:this is user registe message7
[send]:this is user registe message8
[send]:this is user registe message9

3、消费者1(ReceiveEmail)

public class ReceiveEmail
{
//交换机名称
private final static String EXCHANGE_NAME = "test_exchange_fanout"; //队列名称
private static final String QUEUE_NAME = "test_queue_email"; public static void main(String[] args)
{
try
{ //获取连接
Connection connection = ConnectionUtil.getConnection();
//从连接中获取一个通道
final Channel channel = connection.createChannel();
//声明交换机(分发:发布/订阅模式)
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//将队列绑定到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
//保证一次只分发一个
int prefetchCount = ;
channel.basicQos(prefetchCount);
//定义消费者
DefaultConsumer consumer = new DefaultConsumer(channel)
{
//当消息到达时执行回调方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
byte[] body) throws IOException
{
String message = new String(body, "utf-8");
System.out.println("[email] Receive message:" + message);
try
{
//消费者休息2s处理业务
Thread.sleep();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
System.out.println("[1] done");
//手动应答
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
//设置手动应答
boolean autoAck = false;
//监听队列
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
catch (IOException e)
{
e.printStackTrace();
}
} }

运行结果:
[email] Receive message:this is user registe message0
[1] done
[email] Receive message:this is user registe message1
[1] done
[email] Receive message:this is user registe message2
[1] done
[email] Receive message:this is user registe message3
[1] done
[email] Receive message:this is user registe message4
[1] done
[email] Receive message:this is user registe message5
[1] done
[email] Receive message:this is user registe message6
[1] done
[email] Receive message:this is user registe message7
[1] done
[email] Receive message:this is user registe message8
[1] done
[email] Receive message:this is user registe message9
[1] done

4、消费者2(ReceivePhone)

public class ReceivePhone
{
//交换机名称
private final static String EXCHANGE_NAME = "test_exchange_fanout"; //队列名称
private static final String QUEUE_NAME = "test_queue_phone"; public static void main(String[] args)
{
try
{ //获取连接
Connection connection = ConnectionUtil.getConnection();
//从连接中获取一个通道
final Channel channel = connection.createChannel();
//声明交换机(分发:发布/订阅模式)
channel.exchangeDeclare(EXCHANGE_NAME, "fanout");
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//将队列绑定到交换机
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");
//保证一次只分发一个
int prefetchCount = ;
channel.basicQos(prefetchCount);
//定义消费者
DefaultConsumer consumer = new DefaultConsumer(channel)
{
//当消息到达时执行回调方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
byte[] body) throws IOException
{
String message = new String(body, "utf-8");
System.out.println("[phone] Receive message:" + message);
try
{
//消费者休息2s处理业务
Thread.sleep();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
finally
{
System.out.println("[2] done");
//手动应答
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
//设置手动应答
boolean autoAck = false;
//监听队列
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
catch (IOException e)
{
e.printStackTrace();
}
} }

运行结果:
[phone] Receive message:this is user registe message0
[2] done
[phone] Receive message:this is user registe message1
[2] done
[phone] Receive message:this is user registe message2
[2] done
[phone] Receive message:this is user registe message3
[2] done
[phone] Receive message:this is user registe message4
[2] done
[phone] Receive message:this is user registe message5
[2] done
[phone] Receive message:this is user registe message6
[2] done
[phone] Receive message:this is user registe message7
[2] done
[phone] Receive message:this is user registe message8
[2] done
[phone] Receive message:this is user registe message9
[2] done

总结:

  1、该模式下生产者并不是直接操作队列,而是将数据发送给交换机,由交换机将数据发送给与之绑定的队列。从运行结果中可以看到,两中类型的消费者(Email,Phone)都收到相同数量的消息。

  2、该模式必须声明交换机,并且设置模式:channel.exchangeDeclare(EXCHANGE_NAME, "fanout")  fanout指分发模式(将每一条消息都发送到与交换机绑定的队列。

  3、 队列必须绑定交换机:channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "");

注意:本文仅代表个人理解和看法哟!和本人所在公司和团体无任何关系!

RabbitMQ学习第三记:发布/订阅模式(Publish/Subscribe)的更多相关文章

  1. RabbitMQ系列教程之三:发布/订阅(Publish/Subscribe)(转载)

    RabbitMQ系列教程之三:发布/订阅(Publish/Subscribe) (本教程是使用Net客户端,也就是针对微软技术平台的) 在前一个教程中,我们创建了一个工作队列.工作队列背后的假设是每个 ...

  2. RabbitMQ系列教程之三:发布/订阅(Publish/Subscribe)

    (本教程是使用Net客户端,也就是针对微软技术平台的)   在前一个教程中,我们创建了一个工作队列.工作队列背后的假设是每个任务会被交付给一个[工人].在这一部分我们将做一些完全不同的事情--我们将向 ...

  3. 学习ActiveMQ(三):发布/订阅模式(topic)演示

    1.在这个项目中新增两个java类,主题生产者和主题消费者: 2.和点对点的代码差别并不大,所以将消费者和生产者的分别代码拷入新增的java类中,再修改就好了. appProducerTopic代码: ...

  4. Mina、Netty、Twisted一起学(七):发布/订阅(Publish/Subscribe)

    消息传递有很多种方式,请求/响应(Request/Reply)是最常用的.在前面的博文的例子中,很多都是采用请求/响应的方式,当服务器接收到消息后,会立即write回写一条消息到客户端.HTTP协议也 ...

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

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

  6. Go RabbitMQ(三)发布订阅模式

    RabbitMQ 在上一节中我们创建了工作队列,并且假设每一个任务都能够准确的到达对应的worker.在本节中我们将介绍如何将一个消息传递到多个消费者,这也就是所说的发布订阅模式 为了验证该模式我们使 ...

  7. ActiveMQ入门系列三:发布/订阅模式

    在上一篇<ActiveMQ入门系列二:入门代码实例(点对点模式)>中提到了ActiveMQ中的两种模式:点对点模式(PTP)和发布/订阅模式(Pub & Sub),详细介绍了点对点 ...

  8. RabbitMQ学习第四记:路由模式(direct)

    1.什么是路由模式(direct) 路由模式是在使用交换机的同时,生产者指定路由发送数据,消费者绑定路由接受数据.与发布/订阅模式不同的是,发布/订阅模式只要是绑定了交换机的队列都会收到生产者向交换机 ...

  9. RabbitMQ入门:发布/订阅(Publish/Subscribe)

    在前面的两篇博客中 RabbitMQ入门:Hello RabbitMQ 代码实例 RabbitMQ入门:工作队列(Work Queue) 遇到的实例都是一个消息只发送给一个消费者(工作者),他们的消息 ...

随机推荐

  1. SpringBoot---注册Servlet,Filter,Listener

    1.概述 1.1.当使用  内嵌的Servlet容器(Tomcat.Jetty等)时,将Servlet,Filter,Listener  注册到Servlet容器的方法: 1.1.1.直接注册Bean ...

  2. 一次OOM测试并分析

    堆dump文件分析 Java代码: public class HeapTest2 { static class OOMObject { private byte[] arrs = new byte[6 ...

  3. 威胁预警|Solr velocity模板注入远程命令执行已加入watchbog武器库,漏洞修补时间窗口越来越短

    概述 近日,阿里云安全团队监测到挖矿团伙watchbog更新了其使用的武器库,增加了最新Solr Velocity 模板注入远程命令执行漏洞的攻击方式,攻击成功后会下载门罗币挖矿程序进行牟利.建议用户 ...

  4. macOS 和 Linux 的内核区别

    有些人可能会认为 macOS 和 Linux 内核之间存在相似之处,因为它们可以处理类似的命令和类似的软件.有些人甚至认为苹果公司的 macOS 是基于 Linux 的.事实上是,两个内核有着截然不同 ...

  5. 网络体系应用层之万维网、http协议

    1.万维网概述 万维网以客户--服务器方式工作,万维网客户程序就是各式各样的浏览器,万维网文档所驻留的主机则运行服务器程序, 因此这个主机也称为万维网服务器.客户程序向服务器程序发出请求,服务器程序向 ...

  6. 圆周率Pi是如何计算出来的

    object SparkPi { def main(args: Array[String]) { val spark = SparkSession .builder .appName("Sp ...

  7. paper 167:GPU的使用Theano之tutorial

    Theano之使用GPU 英文版本:http://deeplearning.net/software/theano/tutorial/using_gpu.html          using the ...

  8. Vue-Router原理

    Hash 与 History 路由原理 实现路由 /** * 1.前端路由与后端路由的区别 后端路由: 输入url => 请求发送到服务器 => 服务器解析请求路径 => 拿到对应页 ...

  9. CoffeeScript编写简单新闻页(仅UI)

    CoffeeScript编写简单新闻页(仅UI) 1. 配置(在公司搭建好的环境下配置) omnisocials-backend/src/backend/modules/member/config/m ...

  10. Oralce-PL/SQL编程-游标

    PL/SQL(Procedural Language/SQL)是Oracle在数据库中引入的一种过程化编程语言. PL/SQL块结构 声明部分 执行部分(必须的) 异常处理部分 [declare] - ...