最近收到好几个类似的问题:使用Spring Cloud Stream操作RabbitMQ或Kafka的时候,出现消息重复消费的问题。通过沟通与排查下来主要还是用户对消费组的认识不够。其实,在之前的博文以及《Spring Cloud微服务实战》一书中都有提到关于消费组的概念以及作用。

那么什么是消费组呢?为什么要用消费组?它解决什么问题呢?摘录一段之前博文的内容,来解答这些疑问:

通常在生产环境,我们的每个服务都不会以单节点的方式运行在生产环境,当同一个服务启动多个实例的时候,这些实例都会绑定到同一个消息通道的目标主题(Topic)上。默认情况下,当生产者发出一条消息到绑定通道上,这条消息会产生多个副本被每个消费者实例接收和处理(出现上述重复消费问题)。但是有些业务场景之下,我们希望生产者产生的消息只被其中一个实例消费,这个时候我们需要为这些消费者设置消费组来实现这样的功能。

详细也可查看原文:消息驱动的微服务(消费组)

下面,通过一个例子来看看如何使用消费组:

问题重现

构建消息消费端

第一步:创建绑定接口,绑定example-topic输入通道(默认情况下,会绑定到RabbitMQ的同名Exchange或Kafaka的同名Topic)。

interface ExampleBinder {

    String NAME = "example-topic";

    @Input(NAME)
SubscribableChannel input(); }

第二步:对上述输入通道创建监听与处理逻辑。

@EnableBinding(ExampleBinder.class)
public class ExampleReceiver { private static Logger logger = LoggerFactory.getLogger(ExampleReceiver.class); @StreamListener(ExampleBinder.NAME)
public void receive(String payload) {
logger.info("Received: " + payload);
} }

第三步;创建应用主类和配置文件

@SpringBootApplication
public class ExampleApplication { public static void main(String[] args) {
SpringApplication.run(ExampleApplication.class, args);
} }
spring.application.name=stream-consumer-group
server.port=0

这里设置server.port=0,以方便在本地启动多实例来重现问题。

完成上述操作之后,启动两个该应用的实例,以备后续调用。

构建消息生产端

比较简单,需要注意的是,使用@Output创建一个同名的输出绑定,这样发出的消息才能被上述启动的实例接收到。具体实现如下:

@RunWith(SpringRunner.class)
@EnableBinding(value = {ExampleApplicationTests.ExampleBinder.class})
public class ExampleApplicationTests { @Autowired
private ExampleBinder exampleBinder; @Test
public void exampleBinderTester() {
exampleBinder.output().send(MessageBuilder.withPayload("Produce a message from : http://blog.didispace.com").build());
} public interface ExampleBinder { String NAME = "example-topic"; @Output(NAME)
MessageChannel output(); } }

启动上述测试用例之后,可以发现之前启动的两个实例都收到的消息,并在日志中打印了:Received: Produce a message from : http://blog.didispace.com。消息重复消费的问题成功重现!

使用消费组解决问题

如何解决上述消息重复消费的问题呢?我们只需要在配置文件中增加如下配置即可:

spring.cloud.stream.bindings.example-topic.group=aaa

当我们指定了某个绑定所指向的消费组之后,往当前主题发送的消息在每个订阅消费组中,只会有一个订阅者接收和消费,从而实现了对消息的负载均衡。只所以之前会出现重复消费的问题,是由于默认情况下,任何订阅都会产生一个匿名消费组,所以每个订阅实例都会有自己的消费组,从而当有消息发送的时候,就形成了广播的模式。

另外,需要注意上述配置中example-topic是在代码中@Output@Input中传入的名字。

代码示例

本文示例读者可以通过查看下面仓库的中的stream-consumer-group项目:

如果您对这些感兴趣,欢迎star、follow、收藏、转发给予支持!

以下专题教程也许您会有兴趣

