消息派发

上篇《RabbitMQ入门-消息派发那些事儿》发布之后,收了不少反馈,其中问的最多的还是有关消息确认以及超时等场景的处理。


楼主,有遇到消费者后台进程不在,但consumer连接还在,当前消息是unacked状态,导致这个消息一直不被消费
队列在等待回复的时候,这个消息是怎么存放的?如果一直没有返回有超时么?
...

这里再对消息确认做以下补充

有关超时

RabbitMQ是没有超时概念的,如果一个消费者消费一条消息要花费很长时间,比如10分钟,那么这个过程会一直进行下去。除非你采用其他策略来中断它或者重试。

消费者挂了怎么办

如果我们不打开自动确认的标识autoAck,那么消费者在消费完成消息之后会发送一个确认标识给RabbitMQ。RabbitMQ接收到这个标识之后,就会将这条消息从内存中删除。

但是正如上面的网友提到的那样,如果消费者后台进程不在即消费者挂了,这时候RabbiMQ会一直傻等着么?当然不会,RabbitMQ发现消费者挂了之后,它会很快将这条消息转而批发给下一个消费者消费,这样做也能够避免消息丢失的情况。

下面我们启动了两个消费者,一个发送端,在第二个消费者接收到消息的时候,手动让其进程结束,这时候我们会发现最终生产的4条消息都被第一个消费者消费了,并没有出现消息丢失的情况。

消息持久化

上面的情况是在RabbitMQ正常提供服务时避免了消息丢失的情况,但是如果遇到RabbitMQ服务挂了,该如何保证消息不丢失呢?这时候就需要做持久化,细心的同学应该已经发现,在Work模式的发送端和接收端都做了持久化。****

channel.basicPublish("", TASK_QUEUE_NAME,MessageProperties.PERSISTENT_TEXT_PLAIN,message.getBytes("UTF-8"));

这里第三个参数指明了要持久化

channel.queueDeclare(TASK_QUEUE_NAME, true, false, false, null);

这里第二个参数知名了要持久化

订阅者模式



模型组成

一个消费者Producer,一个交换机Exchange,多个消息队列Queue,多个消费者Consumer

Exchange

相比较于前两种模型Hello World和Work,这里多一个一个Exchange。其实Exchange是RabbitMQ的标配组成部件之一,前两种没有提到Exchange是为了简化模型,即使模型中没有看到Exchange的声明,其实还是声明了一个默认的Exchange。

RabbitMQ中实际发送消息并不是直接将消息发送给消息队列,消息队列也没那么聪明知道这条消息从哪来要到哪去。RabbitMQ会先将消息发送个Exchange,Exchange会根据这条消息打上的标记知道该条消息从哪来到哪去。

Exchange凭什么知道消息的何去何从,因为Exchange有几种类型:direct,fanout,topic和headers。这里说的订阅者模式就可以认为是fanout模式了。

订阅者模式有何不同

订阅者模式相对前面的Work模式有和不同?Work也有多个消费者,但是只有一个消息队列,并且一个消息只会被某一个消费者消费。但是订阅者模式不一样,它有多个消息队列,也有多个消费者,而且一条消息可以被多个消费者消费,类似广播模式。下面通过实例代码看看这种模式是如何收发消息的。

发送端

/**
* Created by jackie on 17/8/6.
*/
public class EmitLog { private static final String EXCHANGE_NAME = "logs"; public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.3.161");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel(); channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT); String message = getMessage(argv); channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8"));
System.out.println(" [x] Sent '" + message + "'"); channel.close();
connection.close();
} private static String getMessage(String[] strings){
if (strings.length < 1)
return "info: Hello World!";
return joinStrings(strings, " ");
} private static String joinStrings(String[] strings, String delimiter) {
int length = strings.length;
if (length == 0) return "";
StringBuilder words = new StringBuilder(strings[0]);
for (int i = 1; i < length; i++) {
words.append(delimiter).append(strings[i]);
}
return words.toString();
}
}
  • channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);添加了Exchange的声明,并且采用的是fanout类型

  • channel.basicPublish(EXCHANGE_NAME, "", null, message.getBytes("UTF-8"));声明了Exchange的名称,而不是像之前那样给了个空值

接收端

/**
* Created by jackie on 17/8/6.
*/
public class ReceiveLogs {
private static final String EXCHANGE_NAME = "logs"; public static void main(String[] argv) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.3.161");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel(); channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
String queueName = channel.queueDeclare().getQueue();
channel.queueBind(queueName, EXCHANGE_NAME, ""); System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println(" [x] Received '" + message + "'");
}
};
channel.basicConsume(queueName, true, consumer);
}
}
  • 这里通过String queueName = channel.queueDeclare().getQueue();来声明队列,采取该种方式会生成一个随机名字的消息队列,并且在断开连接时队列会自动删除,但是这并不会影响订阅者模式,因为该场景下所有绑定的queue都会收到消息

  • 通过channel.queueBind(queueName, EXCHANGE_NAME, "");将新建的Queue和Exchange绑定,因为是fanout模式,所以不需要指定routing key的值

  • 启动了两个随机名称的消费者,它们Queue的名称不同

  • 启动生产者,发送一条消息,这时候可以发现两个接受端都收到了消息,这就是订阅者模式

