最近起了个项目消息中心,用来中转各个系统中产生的消息,用到的是RabbitMQ,由于UAT环境、生产环境每台消费者服务都是多台,有些消息要求按顺序消费,所以需要采取一定的措施保证消息的顺序消费,下面讲下我们不断优化的三种方法:

1、我们最开始考虑的比较简单,采用的direct交换机,指定特定消费者服务器监听队列,其他消费者服务器不监听。比如现在有C1、C2、C3三台消费者机器,我们决定C1消费消息,C2、C3不监听。我们在启动C1的时候,启动脚本中添加C1_IP,在代码中做处理,消费者服务器启动时,如果当前服务器IP就是启动脚本的C1_IP,那就会由这台C1来监听并消费消息。这种方式有个单点故障问题,如果C1服务器宕机,那么整个消息中心剩余两个节点都无法消费这个队列,导致队列消息堆积。如果有丰富的监控措施,那么监控到C1宕机后,可通过手动配置C2_IP(或者C3_IP)到启动脚本,重启C2服务器(C3服务器)消费消息。

2、为了解决单点故障问题,我们采用了fanout交换机,每个消费者创建一个专用的queue,这样如果生产者产生两条有先后顺序的消息m1和m2(它们有公共的批次号batchNo和唯一的消息编号msgID),就会给每个queue都推送,如下图所示。同时消费者消费的时候需要配合数据库共同实施,消费者监听到消息后就入库(落库内容包括m1消息信息和消费者IP),根据msgID唯一索引性如果入库了则自己抛弃消息,消费m2时,需要从库表中取出m1的消费者IP是否是当前IP,如果不是则抛弃消息。但是这个方案有个缺点:如果consumer1消费了m1后挂掉了,m2只能等到consumer1正常后才能消费,无法转移到其他消费者进行消费,这样会对一些业务场景不友好(当然这个地方可以考虑死信交换机死信队列进行转移,只不过架构更复杂了)。

3.第三种方式跟第二种类似,采用fanout交换机,每个消费者创建一个专用的queue。但是没有借助数据库,而是通过访问rabbitMQ的API接口,获取这三个队列的所有消费者的IP放到list中,消费者监听到消息后,判断自己的ip是否是ip集合里面的最小值,如果是则消费,如果否则抛弃消息。一旦最小IP的消费者宕机后,则list种就会只剩下两个IP,后续的消息选定的消费者就会从这两个IP中选择最小IP消费。同理它也有第二种方案的缺点。

最后附上通过rabbitmq的api获取minIP的代码(入参consumerIps是初始size=0的list),如下:

 private String findUsefulMinIP(List<String> consumerIps) {
String minIp = null;
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(20000);
try {
RestTemplate rest = new RestTemplateBuilder().basicAuthentication(username, password).build();
rest.setRequestFactory(requestFactory);
JSONArray result2 = rest.getForObject(moccMQApiUrl, JSONArray.class);
if(result2 != null && result2.size() > 0) {
log.info("===clear the ips===new query start===");
consumerIps.clear();
}
for(int m=0; m<result2.size(); m++) {
LinkedHashMap itmap = (LinkedHashMap) result2.get(m);
LinkedHashMap queueMap = (LinkedHashMap)itmap.get("queue");
if(!queueMap.values().stream().anyMatch(v -> v.toString().indexOf(moccQueue)>=0)) {
continue;
} LinkedHashMap consumerMap = (LinkedHashMap)itmap.get("channel_details");
consumerIps.add((String)consumerMap.get("peer_host"));
}
log.info("===query from mq===consumerIps={}", consumerIps);
} catch (RestClientException e) {
log.error(e.getMessage(), e);
}
minIp = Collections.min(consumerIps);
return minIp;
}