Spring Cloud Stream如何处理消息重复消费?的更多相关文章

  1. 使用 Spring Cloud Stream 构建消息驱动微服务

    相关源码: spring cloud demo 微服务的目的: 松耦合 事件驱动的优势:高度解耦 Spring Cloud Stream 的几个概念 Spring Cloud Stream is a ...

  2. Spring Cloud Alibaba学习笔记(12) - 使用Spring Cloud Stream 构建消息驱动微服务

    什么是Spring Cloud Stream 一个用于构建消息驱动的微服务的框架 应用程序通过 inputs 或者 outputs 来与 Spring Cloud Stream 中binder 交互, ...

  3. Spring cloud stream【消息分区】

      在上篇文章中我们给大家介绍了Stream的消息分组,可以实现消息的重复消费的问题,但在某些场景下分组还不能满足我们的需求,比如,同时有多条同一个用户的数据,发送过来,我们需要根据用户统计,但是消息 ...

  4. Spring cloud stream【消息分组】

      上篇文章我们简单的介绍了stream的使用,发现使用还是蛮方便的,但是在上个案例中,如果有多个消息接收者,那么消息生产者发送的消息会被多个消费者都接收到,这种情况在某些实际场景下是有很大问题的,比 ...

  5. SpringCloud之Spring Cloud Stream:消息驱动

    Spring Cloud Stream 是一个构建消息驱动微服务的框架,该框架在Spring Boot的基础上整合了Spring Integrationg来连接消息代理中间件(RabbitMQ, Ka ...

  6. Spring Cloud Stream如何消费自己生产的消息?

    在上一篇<Spring Cloud Stream如何处理消息重复消费>中,我们通过消费组的配置解决了多实例部署情况下消息重复消费这一入门时的常见问题.本文将继续说说在另外一个被经常问到的问 ...

  7. Spring Cloud Stream如何消费自己生产的消息

    在上一篇<Spring Cloud Stream如何处理消息重复消费>中,我们通过消费组的配置解决了多实例部署情况下消息重复消费这一入门时的常见问题.本文将继续说说在另外一个被经常问到的问 ...

  8. Spring Cloud Stream消费失败后的处理策略(一):自动重试

    之前写了几篇关于Spring Cloud Stream使用中的常见问题,比如: 如何处理消息重复消费 如何消费自己生产的消息 下面几天就集中来详细聊聊,当消息消费失败之后该如何处理的几种方式.不过不论 ...

  9. Spring Cloud Stream消费失败后的处理策略(三):使用DLQ队列(RabbitMQ)

    应用场景 前两天我们已经介绍了两种Spring Cloud Stream对消息失败的处理策略: 自动重试:对于一些因环境原因(如:网络抖动等不稳定因素)引发的问题可以起到比较好的作用,提高消息处理的成 ...

随机推荐

  1. _ZNote_Qt_QDialog_修改button名称

    #include <QPushButton> ui->buttonBox->button(QDialogButtonBox::Ok)->setText("Run ...

  2. flask-文件上传

    flask文件上传 流程 1. 上传的文件request.files拿取 2. 可以通过WTForms表单验证 3. 通过secure_filename (from werkzeug.utils im ...

  3. Activity的task任务栈

    转自http://blog.csdn.net/liuhe688/article/details/6761337 古人學問無遺力,少壯工夫老始成.紙上得來終覺淺,絕知此事要躬行.南宋.陸遊<冬夜讀 ...

  4. ES6中的箭头函数和普通函数有什么区别?

    1.普通函数中的this总是指向调用它的那个对象, 箭头函数没有自己的this,他的this永远指向其定义环境,任何方法都改变不了其指向,如call().bind().apply().(正是因为它没有 ...

  5. App设计:消息推送和界面路由跳转

    概要 app消息推送.显示通知栏,点击跳转页面是很一般的功能了,下面以个推为例演示push集成,消息处理模块及app内部路由模块的简单设计. 推送 推送sdk集成 集成sdk步骤根据文档一步步做就行了 ...

  6. 设置HttponlyCookie解决mshtml编程无法获取验证码图片流

    最近给客户做的项目有一个新需求,客户需要在打开的IE浏览器中做自动登录,登录的页面上有神兽验证码.解决验证码的方案是找第三方平台打码.这样就有一个问题,如何把正确的验证码传给第三方打码平台. 大家都知 ...

  7. iOS学习——(转)NSObject详解

    本文主要转载自:ios开发 之 NSObject详解 NSObject是大部分Objective-C类继承体系的根类.这个类遵循NSObject协议,提供了一些通用的方法,对象通过继承NSObject ...

  8. php内核之HashTable

    Zend 把与 HashTable 有关的 API 分成了好几类以便于我们查找,这些 API 的返回值大多都是常量SUCCESS 或者 FAILURE. 初始化 HashTable 下面在介绍函数原型 ...

  9. 如何批量添加图片到ppt的方法

    如何批量添加图片到ppt 许多时候会做一些幻灯片,需要大量的图片,但是往往一张以张的加图片,会很浪费时间,如何快速添加图片,一次解决呢? 步骤:插入-相册-点击相册 点击文件,批量选择你要插入的图片, ...

  10. springBoot(4)---热部署,配置文件使用

    热部署,配置文件使用 一.热加载 spring为开发者提供了一个名为spring-boot-devtools的模块来使Spring Boot应用支持热部署,提高开发者的开发效率,无需手动重启Sprin ...