消息队列的消费语义和投递语义

引言

所谓的消费语义,指的就是如下三种情况

  • 如何保证消息最多消费一次
  • 如何保证消息至少消费一次
  • 如何保证消息恰好消费一次

其实类似还有一个投递语义

  • 如何保证消息最多投递一次
  • 如何保证消息至少投递一次
  • 如何保证消息恰好投递一次

说句实在话,其实还是老问题,只是换了一种问法!
OK,开始我们的正文

正文

我们先做如下约定

  • Producer代表生产者
  • Consumer代表消费者
  • Message Queue代表消息队列

投递语义

我们先从投递语义开始讲起,因为要先把这个概念讲明白了,才能讲消费语义。恰巧,kafka实现了这三种语义,我们以kafka来说明。

如何保证消息最多投递一次?
简单,就是我已经投出去了,收没收到不管了,会存在消息丢失。
我们在初始化Producer时可以通过配置request.required.acks不同的值,来实现不同的发送模式。
这里将request.required.acks设为0,意思就是Producer不等待Leader确认,只管发出即可;最可能丢失消息。如果丢了消息,就是投递0次。如果没丢,就是投递1次。符合最多投递一次的含义。

如何保证消息至少投递一次?
这里将request.required.acks设为-1。ProducerkafkaLeader(主)节点发送消息后,会等follower(从)节点同步完数据以后,再给Producer返回ACK确认消息。
但是这里是有几率出现重复消费的问题的。
例如,kafka保存消息后,发送ACK前宕机,Producer认为消息未发送成功并重试,造成数据重复!
那么,在这种情况下,就会出现大于1次的投递情况,符合至少投递一次的含义。

如何保证消息恰好投递一次?
kafka在0.11.0.0版本之后支持恰好投递一次的语义。
我们将enable.idempotence设置为ture,此时就会默认把request.required.acks设为-1,可以达到恰好投递一次的语义。
如何做到的?
为了实现Producer的幂等语义,Kafka引入了Producer ID(即PID)和Sequence Number。
kafka为每个Producer分配一个pid,作为该Producer的唯一标识。
Producer会为每一个<topic,partition>维护一个单调递增的seq。
类似的,Message Queue也会为每个<pid,topic,partition>记录下最新的seq。
当req_seq == message_seq+1时,Message Queue才会接受该消息。因为:

  • (1)消息的seq比Message Queue的seq大一以上,说明中间有数据还没写入,即乱序了。
  • (2)消息的seq比Message Queue的seq小,那么说明该消息已被保存。

消费语义

这里我们还是做一个定义如下所示

  • consumer.poll()表示消费者获取消息内容
  • processMsg(message)表示下游系统进行消费消息
  • consumer.commit()表示消费者往消息队列提交确认信息,消息队列接到确认消息,删除该消息。