如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。

RabbitMQ入门-消息订阅模式的更多相关文章

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

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

  2. Solon rpc 之 SocketD 协议 - 消息订阅模式

    Solon rpc 之 SocketD 协议系列 Solon rpc 之 SocketD 协议 - 概述 Solon rpc 之 SocketD 协议 - 消息上报模式 Solon rpc 之 Soc ...

  3. RabbitMQ简单应用の订阅模式

    订阅模式 公众号-->订阅之后才会收到相应的文章. 解读: 1.一个生产者,多个消费者 2.每个消费者都有自己的队列 3.生产者没有将消息直接发送到队列里,而是发送给了交换机(转发器)excha ...

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

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

  5. RabbitMQ入门-消息派发那些事儿

    在上篇<RabbitMQ-高效的Work模式>中,我们了解了Work模型,该模型包括一个生产者,一个消息队列和多个消费者. 我们已经通过实例看出消息队列中的消息是如何被一个或者多个消费者消 ...

  6. .NetCore Cap 结合 RabbitMQ 实现消息订阅

    开源分布式消息框架 Cap 可以在GitHub上拉也可以通过nuget添加 上一篇博文写了 Windows RabbitMQ的安装使用 Cap支持事务,通过捕获数据库上下文连接对象实现 消息事务,消息 ...

  7. RabbitMQ入门-竞争消费者模式

    上一篇讲了个 哈喽World,现在来看看如果存在多个消费者的情况. 生产者: package com.example.demo; import com.rabbitmq.client.Channel; ...

  8. Rabbitmq之高级特性——百分百投递消息&消息确认模式&消息返回模式实现

    rabbitmq的高级特性: 如何保障消息的百分之百成功? 要满足4个条件:生产方发送出去,消费方接受到消息,发送方接收到消费者的确认信息,完善的消费补偿机制 解决方案,1)消息落库,进行消息状态打标 ...

  9. RabbitMQ入门到进阶(Spring整合RabbitMQ&SpringBoot整合RabbitMQ)

    1.MQ简介 MQ 全称为 Message Queue,是在消息的传输过程中保存消息的容器.多用于分布式系统 之间进行通信. 2.为什么要用 MQ 1.流量消峰 没使用MQ 使用了MQ 2.应用解耦 ...

随机推荐

  1. Linux系统/dev/mapper目录浅谈

    Linux系统的一般的文件系统名称类似于/dev/sda1或/dev/hda1,但是今天在进行系统维护的时候,利用df -h 命令敲出了/dev/mapper/VolGroup-lv_root和/de ...

  2. linux 升级yum对应的python

    这里记录一下linux 系统升级python对yum带来影响的解决办法 很多人在使用linux系统执行python任务的时候需要升级linux系统自带的python到高级版本.具体如何升级python ...

  3. VS2012打开项目 提示Asp.net4.5未在web服务器上注册

    在用vs2012代开项目时,没回都显示Asp.net4.5未在web服务器上注册,最后是由于没有下载一个补丁的原因,只需要下载安装补丁 VS11-KB3002339.exe ,下载地址:https:/ ...

  4. Object-C知识点 (四) 性能优化

    #pragma mark - 性能优化 1. 行高一定要缓存 缓存行高是解决性能优化的最佳途径(除非刷新频率已经很高了,否则一定要缓存)2. 尽量减少计算,所有需要素材提前计算好3. 控件不要设置圆角 ...

  5. Java基础(2)-基础类型

    java基础类型 基础类型 package knowledge.base; public class Properties { /** * 整型 * int 4字节 -2 147 483 648 ~2 ...

  6. php7.0版本不再以类名命名构造函数

    <?php class Car { var $color = "add"; function Car($color="green") { $this-&g ...

  7. Socket 文件传输

    服务端 1.控件:TServerSocket 2.OnClientRead事件处理 procedure TMainForm.ssClientRead(Sender: TObject; Socket: ...

  8. jQuery知识点整合

    一.jQuery介绍 jQuery是一个函数库,一个js文件,页面用script标签引入js文件就可以使用 <script type="text/javascript" sr ...

  9. 使用travis-ci自动部署github上的项目

    travis-ci是什么? 一个使用yaml格式配置用于持续集成完成自动化测试部署的开源项目 官网:https://travis-ci.org/ 使用travis-ci集成vue.js项目 首先,您需 ...

  10. Jquery table元素操作-创建|数据填充|重置|隐藏行

    1.Jquery创建表格 /** * 创建表格 * @param label 标题 json格式,数据结构见附录1 * @param data 数据 json格式,数据结构见附录1 * @param ...