RabbitMQ多消费者顺序性消费消息实现的更多相关文章

  1. 2.RABBITMQ 入门 - WINDOWS - 生产和消费消息 一个完整案例

    关于安装和配置,见上一篇 1.RABBITMQ 入门 - WINDOWS - 获取,安装,配置 公司有需求,要求使用winform开发这个东西(消息中间件),另外还要求开发一个日志中间件,但是也是要求 ...

  2. rocketmq的以集群模式MessageModel.CLUSTERING实现消费者集群消费消息,实现负载均衡

    package com.bfxy.rocketmq.model; import java.util.List; import org.apache.rocketmq.client.consumer.D ...

  3. Pulsar の 保证消息的顺序性、幂等性和可靠性

    原文链接:Pulsar の 保证消息的顺序性.幂等性和可靠性 一.背景 前面两篇文章,已经介绍了关于Pulsar消费者的详细使用和自研的Pulsar组件. 接下来,将简单分析如何保证消息的顺序性.幂等 ...

  4. RabbitMQ保证消息的顺序性

    当我们的系统中引入了MQ之后,不得不考虑的一个问题是如何保证消息的顺序性,这是一个至关重要的事情,如果顺序错乱了,就会导致数据的不一致.       比如:业务场景是这样的:我们需要根据mysql的b ...

  5. Kafka如何保证消息的顺序性

    1. 问题 比如说我们建了一个 topic,有三个 partition.生产者在写的时候,其实可以指定一个 key,比如说我们指定了某个订单 id 作为 key,那么这个订单相关的数据,一定会被分发到 ...

  6. kafka如何保证消息得顺序性

    1. 问题 比如说我们建了一个 topic,有三个 partition.生产者在写的时候,其实可以指定一个 key,比如说我们指定了某个订单 id 作为 key,那么这个订单相关的数据,一定会被分发到 ...

  7. RabbitMQ 消费消息

    1, 创建一个 springboot 项目, 导入依赖(和生产者一致) 2, application.properties (基础配置和生产者一致, 消费者需要再额外配置一些) # rabbitmq ...

  8. MQ如何解决消息的顺序性

    一.消息的顺序性 1.延迟队列:设置一个全局变量index,根据实际情况一次按照index++的逻辑一次给消息队列设置延迟时间段,可以是0.5s,甚至1s; 弊端:如果A,B,C..消息队列消费时间不 ...

  9. 分布式场景下Kafka消息顺序性的思考

    如果业务中,对于kafka发送消息异步消费的场景,在业务上需要实现在消费时实现顺序消费, 利用kafka在partition内消息有序的特点,消息消费时的有序性. 1.在发送消息时,通过指定parti ...

随机推荐

  1. ECSHOP任意页面显示指定分类、数量、排序的任意类型文章,包括只显示置顶or普通的文章

    1.在需要使用此功能的PHP页面里最后的?>前面添加以下代码,现在以article.php为例子 /** jinmozhe 专业ECSHOP二次开发 * 获得指定分类ID.文章类型.指定数量.排 ...

  2. 如何实现Orchard Core CMS的全文索引

    Orchard Core提供了Lucene功能,允许您在网站上进行全文搜索.大多数情况下,在运行博客或简单的代理网站时,您可能需要在页面内容中进行搜索.在Orchard Core中,您可以使用Liqu ...

  3. MYSQL分页 limit 太慢优化

    limit分页原理 当我们翻到最后几页时,查询的sql通常是:select * from table where column=xxx order by xxx limit 1000000,20.查询 ...

  4. yum 安装 php 环境

    如此简单 第一步: sudo rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7. ...

  5. 大前端快闪二:react开发模式 一键启动多个服务

    最近全权负责了一个前后端分离的web项目,前端使用create-react-app, 后端使用golang做的api服务. npx create-react-app my-app cd my-app ...

  6. MySQL、Redis、MongoDB网络抓包工具

    简介 go-sniffer 可以抓包截取项目(MySQL.Redis.MongoDB)中的请求并解析成相应的语句,并格式化输出.类似于在之前的文章 MySQL抓包工具:MySQL Sniffer[转] ...

  7. C++优化列表

    #pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize("Ofast") #pragma GCC ...

  8. C程序内存布局

    作为计算机专业的来说,程序入门基本都是从C语言开始的,了解C程序中的内存布局,对我们了解整个程序运行,分析程序出错原因,会起到事半功倍的作用 . C程序的内存布局包含五个段,分别是STACK(栈段), ...

  9. javascript-jquery-文档处理

    一.移动元素 1.append():向每个匹配元素的内部追加内容.例如:$("选择器1").qppend("选择器2"):将会匹配选择器2的元素,移动到匹配选择 ...

  10. st表树状数组入门题单

    预备知识 st表(Sparse Table) 主要用来解决区间最值问题(RMQ)以及维护区间的各种性质(比如维护一段区间的最大公约数). 树状数组 单点更新 数组前缀和的查询 拓展:原数组是差分数组时 ...