注意了,我是以processMsg函数,即处理消息的过程,定义为消费消息。
如何保证消息最多消费一次?
Producer:满足最多投递一次的语义即可,即只管发消息,不需要等待消息队列返回确认消息。
Message Queue:接到消息后往内存中一放就行,不用持久化存储。
Consumer:拉取到消息以后,直接给消息队列返回确认消息即可。至于后续消费消息成功与否,无所谓的。即按照以下顺序执行

  java.lang.NoSuchMethodError: com.jacob.com.Dispatch.call(www.seoxinyang.cn Lcom/jacob/com/Dispatch;Ljava/lang/String;[Ljava/lang/Object;www.rmutk.net)Lcom/jacob/com/Variant;
  
  at com.thupdi.project.www.fczxyl.cn utils.OfficeToPDFUtils.ppt2PDF(www.acnet.cn OfficeToPDFUtils.java:99)
  
  at com.thupdi.project.utils.www.yifayuLed.cn OfficeToPDFUtils.convert2PDF(OfficeToPDFUtils.java:69)

consumer.poll();
consumer.commit();
processMsg(message);

如何保证消息至少消费一次?
Producer:满足至少投递一次语义即可,即发送消息后,需要等待消息队列返回确认消息。如果超时没收到确认消息,则重发。
Message Queue:接到消息后,进行持久化存储,而后返回生产者确认消息。
Consumer:拉取到消息后,进行消费,消费成功后,再返回确认消息。即按照如下顺序执行

consumer.poll();
processMsg(message);
consumer.commit();

由于这里Producer满足的是至少投递一次语义,因此消息队列中是有重复消息的。所以我们的Consumer会出现重复消费的情形!

如何保证消息恰好消费一次?
在保证至少消费一次的基础上,processMsg满足幂等性操作即可。
如何保证幂等性操作?
老问题了,比如有状态的消息啊。比如唯一表啊。大家搜一搜,一大堆答案,不想重复说了。

总结

本文讲的是消息队列的消费语义和投递语义的含义,希望大家有所收获。

原文地址:https://www.cnblogs.com/qwangxiao/p/11051825.html

时间: 2024-08-30 15:43:35

消息队列的消费语义和投递语义的相关文章

【原创】消息队列的消费语义和投递语义

引言 所谓的消费语义,指的就是如下三种情况 如何保证消息最多消费一次 如何保证消息至少消费一次 如何保证消息恰好消费一次 其实类似还有一个投递语义 如何保证消息最多投递一次 如何保证消息至少投递一次 如何保证消息恰好投递一次 说句实在话,其实还是老问题,只是换了一种问法! OK,开始我们的正文 正文 我们先做如下约定 Producer代表生产者 Consumer代表消费者 Message Queue代表消息队列 投递语义 我们先从投递语义开始讲起,因为要先把这个概念讲明白了,才能讲消费语义.恰巧

消费端如何保证消息队列MQ的有序消费

消息无序产生的原因 消息队列,既然是队列就能保证消息在进入队列,以及出队列的时候保证消息的有序性,显然这是在消息的生产端(Producer),但是往往在生产环境中有多个消息的消费端(Consumer),尽管消费端在拉取消息时是有序的,但各个消息由于网络等方面原因无法保证在各个消费端中处理时有序. 场景分析 先后两次修改了商品信息,消息A和消息B先后同步写入MySQL,接着异步写入消息队列中发送消息,此时消息队列生产端(Producer)按时序先后发出了A和B两条消息(消息A先发出,消息B后发出)

使用消息队列异步化系统

使用消息队列异步化系统 基于Spring与ActiveMQ的配置实现方案 前言 前期为了快速开发,项目结构较为混乱,代码维护与功能扩展都比较困难,为了方便后续功能开发,最近对项目进行的重构,顺便在重构的过程中将之前的部分操作进行了异步处理,也第一次实际接触了JMS与消息队列.项目中采用的消息中间件为ActiveMQ. 什么是JMS Java消息服务(Java Message Service,JMS)应用程序接口是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分

消息队列属性及常见消息队列介绍

什么是消息队列?消息队列是在消息的传输过程中保存消息的容器,用于接收消息并以文件的方式存储,一个队列的消息可以同时被多个消息消费者消费.分布式消息服务DMS则是分布式的队列系统,消息队列中的消息分布存储,且每条消息存储多个副本,以实现高可用性,如下图所示. 一般来说,消息队列具有如下属性: 消息顺序普通队列支持"分区有序"和"全局队列"两种模式,ActiveMQ队列和Kafka队列均为分区有序. 分区有序的队列通过分布式处理,支持更高的并发,但由于队列的分布式特性,

系统学习消息队列分享(二) 为什么需要消息队列?

消息队列是最古老的中间件之一,从系统之间有通信需求开始,就自然产生了消息队列.但是给消息队列下一个准确的定义却不太容易.我们知道,消息队列的主要功能就是收发消息,但是它的作用不仅仅只是解决应用之间的通信问题这么简单. 我们举个例子说明一下消息队列的作用.话说小袁是一家巧克力作坊的老板,生产出美味的巧克力需要三道工序:首先将可可豆磨成可可粉,然后将可可粉加热并加入糖变成巧克力浆,最后将巧克力浆灌入模具,撒上坚果碎,冷却后就是成品巧克力了. 最开始的时候,每次研磨出一桶可可粉后,工人就会把这桶可可粉

消息服务百科全书——消息投递语义

消息投递语义(Message delivery semantics) 有如下几种可能的消息传递保障: 1.At most once:消息可能丢失,但是不会重复. 2.At least once:消息不会丢失,但是可能重复.系统保证每条消息至少会发送一次,但在有故障的情况下可能会导致重复发送. 3.Exactly once:仅仅一次-这种是人们实际想要的,每条消息只会而且仅会发送一次. 这里需要拆开为两个问题:发布消息保证和消费消息保证. 大多数系统号称支持Exactly once,但是仔细去看那

RabbitMQ,Apache的ActiveMQ,阿里RocketMQ,Kafka,ZeroMQ,MetaMQ,Redis也可实现消息队列,RabbitMQ的应用场景以及基本原理介绍,RabbitMQ基础知识详解,RabbitMQ布曙

消息队列及常见消息队列介绍 2017-10-10 09:35操作系统/客户端/人脸识别 一.消息队列(MQ)概述 消息队列(Message Queue),是分布式系统中重要的组件,其通用的使用场景可以简单地描述为: 当不需要立即获得结果,但是并发量又需要进行控制的时候,差不多就是需要使用消息队列的时候. 消息队列主要解决了应用耦合.异步处理.流量削锋等问题. 当前使用较多的消息队列有RabbitMQ.RocketMQ.ActiveMQ.Kafka.ZeroMQ.MetaMq等,而部分数据库如Re

Java常用消息队列原理介绍及性能对比

消息队列使用场景 为什么会需要消息队列(MQ)? 解耦  在项目启动之初来预测将来项目会碰到什么需求,是极其困难的.消息系统在处理过程中间插入了一个隐含的.基于数据的接口层,两边的处理过程都要实现这一接口.这允许你独立的扩展或修改两边的处理过程,只要确保它们遵守同样的接口约束. 冗余  有些情况下,处理数据的过程会失败.除非数据被持久化,否则将造成丢失.消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失风险.许多消息队列所采用的"插入-获取-删除"范式中,在把一

消息队列二三事

最近在看kafka的代码,就免不了想看看消息队列的一些要点:服务质量(QOS).性能.扩展性等等,下面一一探索这些概念,并谈谈在特定的消息队列如kafka或者mosquito中是如何具体实现这些概念的. 服务质量 服务语义 服务质量一般可以分为三个级别,下面说明它们不同语义. At most once 至多一次,消息可能丢失,但绝不会重复传输. 生产者:完全依赖底层TCP/IP的传输可靠性,不做特殊处理,所谓"发送即忘".kafka中设置acks=0. 消费者:先保存消费进度,再处理消