CN113505012A - 一种消息队列的处理方法、介质、设备和*** - Google Patents
一种消息队列的处理方法、介质、设备和*** Download PDFInfo
- Publication number
- CN113505012A CN113505012A CN202111065794.6A CN202111065794A CN113505012A CN 113505012 A CN113505012 A CN 113505012A CN 202111065794 A CN202111065794 A CN 202111065794A CN 113505012 A CN113505012 A CN 113505012A
- Authority
- CN
- China
- Prior art keywords
- message
- message queue
- partition
- queue
- name
- Prior art date
- Legal status (The legal status is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the status listed.)
- Granted
Links
Images
Classifications
-
- G—PHYSICS
- G06—COMPUTING; CALCULATING OR COUNTING
- G06F—ELECTRIC DIGITAL DATA PROCESSING
- G06F9/00—Arrangements for program control, e.g. control units
- G06F9/06—Arrangements for program control, e.g. control units using stored programs, i.e. using an internal store of processing equipment to receive or retain programs
- G06F9/46—Multiprogramming arrangements
- G06F9/54—Interprogram communication
- G06F9/546—Message passing systems or structures, e.g. queues
-
- G—PHYSICS
- G06—COMPUTING; CALCULATING OR COUNTING
- G06F—ELECTRIC DIGITAL DATA PROCESSING
- G06F9/00—Arrangements for program control, e.g. control units
- G06F9/06—Arrangements for program control, e.g. control units using stored programs, i.e. using an internal store of processing equipment to receive or retain programs
- G06F9/46—Multiprogramming arrangements
- G06F9/52—Program synchronisation; Mutual exclusion, e.g. by means of semaphores
-
- G—PHYSICS
- G06—COMPUTING; CALCULATING OR COUNTING
- G06F—ELECTRIC DIGITAL DATA PROCESSING
- G06F2209/00—Indexing scheme relating to G06F9/00
- G06F2209/54—Indexing scheme relating to G06F9/54
- G06F2209/548—Queue
Landscapes
- Engineering & Computer Science (AREA)
- Software Systems (AREA)
- Theoretical Computer Science (AREA)
- Physics & Mathematics (AREA)
- General Engineering & Computer Science (AREA)
- General Physics & Mathematics (AREA)
- Information Retrieval, Db Structures And Fs Structures Therefor (AREA)
Abstract
本发明提供一种消息队列的处理方法、介质、设备和***,该方法包括:根据消息生产端发送的分区名,获取写分段索引的值;根据分区名从写分段索引的Hash中查询得到分段号,并且使用分区名和分段号拼接获得存储键名;据存储键名写入消息内容,并确定消息内容对应的消息时间序号entryId;向消息内容中写入标记,标记用于指示消息内容未在消息队列的主节点和从节点之间完成同步;消息队列包括主节点和从节点;判断已写入的消息内容个数是否达到分段的预设长度;当已写入的消息内容个数达到分段的预设长度时,将写分段索引的值加1;获取消息队列的主节点复制偏移量;向消息生产端返回entryId和消息队列的主节点复制偏移量。上述技术方案确保了消息处理的时序。
Description
技术领域
本发明涉及对时序要求严格的应用间异步通讯领域,具体涉及一种消息队列的处理方法、介质、设备和***。
背景技术
现有的开源分布式消息队列(Message Queue,简称MQ)主要有两类:第一类是基于内存的消息队列如ActiveMQ、RabbitMQ等,第二类是基于磁盘的消息中间件如Kafka、RocketMQ等。
在实现本发明的过程中,发明人发现现有技术中至少存在如下问题:
第一类消息队列一般对消息堆积能力在10万条左右,对消息进行顺序确认的前提下,吞吐能力不足。
第二类消息队列消息堆积能力可达上亿,但是RocketMQ并不严格保证消息的顺序。
发明内容
有鉴于此,本发明实施例的目的在于提供一种消息队列的处理方法、介质、设备和***,以保证消息处理的顺序或时序。
第一方面,提供一种消息队列的处理方法,应用于消息队列设备,所述方法包括:
根据消息生产端发送的分区名,获取写分段索引的值;
根据所述分区名从写分段索引的Hash中查询得到分段号,并且使用所述分区名和所述分段号拼接获得存储键名;
根据所述存储键名写入消息内容,并确定所述消息内容对应的消息时间序号entryId;
向所述消息队列的预设数据结构中写入标记,所述标记用于指示所述消息内容未在所述消息队列的主节点和从节点之间完成同步;其中,所述消息队列包括主节点和从节点;
判断已写入的消息内容个数是否达到分段的预设长度;
当已写入的消息内容个数达到分段的预设长度时,将所述写分段索引的值加1;
获取消息队列的主节点复制偏移量;
向所述消息生产端返回所述entryId和所述消息队列的主节点复制偏移量。
在一些可能的实施方式中,在所述的向消息生产端返回写入的entryId和所述消息队列的主节点复制偏移量之后,还可以包括:
获取消息队列的运行信息;
根据所述消息队列的运行信息,判断是否发生了主节点故障切换;
如果发生了主节点故障切换,进一步根据从节点复制偏移量和主节点复制偏移量判断判断复制偏移量是否未同步;
如果复制偏移量未同步,则确定消息已丢失,返回写入失败信息;
如果未发生主节点故障切换,则确定主节点正常,依次获取每个待检查的从节点的复制偏移量,判断每个待检查的从节点的复制偏移量是否已经完成复制;
当任意待检查的从节点的复制偏移量已经完成复制时,返回成功信息。
在一些可能的实施方式中,在所述的当任意待检查的从节点的复制偏移量已经完成复制时,返回成功信息之后,还可以包括:
拼接租约锁键名;
根据租约锁键名,判断租约锁是否存在;
如果所述租约锁不存在,则设置所述租约锁的值为客户端信息,过期时间为指定时长;
如果所述租约锁存在,则进一步判断所述租约锁与客户端信息是否相同;
当所述租约锁与客户端信息相同时,更新所述租约锁的过期时间;
当所述租约锁与客户端信息不相同时,确定所述租约锁已被占用,输出获取所述租约锁失败的指示信息。
在一些可能的实施方式中,所述方法还可以包括:
判断是否成功获取所述租约锁;
如果已成功获取所述租约锁,则获取读分段索引;
将分区名和分段号拼接成存储键名;
获取当前处理偏移量;
向消费者端返回所述存储键名和所述当前处理的偏移量。
在一些可能的实施方式中,在所述的返回存储键名和偏移量之后,还可以包括:
检查租约锁的值是否与客户端信息相同,判断租约锁是否被其它客户端获取;
当租约锁未被其它客户端获取时,更新偏移量的值;
当租约锁已被其它客户端获取时,输出异常。
在一些可能的实施方式中,所述消息队列包括:Redis;所述的根据所述存储键名写入消息内容,并确定所述消息内容对应的消息时间序号entryId,具体包括:
根据所述存储键名将所述消息内容包含的消息体写入到Redis的Stream数据结构中;其中,所述消息体包括多个键值对;
接收返回的与所述消息体对应的消息时间序号entryId;其中,所述entryId包括时间戳和序号;所述entryId存储于Hash数据结构中。
在一些可能的实施方式中,所述的根据从节点复制偏移量和主节点复制偏移量判断复制偏移量是否未同步,具体包括:
当从节点复制偏移量大于或者等于主节点复制偏移量时,确定复制偏移量同步。
在一些可能的实施方式中,所述消息队列包括:Redis;所述的检查租约锁的值是否与客户端信息相同,判断租约锁是否被其它客户端获取,具体包括:
使用Redis的第一指令获取租约锁的值;其中,所述第一指令是get命令;
当未取得租约锁的值时,使用Redis的第二指令将租约锁的值设置为目标客户端信息;其中,所述第二指令是set命令;
当取得租约锁的值时,将取得的租约锁的值与传入的目标客户端信息进行比较;
当取得的租约锁的值与传入的目标客户端信息不同时,则判定租约锁已被其它客户端获取;
当取得的租约锁的值与传入的目标客户端信息相同时,则判定租约锁未被其它客户端获取,并且通过Redis的第三指令对所述租约锁进行续约,续约时长为指定时长;其中,所述第三指令是expire命令。
在一些可能的实施方式中,所述的方法还包括:
在封锁交易时,使用特定关键字写入租约锁,使得消费者端在获取租约锁时失败;
在恢复交易时,为每个租约锁设置随机过期时间。
第二方面,本发明实施例提供了一种消息队列的处理方法,应用于消息生产端,所述方法包括:
获取队列名、业务关联key键名和消息内容;
根据所述业务关联key计算确定分区号;
将所述队列名和所述分区号拼接分区名;
将所述分区名发送至消息队列,以触发所述消息队列根据所述分区名将所述消息内容写入所述消息队列。
在一些可能的实施方式中,在所述的根据所述分区名将所述消息内容写入消息队列之后,还包括:
判断所述消息内容是否已经写入至所述消息队列;
如果所述消息内容已经写入至所述消息队列,则返回写入成功的信息;
如果所述消息内容没有写入至所述消息队列,则进一步判断所述消息内容是否丢失;
如果所述消息内容已丢失,则抛出异常;
如果所述消息内容未丢失,则进一步判断将所述消息内容写入所述消息队列的写入时长是否超出了预设的时间阈值;
如果所述写入时长超出了预设的时间阈值,则输出所述消息内容写入失败。
第三方面,提供一种计算机可读存储介质,其上存储有计算机程序,该程序被处理器执行时实现如上所述的任意一种消息队列的处理方法。
第四方面,提供一种计算机设备,其包括:
一个或多个处理器;
存储装置,用于存储一个或多个程序;
当所述一个或多个程序被所述一个或多个处理器执行时,使得所述一个或多个处理器实现如上所述的任意一种消息队列的处理方法。
第五方面,提供一种消息队列的处理***,其包括:
消息生产端,用于获取队列名、业务关联键名key和消息内容;根据所述业务关联key计算确定分区号;将所述队列名和所述分区号拼接成分区名;将所述分区名发送至消息队列,以触发所述消息队列根据所述分区名将所述消息内容写入所述消息队列;
消息队列设备,用于根据消息生产端发送的分区名,获取写分段索引的值;根据所述分区名从写分段索引的Hash中查询得到分段号,并且使用所述分区名和所述分段号拼接获得存储键名;根据所述存储键名写入消息内容,并确定所述消息内容对应的消息时间序号entryId;向所述消息队列的预设数据结构中写入标记,所述标记用于指示所述消息内容未在所述消息队列的主节点和从节点之间完成同步;其中,所述消息队列包括主节点和从节点;判断已写入的消息内容个数是否达到分段的预设长度;当已写入的消息内容个数达到分段的预设长度时,将所述写分段索引的值加1;获取消息队列的主节点复制偏移量;向所述消息生产端返回所述entryId和所述消息队列的主节点复制偏移量。
第六方面,提供一种消息队列的处理装置,设置于消息队列设备中,所述处理装置包括:
写索引获取模块,用于根据消息生产端发送的分区名,获取写分段索引的值;
存储键名拼接模块,用于根据所述分区名从写分段索引的Hash中查询得到分段号,并且使用所述分区名和所述分段号拼接获得存储键名;
消息写入模块,用于根据所述存储键名写入消息内容,并确定所述消息内容对应的消息时间序号entryId;
标记写入模块,用于向所述消息队列的预设数据结构中写入标记,所述标记用于指示所述消息内容未在所述消息队列的主节点和从节点之间完成同步;其中,所述消息队列包括主节点和从节点;
消息数量判断模块,用于判断已写入的消息内容个数是否达到分段的预设长度;
写索引更新模块,用于当已写入的消息内容个数达到分段的预设长度时,将所述写分段索引的值加1;
复制偏移量获取模块,用于获取消息队列的主节点复制偏移量;
写入反馈模块,用于向所述消息生产端返回所述entryId和所述消息队列的主节点复制偏移量。
进一步地,所述的处理装置,还包括:
运行信息获取模块,用于获取消息队列的运行信息;其中,所述消息队列包括主节点和从节点;
故障切换判断模块,用于根据所述消息队列的运行信息,判断是否发生了主节点故障切换;
第一主从同步判断模块,用于如果发生了主节点故障切换,进一步根据从节点复制偏移量和主节点复制偏移量判断复制偏移量是否未同步;
失败反馈模块,用于如果复制偏移量未同步,则确定消息已丢失,向所述消息生产端返回写入失败信息;
第二主从同步判断模块,用于如果未发生主节点故障切换,则确定主节点正常,依次获取每个待检查的从节点的复制偏移量,判断每个待检查的从节点的复制偏移量是否已经完成复制;
成功反馈模块,用于当任意待检查的从节点的复制偏移量已经完成复制时,向所述消息生产端返回写入成功信息。
第七方面,提供一种消息队列的处理装置,设置于消息生产端,所述处理装置包括:
信息获取模块,用于获取队列名、业务关联key和消息内容;
分区号计算模块,用于根据所述业务关联key计算确定分区号;
分区名拼接模块,用于将所述队列名和所述分区号拼接成分区名;
写入触发模块,用于将所述分区名发送至消息队列,以触发所述消息队列根据所述分区名将所述消息内容写入所述消息队列。
进一步地,所述的处理装置,还包括:
写入判断模块,用于判断所述消息内容是否已经写入至所述消息队列;
写入反馈模块,用于如果所述消息内容已经写入至所述消息队列,则返回写入成功的信息;
丢失判断模块,用于如果所述消息内容没有写入至所述消息队列,则进一步判断所述消息内容是否丢失;
异常提示模块,用于如果所述消息内容已丢失,则抛出异常;
超时判断模块,用于如果所述消息内容未丢失,则进一步判断将所述消息内容写入所述消息队列的写入时长是否超出了预设的时间阈值;
所述写入反馈模块,还用于如果所述写入时长超出了预设的时间阈值,则输出所述消息内容写入失败。
上述技术方案具有如下有益效果:由于消息队列是分区的,具有多个并行的分区,每个分区内部具有多个分段,每个分段中存储多条消息内容,分区名和分段号拼接成存储键名,根据该存储键名向消息队列中写入消息内容,并得到该消息内容对应的消息时间序号entryId,其可保障消息处理的时序,并且向消息内容中写入未主从同步的标记,向消息生产端返回entryId和主节点复制偏移量,有利于保障消息处理的时序,并且实现消息内容写入过程的可靠性,避免数据丢失。
附图说明
为了更清楚地说明本发明实施例或现有技术中的技术方案,下面将对实施例或现有技术描述中所需要使用的附图作简单地介绍,显而易见地,下面描述中的附图仅仅是本发明的一些实施例,对于本领域普通技术人员来讲,在不付出创造性劳动的前提下,还可以根据这些附图获得其它的附图。
图1是本发明实施例的Redis消息队列线程与存储模型;
图2A是本发明实施例的一种消息队列的处理方法的流程图;
图2B是本发明实施例的另一种消息队列的处理方法的流程图;
图3A是本发明实施例的消息生产端逻辑流程图一;
图3B是本发明实施例的消息生产端逻辑流程图二;
图4是本发明实施例的消费端逻辑流程图;
图5是本发明实施例的计算机设备的功能框图。
具体实施方式
下面将结合本发明实施例中的附图,对本发明实施例中的技术方案进行清楚、完整地描述,显然,所描述的实施例仅仅是本发明一部分实施例,而不是全部的实施例。基于本发明中的实施例,本领域普通技术人员在没有做出创造性劳动前提下所获得的所有其它实施例,都属于本发明保护的范围。
以下首先对本发明各实施例将使用到的技术术语进行定义说明:
事务:在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元,由事务开始语句和事务结束语句之间执行的全体操作组成,是恢复和并发控制的基本单位。事务应包含原子性、一致性、隔离性、持久性(ACID)的特点。事务执行的语句要么全成功(提交)要么全失败(取消),同时是从一个稳定状态(区别于中间状态)到达另一个稳定状态。不同事务间不能相互干扰(事务A与事务B共享的数据只能在其中一个事务提交或取消后才能被另一个事务使用),事务一旦提交,其结果作为一个稳定状态不会被其它操作或故障影响。
Redis(Remote Dictionary Server ,远程字典服务)数据结构:String、Hash、List、Stream都是Redis API提供的数据存储结构。
Stream:Redis5.0新特性的一种新型数据结构,存储占用的内存空间约为原有List数据结构的15%(即原本List存储100万条数据需要10G内存,使用Stream存储相同的数据仅需要不到2G内存),检索时间复杂度由原本的O(N)降低到O(logN)(即假设O(1)为5微秒,原本要检索第10万个元素需要5微秒*100 000=0.5秒,而使用Stream,在检索100万条数据中第10万个元素时,log(1 000 000)=6,仅需要5微秒*6=30微秒)。其每一个元素称为Entry,由两部分组成。一个是基于时间戳的唯一识别ID,称为消息时间序号entryId,可以用于对指定元素的直接检索;另一个是一组键值对的集合,用于存储不同的数据字段。
Redis Lua:Redis内嵌的脚本语言,可以组合一定的业务逻辑。Lua脚本具有独占性,即Lua脚本执行期间不受理杀死该脚本外的任何Redis操作请求,可以确保其中执行的Redis数据变更的ACID(原子性、一致性、隔离性和持久性)特性。
Redis主-从复制:Redis自身分布式高可用的基础,每个主节点下可以有若干个从节点,应用只与主节点进行数据交互,主节点通过网络通讯管道将自身的数据同步给从节点,使其与自身保持一致。主节点和从节点都会维护一个复制偏移量的值,每次变更操作主节点都是先变更自身的复制偏移量,再将数据推送给从节点,从节点执行完变更后再更新自身复制偏移量。从节点会定时向主节点上报自身已完成复制的偏移量,并将其存储在主节点的同步信息中。该同步信息可供监控***查询,当主节点宕机时,被记录的复制偏移量最接近主节点复制偏移量的从节点会优先成为主节点。
Redis CDC:基于Redis主-从复制协议,实时感知Redis中数据的变化,由此可以衍生一系列的操作,主要应用场景有:流程的驱动(感知到数据写入触发下一步操作),跨机房、多主节点之间的数据同步,数据的异步分发等。
发明人发现,RocketMQ不能严格保证消息的顺序或者可靠性;Kafka总体可满足使用,但不支持从外部干预的交易封锁能力,并且其生产部署架构稍重,会增加***复杂度。为解决上述至少一个技术问题,提出如下技术方案:
实施例一
金融***对账户类***的一致性要求中,要求同一账户的前一笔交易未完成时,后一笔交易不能被处理,即要求严格的时序。在进行异步处理时,需要使用消息队列进行***间通讯,并严格保证同一账户消息的时序性。同时,在进行批量处理时,要求消息队列能达到每秒万级的消息吞吐能力,且要有百万级以上的消息堆积能力。
本发明实施例中的轻量化部署的消息队列能够应用于核算微服务***中,并具备如下能力:可以支撑1小时内百万以上消息的处理/堆积能力;支持指定业务关联key(例如交易流水号、借据号等具有***中可识别唯一性的业务字段)的消息严格按照顺序处理;具备从外部可干预的封锁/恢复消费能力;具有消息队列的熔断机制,当发生消息连续大量处理异常时,能自动中断处理,由人工介入核对后再次恢复的能力。
图1是本发明实施例的Redis消息队列线程与存储模型。如图1所示,图1中各术语的含义及作用如下:
Provider:消息的生产者(生产端),业务代码通过API操作消息写入。
Consumer: 消息的消费者(消费端),从消息队列中读取消息并执行相应的处理,已处理过的消息会被消息队列标记,不会再次被读取到。
Partitioner: 队列的分区路由,通过可自定义的算法,对业务关联key进行计算,分配指定的分区名。对于使用同一个key进行计算的,确保其分到同一个分区。
Scheduler: 定时任务调度器,用于检索过期(已读取)的消息并淘汰出内存,同时还负责消息异常时的处理。
Segment indexs: 消息分段的索引存储,其中元素为每个队列分区名当前处理的分段号,供消息写入、读取时检索。
Partitions offset: 每个队列分区当前处理的偏移量,其是Redis Stream数据结构中的entryId,消息读取时需要从记录的偏移量之后进行读取,消息消费成功后会更新(提交)偏移量的值。
Partition Leases: 分区租约锁,通过String数据结构存储,其值为应用所在服务器主机名、进程号、线程池Id、线程Id拼接而成。通过Redis Lua确保同一时间段内只会被写入一次,即除非该值不存在或与自己要写入的值相同时才算获取到租约锁,否则不能写入新值。当启动多个消费者时,由于只有一个线程能写入成功,该过程称为租约锁的争抢,只有获取到租约锁的线程(在微观时间上第一个执行Lua脚本的)才能进行数据的读取和偏移量的提交,其它同分区线程进入休眠。租约锁有过期时间,获取到租约锁的线程在每次读取数据前会更新过期时间,该行为称为续约。如果租约锁由于长时间未续约导致了过期,会引发新一轮的争抢。
Info replication: Redis主节点中存储的主-从数据同步信息。
Redis Replica: Redis从节点,实时同步主节点的数据,确保主节点宕机后可以代替主节点继续提供数据服务。
Partition Thread: 存在于消费者中的分区线程,每个队列分区对应一个线程,负责从消息队列中读取消息、业务逻辑执行后提交消息的偏移量。
Worker Pool: 用于对消息执行业务逻辑的线程池,Partition Thread从消息队列中取得消息后放入Worker Pool中执行,执行结果反馈给Partition Thread后消费结束。
Provider、Redis、Consumer可以在不同的服务器上运行,也可以在相同的服务器上运行。消息队列设备是运行消息队列的设备(服务器)。
以下详细描述消息写入与存储、消息的消费、以及消息的封锁与恢复。
(1)消息写入与存储:
基于Redis5.0的Stream数据结构,由Redis提供的原子性API实现基础的消息队列存储,确保消息按写入顺序排序,Stream会为每个消息分配一个基于时间戳的消息时间序号entryId (按Stream设计,其由时间戳、短横线和一个64比特的序号组成,支持对每毫秒264条写入进行排序)。
基于Redis主-从同步的单线程串行机制,通过Redis Lua脚本校验Redis主-从复制偏移量来确保写入操作已经被同步到了至少一个从节点上,从而确保消息的可靠性。原本Redis存在主节点写入完成就反馈给应用,一旦主节点宕机时数据还未同步给从节点,主节点切换后就会存在数据丢失。这里确保数据已经被同步给了从节点后才反馈给应用写入成功的消息,否则该写入就会被视为无效的。
将写入的消息按业务关联key 进行哈希路由,写入不同的分区中。
每个分区按一定长度进行分段,超过一定长度时分段号加一,并记录读/写指向分段号。
队列名、分区号、分段号共同构成了Stream数据结构的存储键名。
由定时任务调度器Scheduler检查Redis,将所有分段号小于读分段号的数据淘汰出内存。在淘汰前可根据配置将其持久化到磁盘。
(2)消息的消费:
消费者端每分区一个单线程进行消费,通过分布式租约锁,确保同一时间只有一个消费线程可以消费指定分区来确保消费的顺序性。
使用一个Hash数据结构来存储每个Stream的entryId。
通过Redis lua脚本进行租约锁检查,拼接Stream键名(队列名、分区号、分段号共同构成了Stream数据结构的键名),取entryId合成一个原子操作。
取得消息并处理后,由Redis Lua脚本检查租约锁有效性、变更entryId存储值、更新读分段号操作合并在一个事务中,来确保消息处理的原子性。
通过对一次性取得多条消息后,检查其业务关联key是否相同决定是串行执行还是并行执行,以提升消息消费的速度。即当业务关联key相同时,决定是串行执行,当业务关联key不相同时,决定是并行执行。
(3)消息的封锁与恢复:
借助于租约锁的特性,使用特定关键字(例如barrier)写入租约锁,使得消费端在获取租约锁时失败,从而实现交易封锁。
恢复交易时,通过为每个租约锁设置随机过期时间,使因运行环境微小差异而唤醒时间有差异的分区线程能较公平地得到获取租约锁的机会。在租约锁过期时由消费线程争抢租约锁以恢复交易。
当消息处理失败引发异常达到在指定时间区间内的指定数量时,定时任务调度器Scheduler会触发相应的事件,自动执行交易封锁。
实施例二
图2A是本发明实施例的一种消息队列的处理方法的流程图。如图2A所示,该方法应用于消息队列设备,例如包括但不限于Redis消息队列,其包括如下步骤:
S101、根据消息生产端发送的分区名,获取写分段索引值;
在本实施例中,分段是一个存储键名下存储的消息内容的集合,是一个Stream数据结构。在存储队列的同个Redis中以Hash数据结构存储,在指定该Hash键名,以分区名为字段名,可以访问到该分区的写索引。对应的Redis操作指令为:
hget streamreaderWriteIdx 分区名。
另外,与写索引对应的还有读索引(streamreaderReadIdx),同样存储在另一个Hash数据结构中。
在本实施例中,消息是分段存储的,每个分段的长度由参数进行设置,默认一个分段是10万条消息,但不是严格的,一般实际使用中是10万零几条消息。分段索引是指向操作中分段的指针,分为写索引和读索引。作为一个举例,例如消息队列中写入了99万条消息,这时写索引是10;而这时消费者(Consumer)刚刚处理到34万条消息,读索引就是4。
S102、根据所述分区名从写分段索引的Hash中查询得到写分段号,并且使用分区名和分段号拼接获得存储键名;在本实施例中,分区名可以由队列名和分区号拼接而成。
S103、根据存储键名写入消息内容,并确定所述消息内容对应的消息时间序号entryId;
在一个示例中,消息时间序号entryId可以由如下三部分组成:时间戳、短横线、同一时间内一个64比特的序号。消息时间序号entryId是消息顺序的保障。
S104、向所述消息队列的预设数据结构中写入标记,该标记用于指示消息内容未在消息队列的主节点和从节点之间完成同步;
具体地,通过写入标记,有得于避免整个一致性保障的逻辑无法闭环。在一个示例中,将标记写入redis 的Set数据结构中。标记是在luaWrite里写入,在luaCheck中清除,在luaGetOffset后校验。进一步地,Set是Redis的一种集合类的数据结构,其中存储的值不会有重复,通过命令sadd streamReaderPrepare entryId写入标记,通过命令sremstreamReaderPrepare entryId删除标记,通过命令sismember streamReaderPrepareentryId校验标记。在进一步的实施例中,在写入标记时,将entryId作为值,写入另一个Set数据结构中,Consumer在消费时(执行xread前)检查这个Set数据结构,如果其中包含了xread参数中的entryId,就阻塞1秒。
S105、判断已写入的消息内容个数是否达到分段的预设长度;
S106、当已写入的消息内容个数达到分段的预设长度时,将写分段索引值加1;
S107、获取消息队列的主节点复制偏移量;
S108、向消息生产端返回写入的entryId和消息队列的主节点复制偏移量。
在本实施例中,entryId是Redis术语。Redis的Stream数据结构是有序集合,集合中的元素称为Entry。Entry由两个部分组成,可以理解为类似报文头和报文体的形式:
报文头就是entryId,由时间戳和具有预设比特的序号(Redis自行生成的,有序、自增的)组成,可以用于定位元素,Redis操作命令为:
xrange 键名 entryId entryId count 1。
报文体是多个键值对组成,可以理解为是Hash数据结构。
在进一步的实施例中,在S108之后,还可以包括如下步骤:
获取消息队列的运行信息;其中,所述消息队列包括主节点和从节点;
根据消息队列的运行信息,判断是否发生了主节点故障切换;具体地,根据Redis的集群部署方案不同,故障转移(Failover)实现机制不同。其大体原理包括:由其它的Redis节点或外部监控节点(以下简称“节点”)对存储数据的Redis主节点(以下简称“Redis主节点”)进行定期的健康检查/探活,如果某个节点在一定时间内未收到Redis主节点的响应,则认为Redis主节点已宕机/下线,称为主观下线(sdown),当多个节点之间就自身探测到的主观下线达成一致时,认为Redis主节点确实已经宕机/下线,称为客观下线(odown),此时允许提升原Redis主节点(以下简称“原主节点”)的一个从节点为新的Redis主节点(以下简称“新主节点”),并将所有对原主节点的访问转到新主节点。
如果发生了主节点故障切换,进一步根据从节点复制偏移量和主节点复制偏移量判断复制偏移量是否未同步;具体地,确定“复制偏移量同步”等效于满足以下表达式:从节点复制偏移量(slave:offset)>=LuaWrite返回的主节点复制偏移量(master_repl_offset)。
如果复制偏移量未同步,则确定消息已丢失,返回写入失败信息;
如果未发生主节点故障切换,则确定主节点正常,依次获取每个待检查的从节点的复制偏移量,判断每个待检查的从节点的复制偏移量是否已经完成复制;
当任意待检查的从节点的复制偏移量已经完成复制时,返回成功信息。
在进一步的实施例中,在当任意待检查的从节点的复制偏移量已经完成复制时,返回成功信息之后,还可以包括:
拼接租约锁键名;根据租约锁键名,判断租约锁是否存在;如果租约锁不存在,则设置租约锁的值为客户端信息,过期时间为指定时长;如果租约锁存在,则进一步判断租约锁与客户端信息是否相同;当租约锁与客户端信息相同时,更新租约锁的过期时间;当租约锁与客户端信息不相同时,确定租约锁已被占用,输出获取租约锁失败的指示信息。
在进一步的实施例中,该方法还可以包括:判断是否成功获取租约锁;如果已成功获取租约锁,则获取读分段索引;将分区名和分段号拼接成存储键名;获取当前处理偏移量;向消费者端返回该存储键名和当前处理的偏移量。
在进一步的实施例中,在返回存储键名和偏移量之后,还可以包括:检查租约锁的值是否与客户端信息相同,判断租约锁是否被其它客户端获取;当租约锁未被其它客户端获取时,更新偏移量的值;当租约锁已被其它客户端获取时,输出异常。
图2B是本发明实施例的另一种消息队列的处理方法的流程图。如图2B所示,该方法应用于消息生产端,其包括如下步骤:
S201、获取队列名、业务关联key和消息内容;
S202、根据业务关联key计算确定分区号;
S203、将队列名和分区号拼接成分区名;
S204、将分区名发送至消息队列,以触发消息队列根据分区名将消息内容写入消息队列。
在进一步的实施例中,在S204中的将分区名发送至消息队列,以触发消息队列根据分区名将消息内容写入消息队列之后,还包括如下步骤:
判断消息内容是否已经写入至消息队列;
如果消息内容已经写入至消息队列,则返回写入成功的信息;
如果消息内容没有写入至消息队列,则进一步判断消息内容是否丢失;
如果消息内容已丢失,则抛出异常;
如果消息内容未丢失,则进一步判断将消息内容写入消息队列的写入时长是否超出了预设的时间阈值;
如果写入时长超出了预设的时间阈值,则输出消息内容写入失败。
以下对图2A和图2B所示的方法进行更加详细的说明:
图3A是本发明实施例的消息生产端逻辑流程图一;图3B是本发明实施例的消息生产端逻辑流程图二。如图3A和图3B所示,消息生产端的工作流程包括如下步骤:
S1、业务代码执行:调用消息入队API,向消息的生产者Provider传入队列名、业务关联key、消息内容。
步骤S2-S4由消息的生产者Provider执行。
S2、消息的生产者Provider中的队列的分区路由Partitioner执行:根据业务关联key计算确定分区号。
具体地,业务关联key是某种可确定唯一业务属性的值。比如贷款时的借据号,通过这个可以操作同一个借据下的放款/还款操作,对于循环贷款,余额不足时需要先还款再借款,这个顺序不能错,那就需要时序保障。
分区号是本发明实施例给一个消息队列可以最多并行处理的设置。因为时序保障是单线程,加上分区就可以并发了,保障同一分区内的顺序。
计算分区号的方法可以是对这个业务关联key做哈希计算后取模(余数),然后用这个分区号去拼成对应的分区名。
哈希算法支持自定义,也可以是路由表的,同一个值经过相同哈希算法计算后的值也是相同的,由此来确保同一业务key的时序。
S3、消息的生产者Provider中的队列的分区路由Partitioner执行:使用队列名和分区号拼接成分区名。
S4、消息的生产者Provider触发或者启动消息队例Redis根据上述分区名将消息内容写入消息队列Redis。
具体地,本步骤是调用LuaWrite脚本的入口,本身只是LuaWrite脚本的启动动作,写入操作最终是在Lua脚本中完成。
S5、Redis中的LuaWrite执行:获取写分段索引的值。具体地,本步骤可以根据消息生产端发送的分区名,从写索引的Hash中以分区名查询到的值作为写分段号。
其中,在本实施例中,分区后再分段。同一个消息队列先有分区,多个分区之间是并行的,单个分区是分段的,分段是具有顺序的。
在本实施例中,在存储队列的同一个Redis中以Hash数据结构存储,指定该Hash键名,以分区名为字段名,可以访问到该分区的写索引。对应的Redis操作指令为:
hget streamreaderWriteIdx 分区名。
另外,与写索引对应的还有读索引(streamreaderReadIdx),同样存储在另一个Hash数据结构中。
S6、Redis中的LuaWrite执行:使用分区名和分段号拼接获得存储键名。
具体地,分段号是以分区名为字段名,从写索引的Hash中查询得到,即执行了Redis命令:hget streamreaderWriteIdx 分区名。
S7、Redis中的LuaWrite执行:根据该存储健名写入消息内容,并确定该消息内容对应的消息时间序号entryId。
具体地,本步骤以存储键名写入Stream的元素,存储键名来自前面步骤S5、S6两步骤获取的数据进行字符串拼接。
在一个示例中,写入是执行了如下Redis命令:
xadd 存储键名 * 消息体(若干个键值对)。
该命令会返回entryId。
在一个示例中,消息时间序号entryId可以由如下三部分组成:时间戳、短横线、同一时间内一个例如64比特的序号。消息时间序号entryId是消息顺序的保障。
S8、Redis中的LuaWrite执行:写入标记。
具体地,写入标记的目的是Redis写入但未同步的标记,在一个集合里存储了上一步操作返回的entryId。用于消费者消费时检索到该标记时,认为该条消息未完成主从同步,是不可见消息,需要等待主从同步完成。
S9、Redis中的LuaWrite执行:判断已写入消息个数是否达到分段的预设长度。如果是,则执行步骤S10,如果否,则执行步骤S11。
S10、Redis中的LuaWrite执行:写分段索引值加1。
S11、Redis中的LuaWrite执行:获取Redis主节点复制偏移量,其是主节点的复制偏移量。
具体地,从Redis的命令info replication获取,然后使用Lua的函数,以正则表达式过滤出主节点的复制偏移量master_repl_offset字段的值。
S12、Redis中的LuaWrite执行:向消息的生产者Provider返回写入的entryId和主节点复制偏移量。
在本实施例中,entryId是Redis术语。Redis的Stream数据结构是有序集合,集合中的元素称为Entry。Entry由两个部分组成,可以理解为类似报文头和报文体的形式:
报文头或消息头就是entryId,由时间戳和序号(Redis自行生成的,有序、自增的)组成,可以用于定位元素,Redis操作命令为:
xrange 键名 entryId entryId count 1。
报文体或消息体是由多个键值对组成,可以理解为是Hash数据结构。
S13、消息的生产者Provider执行:判断Redis Lua脚本执行是否成功。如果是,则执行步聚S14,如果否,执行步骤S30。
S14、消息的生产者Provider执行:获取复制偏移量。
具体地,Redis的从节点在复制主节点信息的时候,会不断上报自已的复制偏移量(slave:offset)
由于Redis的数据是串行写入的,也就是说偏移量相同时写入的数据也相同。
利用这个特性,在Lua脚本执行完写入操作时获取一下主节点的复制偏移量(master_repl_offset),当从节点的复制偏移量(slave:offset)达到这个值时,可以认为该数据已经被复制。
在一个Lua脚本的执行过程中,Redis无法响应从节点上报的偏移量,所以需要先结束LuaWrite脚本,再由Provider轮询该值。
S15、Redis中的LuaCheck执行:获取Redis运行信息。
S16、Redis中的LuaCheck执行:根据Redis运行信息,判断是否发生了主节点故障切换。如果是,则执行步骤S17,如果否,则执行步骤S19。
S17、Redis中的LuaCheck执行:判断复制偏移量是否未同步。如果是,则确定消息已丢失,执行步骤S18,如果否,则执行步骤S23。
具体地,当满足如下表达式时,确定复制偏移量同步:
从节点复制偏移量(slave:offset)>=LuaWrite返回的主节点复制偏移量(master_repl_offset)。
S18、Redis中的LuaCheck执行:返回失败消息。然后进入步骤S23。
具体地,LuaCheck脚本是由Provider轮询调用的,如果从节点上报的偏移量达到了,轮询就结束了,否则继续轮询。
一旦在两次脚本调用的间隔里发生了故障转移,info 中的run_id和master_repl_id会发生变化,此时未复制到从节点的信息就丢失了。所以可以认为消息写入失败。
Lua脚本返回的失败是给Provider的,然后由Provider再反馈给业务代码。
S19、Redis中的LuaCheck执行:当主节点正常时,获取一个从节点的复制偏移量。
S20、Redis中的LuaCheck执行:判断是否已完成复制。如果是,则执行步骤S21,如果否,则执行步骤S22。
S21、Redis中的LuaCheck执行:返回写入成功信息。
S22、Redis中的LuaCheck执行:判断是否复制偏移量未完成同步并且还有未检查的从节点,如果否,则返回步骤S19,如果是,则执行步骤S23。
S23、Redis中的LuaCheck执行:返回继续等待信息。
具体地,这里是Provider对Lua脚本的返回值进行处理。
S24、消息的生产者Provider执行:判断消息是否已同步完成。如果是,则执行步骤S25,如果否,则执行步骤S26。
具体地,本步骤是判断是否同步完成,即从节点的复制偏移量slave:offset已经达到或超过了步骤S12返回的主节点的复制偏移量master_repl_offset的值。
S25、消息的生产者Provider执行:向业务代码返回写入成功。
S26、消息的生产者Provider执行:判断是否消息已丢失。如果是,则执行步骤S27,如果否,则执行步骤S28。
S27、消息的生产者Provider执行:抛出异常。然后执行步骤S28。
S28、业务代码执行:写入失败。
具体地,本步骤是业务代码处理写入失败的策略,由业务代码开发者编写,捕获S27抛出类型的异常,进行相应的业务代码处理。
S29、消息的生产者Provider执行:继续检查并且进一步判断是否未超时,即判断消息内容写入消息队列的写入时长是否超出了预设的时间阈值。如果是,则返回步骤S14,如果否,则进入步骤S30。
在一个示例中,超时时间对应的阈值为使用者通过***参数设置,在Provider初始化时加载。
S30、业务代码执行:判断是否写入检查超时。如果是,则执行步骤S31,如果否,则执行步骤S32。
S31、业务代码执行:处理消息写入失败。在本步骤中超时也是一种失败。步骤S31之后结束流程。
S32、业务代码执行:继续处理后续的代码。步骤S32之后结束流程。
在本实施例中,如果前面的操作都成功了,即消息写入了Redis,并经由复制偏移量检查(slave:offset>=master_repl_offset)通过的,视为写入成功。则业务代码可以处理后续的业务。否则,业务代码就会走到写入失败的分支进行处理。
图4是本发明实施例的消费端逻辑流程图。如图4所示,包括如下步骤:
S41、消息的消费者Consumer执行:拼接客户端信息。
S42、消息的消费者Consumer执行:触发或请求获取偏移量。S42只是入口,此时并没有获取到偏移量。本步骤是Consumer向Redis发出获取偏移量的请求。
在本实施例中,该偏移量是消息队列的术语,对应等效于Redis中Stream的entryId。
本步骤是调用LuaGetOffset的入口。只是这个Lua脚本叫“getOffset”,直译是获取偏移量。
S43、Redis中的LuaGetOffset执行:拼接租约锁键名,并检查。
具体地,租约锁可以通过String数据结构存储,其值为应用所在服务器主机名、进程号、线程池Id、线程Id拼接而成。java中每个对象都有一个hash值,线程池id,线程id都指的是这个哈希值。进程号和服务器主机名可以是通过java自带方法获取的。租约锁的键名可以为:Reading__分区名。
具体地,在本步骤中,还可以借助于租约锁的特性,使用特定关键字(例如barrier)写入租约锁,使得消费端在获取租约锁时失败,从而实现交易封锁。
S44、Redis中的LuaGetOffset执行:根据租约锁键名,判断租约锁是否不存在。如果不存在,则执行步骤S45,如果存在,执行步骤S46。
具体地,可以在lua脚本中执行指令:get 租约锁键名,没取到值则表示租约锁不存在,则在步骤S45进一步执行如下指令:set 租约锁键名 租约锁值,get指令取到了值则表示租约锁存在,则在步骤S46进一步判断是否与参数传入的租约锁值相同,不同就直接返回,相同则继续执行后面的步骤。
S45、Redis中的LuaGetOffset执行:设置租约锁的值为客户端信息,过期时间为例如但不限于1分钟。
S46、Redis中的LuaGetOffset执行:判断是否满足租约锁存在并且与客户端信息相同。如果是,则执行步骤S47,如果否,执行步骤S48。
S47、Redis中的LuaGetOffset执行:确定是自身的租约锁,更新租约锁的过期时间,例如续约1分钟,本实施例对租约锁的续约时长不做限制。
S48、Redis中的LuaGetOffset执行:确定租约锁已被占用,输出获取租约锁失败的提示信息。
S49、Redis中的LuaGetOffset执行:判断是否获取租约锁成功。如果是,则依次执行步骤S50至步骤S53,如果否,则执行步骤S53。
具体地,租约(Lease)是指在多个节点中,通过对一个逻辑上单点的共同资源的抢占,利用其独占的特性,只会有一个写入成功,写入成功的那个节点拥有对资源的写操作权限(持有锁),其它节点只能读取。经过一段时间(过期时间)后,该资源被自动释放,又开始新一轮争抢。在这个过程中,持有锁的节点可以发起续约,即利用自身的写权限更改过期时间,从而达到长期持有锁的目的。而未持有锁的节点,处于standby状态。当持有锁的节点宕机时,由于不再续约,该资源在过期时间到达后就会被释放,处于standby状态的节点就有机会抢占该资源,代替原节点继续执行操作。
通常对于单一业务操作的分布式锁的过期时间不大于20秒,这种不会被认为是租约,而本实施例使用的是多个节点是standby模式,超时时间是分钟级的,所以更靠近租约。
而在Lua脚本中,由于脚本本身的原子性,可以通过ttl=-2、exists=0、get=nil这些进行key不存在的等效判断,并且可以执行更复杂的后续操作。
租约锁是否获取成功是在Lua脚本一开始通过get key返回nil或返回值与传入参数相同来确认的,未获取到的脚本就结束了,获取成功的才会做后续操作。
S50、Redis中的LuaGetOffset执行:获取读分段索引。
S51、Redis中的LuaGetOffset执行:拼接存储键名。具体地,存储键名可以由分区名和分段号拼接而成的。
S52、Redis中的LuaGetOffset执行:获取当前处理偏移量。具体地,从Partitions这个Hash中使用存储键名获取对应字段的值。
S53、Redis中的LuaGetOffset执行:向消费者端返回存储键名和偏移量。
S54、消息的消费者Consumer执行:判断是否获取到租约锁。如果是,则依次执行步骤S55至步骤S57,如果否,则执行步骤S58。
S55、消息的消费者Consumer执行:获取消息。
具体地,本步骤可以根据存储键名、上一次的偏移量,从Redis中获取消息,例如可以执行Redis命令:
xread streams 存储键名 偏移量。
在进一步的实施例中,S55还包括如下步骤:检查标记是否存在,如果标记存在时,需要阻塞等待的操作。这里是为了防止有未完成标记(Provider未确认消息已完成主从同步,可能会反馈写入失败)的信息被消费,引起生产端与消费端不一致产生的业务问题。通过标记有利于实现整个一致性保障的逻辑闭环。
S56、消息的消费者Consumer执行:业务代码执行。
S57、消息的消费者Consumer执行:提交处理偏移量。然后进入步骤S60。
具体地,步骤S57可以通过更新Partitions这个Hash中对应于存储键名的字段的值,完成提交偏移量。
S58、消息的消费者Consumer执行:休眠指定时长例如1秒。然后进入步骤S63。
具体地,休眠指定时间区间,例如1秒是在没有获取到租约锁,或没有取到消息时的操作,有利于空闲时降低CPU负载,一般时间在200毫秒(本地)到1秒(通过网络访问)左右,上述休眠操作能够兼顾CPU使用率和实时时效性。
S59、Redis中的LuaCommitOffset执行:检查租约锁的值是否与客户端信息相同,判断租约锁是否被其它客户端获取。如果是,则执行步骤S61,如果否,则执行步骤S60。
客户端信息是作为租约锁的值存在的,通过以下Redis命令进行设置:
set 租约锁 客户端信息;
在检查时,使用Redis命令:
get 租约锁;
如果未得到值(返回值为nil,从未设置过或已过期),则通过set命令直接设置,视为与传入的值相同;
将得到的值与Lua脚本参数传入的客户端信息进行比较。
如果相同,则获取租约锁,并可以进一步执行Redis指令进行例如1分钟的续约:
expire 租约锁 60;
如果传入的客户端信息与租约锁的值不同,未获取到租约锁,不再进行后续Lua脚本的操作,返回nil。
其中,Redis SET命令用于设置给定key的值。如果key已经存储其他值,SET就覆写旧值,且无视类型。Redis Get命令用于获取指定 key 的值。如果 key不存在,返回特殊值nil。如果key储存的值不是字符串类型,返回一个错误。Redis Expire命令用于设置key的过期时间,key过期后将不再可用,单位以秒计。
租约锁机制主要是防止单个客户端由于网络、宕机等原因无法执行消费操作时,由其它处于Standby状的客户端接管继续消费,从而避免单点故障。
这里主要是在执行CommitOffset时要进行一次校验:
因为Java在执行中可能由于网络、CPU负载高或者Java自身的垃圾回收机制进入到阻塞状态,这个状态是不会被程序感知的。但如果这个阻塞时间超过了租约锁的过期时间,Redis中的租约锁就过期了。那么其它的客户端就可以获取到租约锁来进行相关的处理。
这里的处理是为了以上描述的这种可能性,如果被其它客户端获取了租约锁,意味着Consumer在步骤S54-S57之间的操作可能被执行了两次,因此需要将异常抛给业务代码,由业务代码判断此操作是否有影响,是否需要补偿/冲正处理。
S60、Redis中的LuaCommitOffset执行:更新偏移量的值。然后进入步骤S62。
上述“复制偏移量”是Redis术语,单独的“偏移量”是消息队列术语。消息队列的“提交偏移量”是对消息ACK(确认已处理)的技术实现方式,这里的更新指的是通过更新Partitions中对应存储键名所在字段的值,实现提交偏移量的动作。
S61、Redis中的LuaCommitOffset执行:输出异常。然后进入步骤S62。
S62、消息的消费者Consumer执行:消费者正在运行。
本发明实施例的上述技术方案的技术效果包括:
1、基于Redis Stream本身,可以在8G内存的Redis中保留500万条以上的消息,并可以在消息消费完成后释放占用的内存,从而处理更多的消息。
2、实测在使用Intel Xeon E5-2680 v4 CPU、8C8G的虚拟机中,可以达到2万+/秒的消费速度,总吞吐能力(同时写入与消费)超过1万条/秒。
3、在实际使用中大幅提高了交易的处理速度。
4、由于使用Redis做队列,基于Redis主-从同步机制,辅以Redis的CDC 方案,使队列实现了可跨数据中心的异步数据同步机制。
5、队列封装使用简便,维护方便。
6、由于微服务***中Redis是必备组件,使用Redis做队列,架构得以简化,硬件成本和运维成本大幅降低。
7、确保了消息处理的时序。
8、支持熔断机制,在指定时间内达到指定失败笔数时封锁对应分区的消费。
9、解决了Redis本身占用内存和发生故障时数据丢失的问题,扩大了Redis的使用场景。
实施例三
本发明实施例还提供了一种计算机可读存储介质,计算机可读存储介质内存储有计算机程序,计算机程序被处理器执行时实现:
根据消息生产端发送的分区名,获取写分段索引的值;
根据所述分区名从写分段索引的Hash中查询得到分段号,并且使用所述分区名和所述分段号拼接获得存储键名;
根据所述存储键名写入消息内容,并确定所述消息内容对应的消息时间序号entryId;
向所述消息队列的预设数据结构中写入标记,所述标记用于指示所述消息内容未在所述消息队列的主节点和从节点之间完成同步;判断已写入的消息内容个数是否达到分段的预设长度;
当已写入的消息内容个数达到分段的预设长度时,将所述写分段索引的值加1;
获取消息队列的主节点复制偏移量;
向所述消息生产端返回所述entryId和所述消息队列的主节点复制偏移量。
本发明实施例还提供了另一种计算机可读存储介质,计算机可读存储介质内存储有计算机程序,计算机程序被处理器执行时实现:
获取队列名、业务关联key和消息内容;
根据所述业务关联key计算确定分区号;
将所述队列名和所述分区号拼接成分区名;
将所述分区名发送至消息队列,以触发所述消息队列根据所述分区名将所述消息内容写入所述消息队列。
所述集成的模块/单元如果以软件功能单元的形式实现并作为独立的产品销售或使用时,可以存储在一个计算机可读取存储介质中。基于这样的理解,本发明实现上述实施例方法中的全部或部分流程,也可以通过计算机程序来指令相关的硬件来完成,所述的计算机程序可存储于一计算机可读存储介质中,该计算机程序在被处理器执行时,可实现上述各个方法实施例的步骤。其中,所述计算机程序包括计算机程序代码,所述计算机程序代码可以为源代码形式、对象代码形式、可执行文件或某些中间形式等。所述计算机可读介质可以包括:能够携带所述计算机程序代码的任何实体或装置、记录介质、U盘、移动硬盘、磁碟、光盘、计算机存储器、只读存储器(ROM,Read-Only Memory)、随机存取存储器(RAM,Random Access Memory)、电载波信号、电信信号以及软件分发介质等。当然,还有其它方式的可读存储介质,例如量子存储器、石墨烯存储器等等。需要说明的是,所述计算机可读介质包含的内容可以根据司法管辖区内立法和专利实践的要求进行适当的增减,例如在某些司法管辖区,根据立法和专利实践,计算机可读介质不包括电载波信号和电信信号。
实施例四
本发明实施例还提供了一种计算机设备,如图5所示,包括一个或多个处理器301、通信接口302、存储器303和通信总线304,其中,处理器301,通信接口302,存储器303通过通信总线304完成相互间的通信。
存储器303,用于存放计算机程序;
处理器301,用于执行存储器303上所存放的程序时,实现:
根据消息生产端发送的分区名,获取写分段索引的值;
根据所述分区名从写分段索引的Hash中查询得到分段号,并且使用所述分区名和所述分段号拼接获得存储键名;
根据所述存储键名写入消息内容,并确定所述消息内容对应的消息时间序号entryId;
向所述消息队列的预设数据结构中写入标记,所述标记用于指示所述消息内容未在所述消息队列的主节点和从节点之间完成同步;判断已写入的消息内容个数是否达到分段的预设长度;
当已写入的消息内容个数达到分段的预设长度时,将所述写分段索引的值加1;
获取消息队列的主节点复制偏移量;
向所述消息生产端返回所述entryId和所述消息队列的主节点复制偏移量。
或者,处理器301,用于执行存储器303上所存放的程序时,实现:
获取队列名、业务关联key和消息内容;
根据所述业务关联key计算确定分区号;
将所述队列名和所述分区号拼接成分区名;
将所述分区名发送至消息队列,以触发所述消息队列根据所述分区名将所述消息内容写入所述消息队列。
处理器301可以是通用处理器,包括中央处理器(Central Processing Unit,CPU)、网络处理器(Network Processor,NP)等;还可以是数字信号处理器(Digital SignalProcessing,DSP)、专用集成电路(Application Specific Integrated Circuit,ASIC)、现场可编程门阵列(Field-Programmable Gate Array,FPGA)或者其它可编程逻辑器件、分立门或者晶体管逻辑器件、分立硬件组件。
通信接口302用于上述电子设备或计算机设备与其它设备之间的通信。
存储器303可以包括用于数据或指令的大容量存储器。举例来说而非限制,存储器303可包括硬盘驱动器(Hard Disk Drive,HDD)、软盘驱动器、闪存、光盘、磁光盘、磁带或通用串行总线(Universal Serial Bus,USB)驱动器或者两个或更多个以上这些的组合。在合适的情况下,存储器303可包括可移除或不可移除(或固定)的介质。在特定实施例中,存储器303是非易失性固态存储器。在特定实施例中,存储器303包括只读存储器(ROM)。在合适的情况下,该ROM可以是掩模编程的ROM、可编程ROM(PROM)、可擦除PROM(EPROM)、电可擦除PROM(EEPROM)、电可改写ROM(EAROM)或闪存或者两个或更多个以上这些的组合。
总线304包括硬件、软件或两者,用于将上述部件彼此耦接在一起。举例来说,总线可包括加速图形端口(AGP)或其它图形总线、增强工业标准架构(EISA)总线、前端总线(FSB)、超传输(HT)互连、工业标准架构(ISA)总线、无限带宽互连、低引脚数(LPC)总线、存储器总线、微信道架构(MCA)总线、***组件互连(PCI)总线、PCI-Express(PCI-X)总线、串行高级技术附件(SATA)总线、视频电子标准协会局部(VLB)总线或其它合适的总线或者两个或更多个以上这些的组合。在合适的情况下,总线可包括一个或多个总线。尽管本发明实施例描述和示出了特定的总线,但本发明考虑任何合适的总线或互连。为便于表示,图中仅用一条粗线表示,但并不表示仅有一根总线或一种类型的总线。
实施例五
本实施例提供一种消息队列的处理***,其包括:
消息生产端,用于获取队列名、业务关联key和消息内容;根据业务关联key计算确定分区号;将队列名和分区号拼接成分区名;将分区名发送至消息队列,以触发消息队列根据分区名将消息内容写入消息队列;
消息队列设备,用于根据消息生产端发送的分区名,获取写分段索引的值;根据所述分区名从写分段索引的Hash中查询得到分段号,并且使用所述分区名和所述分段号拼接获得存储键名;根据所述存储键名写入消息内容,并确定所述消息内容对应的消息时间序号entryId;向所述消息队列的预设数据结构中写入标记,所述标记用于指示所述消息内容未在所述消息队列的主节点和从节点之间完成同步;其中,所述消息队列包括主节点和从节点;判断已写入的消息内容个数是否达到分段的预设长度;当已写入的消息内容个数达到分段的预设长度时,将所述写分段索引的值加1;获取消息队列的主节点复制偏移量;向所述消息生产端返回所述entryId和所述消息队列的主节点复制偏移量。
上述实施例阐明的***、装置、模块或单元,具体可以由计算机芯片或实体实现,或者由具有某种功能的产品来实现。一种典型的实现设备为计算机。具体的,计算机例如可以为个人计算机、膝上型计算机、车载人机交互设备、蜂窝电话、相机电话、智能电话、个人数字助理、媒体播放器、导航设备、电子邮件设备、游戏控制台、平板计算机、可穿戴设备或者这些设备中的任何设备的组合。
虽然本申请提供了如实施例或流程图所述的方法操作步骤,但基于常规或者无创造性的手段可以包括更多或者更少的操作步骤。实施例中列举的步骤顺序仅仅为众多步骤执行顺序中的一种方式,不代表唯一的执行顺序。在实际中的装置或终端产品执行时,可以按照实施例或者附图所示的方法顺序执行或者并行执行(例如并行处理器或者多线程处理的环境,甚至为分布式数据处理环境)。
本发明是参照根据本发明实施例的方法、设备(***)、和计算机程序产品的流程图和/或方框图来描述的。应理解可由计算机程序指令实现流程图和/或方框图中的每一流程和/或方框、以及流程图和/或方框图中的流程和/或方框的结合。可提供这些计算机程序指令到通用计算机、专用计算机、嵌入式处理机或其它可编程数据处理设备的处理器以产生一个机器,使得通过计算机或其它可编程数据处理设备的处理器执行的指令产生用于实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能的装置。
这些计算机程序指令也可装载到计算机或其它可编程数据处理设备上,使得在计算机或其它可编程设备上执行一系列操作步骤以产生计算机实现的处理,从而在计算机或其它可编程设备上执行的指令提供用于实现在流程图一个流程或多个流程和/或方框图一个方框或多个方框中指定的功能的步骤。
需要说明的是,在本文中,诸如第一和第二等之类的关系术语仅仅用来将一个实体或者操作与另一个实体或操作区分开来,而不一定要求或者暗示这些实体或操作之间存在任何这种实际的关系或者顺序。而且,术语“包括”、“包含”或者其任何其它变体意在涵盖非排他性的包含,从而使得包括一系列要素的过程、方法、物品或者设备不仅包括那些要素,而且还包括没有明确列出的其它要素,或者是还包括为这种过程、方法、物品或者设备所固有的要素。在没有更多限制的情况下,由语句“包括一个……”限定的要素,并不排除在包括所述要素的过程、方法、物品或者设备中还存在另外的相同要素。
本说明书中的各个实施例均采用相关的方式描述,各个实施例之间相同相似的部分互相参见即可,每个实施例重点说明的都是与其它实施例的不同之处。尤其,对于装置、电子设备及可读存储介质实施例而言,由于其基本相似于方法实施例,所以描述的比较简单,相关之处参见方法实施例的部分说明即可。
以上所述仅为本发明的较佳实施例而已,并非用于限定本发明的保护范围。凡在本发明的精神和原则之内所作的任何修改、等同替换、改进等,均包含在本发明的保护范围内。
Claims (16)
1.一种消息队列的处理方法,应用于消息队列设备,其特征在于,所述方法包括:
根据消息生产端发送的分区名,获取写分段索引的值;
根据所述分区名从写分段索引的Hash中查询得到分段号,并且使用所述分区名和所述分段号拼接获得存储键名;
根据所述存储键名写入消息内容,并确定所述消息内容对应的消息时间序号entryId;
向所述消息队列的预设数据结构中写入标记,所述标记用于指示所述消息内容未在所述消息队列的主节点和从节点之间完成同步;其中,所述消息队列包括主节点和从节点;
判断已写入的消息内容个数是否达到分段的预设长度;
当已写入的消息内容个数达到分段的预设长度时,将所述写分段索引的值加1;
获取消息队列的主节点复制偏移量;
向所述消息生产端返回所述entryId和所述消息队列的主节点复制偏移量。
2.根据权利要求1所述的方法,其特征在于,在所述的向所述消息生产端返回所述entryId和所述消息队列的主节点复制偏移量之后,还包括:
获取消息队列的运行信息;
根据所述消息队列的运行信息,判断是否发生了主节点故障切换;
如果发生了主节点故障切换,进一步根据从节点复制偏移量和主节点复制偏移量判断复制偏移量是否未同步;
如果复制偏移量未同步,则确定消息已丢失,向所述消息生产端返回写入失败信息;
如果未发生主节点故障切换,则确定主节点正常,依次获取每个待检查的从节点的复制偏移量,判断每个待检查的从节点的复制偏移量是否已经完成复制;
当任意待检查的从节点的复制偏移量已经完成复制时,向所述消息生产端返回写入成功信息。
3.根据权利要求2所述的方法,其特征在于,在所述的当任意待检查的从节点的复制偏移量已经完成复制时,向所述消息生产端返回成功信息之后,还包括:
拼接租约锁键名;
根据租约锁键名,判断租约锁是否存在;
如果所述租约锁不存在,则设置所述租约锁的值为客户端信息,并且设置所述租约锁的过期时间为指定时长;
如果所述租约锁存在,则进一步判断所述租约锁与客户端信息是否相同;
当所述租约锁与所述客户端信息相同时,更新所述租约锁的过期时间;
当所述租约锁与所述客户端信息不相同时,确定所述租约锁已被占用,输出获取所述租约锁失败的指示信息。
4.根据权利要求3所述的方法,其特征在于,还包括:
判断是否成功获取所述租约锁;
如果已成功获取所述租约锁,则获取读分段索引;
将分区名和分段号拼接成存储键名;
获取当前处理的偏移量;
向消费者端返回所述存储键名和所述当前处理的偏移量。
5.根据权利要求4所述的方法,其特征在于,在所述的返回所述键名和所述当前处理的偏移量之后,还包括:
检查租约锁的值是否与客户端信息相同,判断所述租约锁是否被其它客户端获取;
当所述租约锁未被其它客户端获取时,更新偏移量的值;
当所述租约锁已被其它客户端获取时,输出异常。
6.根据权利要求1所述的方法,其特征在于,所述消息队列包括:远程字典服务Redis;所述的根据所述存储键名写入消息内容,并确定所述消息内容对应的消息时间序号entryId,具体包括:
根据所述存储键名将所述消息内容包含的消息体写入到Redis的Stream数据结构中;其中,所述消息体包括多个键值对;
接收返回的与所述消息体对应的消息时间序号entryId;其中,所述entryId包括时间戳和序号。
7.根据权利要求2所述的方法,其特征在于,所述的根据从节点复制偏移量和主节点复制偏移量判断复制偏移量是否未同步,具体包括:
当从节点复制偏移量大于或者等于主节点复制偏移量时,确定复制偏移量同步。
8.根据权利要求5所述的方法,其特征在于,所述消息队列包括:Redis;所述的检查租约锁的值是否与客户端信息相同,判断租约锁是否被其它客户端获取,具体包括:
使用Redis的第一指令获取租约锁的值;其中,所述第一指令是get命令;
当未取得租约锁的值时,使用Redis的第二指令将租约锁的值设置为目标客户端信息;其中,所述第二指令是set命令;
当取得租约锁的值时,将取得的租约锁的值与传入的目标客户端信息进行比较;
当取得的租约锁的值与传入的目标客户端信息不同时,则判定租约锁已被其它客户端获取;
当取得的租约锁的值与传入的目标客户端信息相同时,则判定租约锁未被其它客户端获取,并且通过Redis的第三指令对所述租约锁进行续约,续约时长为指定时长;其中,所述第三指令是expire命令。
9.根据权利要求3、4、5或者8所述的方法,其特征在于,还包括:
在封锁交易时,使用特定关键字写入租约锁,使得消费者端在获取租约锁时失败;
在恢复交易时,为每个租约锁设置随机过期时间。
10.一种消息队列的处理方法,应用于消息生产端,其特征在于,所述方法包括:
获取队列名、业务关联键名key和消息内容;
根据所述业务关联key计算确定分区号;
将所述队列名和所述分区号拼接成分区名;
将所述分区名发送至消息队列,以触发所述消息队列根据所述分区名将所述消息内容写入所述消息队列。
11.根据权利要求10所述的方法,其特征在于,将所述分区名发送至消息队列,以触发所述消息队列根据所述分区名将所述消息内容写入所述消息队列之后,还包括:
判断所述消息内容是否已经写入至所述消息队列;
如果所述消息内容已经写入至所述消息队列,则返回写入成功的信息;
如果所述消息内容没有写入至所述消息队列,则进一步判断所述消息内容是否丢失;
如果所述消息内容已丢失,则抛出异常;
如果所述消息内容未丢失,则进一步判断将所述消息内容写入所述消息队列的写入时长是否超出了预设的时间阈值;
如果所述写入时长超出了预设的时间阈值,则输出所述消息内容写入失败。
12.一种计算机可读存储介质,其上存储有计算机程序,其特征在于,该程序被处理器执行时实现如权利要求1-11中任意一项所述的消息队列的处理方法。
13.一种计算机设备,其特征在于,其包括:
一个或多个处理器;
存储装置,用于存储一个或多个程序;
当所述一个或多个程序被所述一个或多个处理器执行时,使得所述一个或多个处理器实现如权利要求1-11中任一所述的一种消息队列的处理方法。
14.一种消息队列的处理***,其特征在于,包括:
消息生产端,用于获取队列名、业务关联key和消息内容;根据所述业务关联key计算确定分区号;将所述队列名和所述分区号拼接成分区名;将所述分区名发送至消息队列,以触发所述消息队列根据所述分区名将所述消息内容写入所述消息队列;
消息队列设备,用于根据消息生产端发送的分区名,获取写分段索引的值;根据所述分区名从写分段索引的Hash中查询得到分段号,并且使用所述分区名和所述分段号拼接获得存储键名;根据所述存储键名写入消息内容,并确定所述消息内容对应的消息时间序号entryId;向所述消息队列的预设数据结构中写入标记,所述标记用于指示所述消息内容未在所述消息队列的主节点和从节点之间完成同步;其中,所述消息队列包括主节点和从节点;判断已写入的消息内容个数是否达到分段的预设长度;当已写入的消息内容个数达到分段的预设长度时,将所述写分段索引的值加1;获取消息队列的主节点复制偏移量;向所述消息生产端返回所述entryId和所述消息队列的主节点复制偏移量。
15.一种消息队列的处理装置,设置于消息队列设备中,其特征在于,所述处理装置包括:
写索引获取模块,用于根据消息生产端发送的分区名,获取写分段索引的值;
存储键名拼接模块,用于根据所述分区名从写分段索引的Hash中查询得到分段号,并且使用所述分区名和所述分段号拼接获得存储键名;
消息写入模块,用于根据所述存储键名写入消息内容,并确定所述消息内容对应的消息时间序号entryId;
标记写入模块,用于向所述消息队列的预设数据结构中写入标记,所述标记用于指示所述消息内容未在所述消息队列的主节点和从节点之间完成同步;其中,所述消息队列包括主节点和从节点;
消息数量判断模块,用于判断已写入的消息内容个数是否达到分段的预设长度;
写索引更新模块,用于当已写入的消息内容个数达到分段的预设长度时,将所述写分段索引的值加1;
复制偏移量获取模块,用于获取消息队列的主节点复制偏移量;
写入反馈模块,用于向所述消息生产端返回所述entryId和所述消息队列的主节点复制偏移量。
16.一种消息队列的处理装置,设置于消息生产端,其特征在于,所述处理装置包括:
信息获取模块,用于获取队列名、业务关联key和消息内容;
分区号计算模块,用于根据所述业务关联key计算确定分区号;
分区名拼接模块,用于将所述队列名和所述分区号拼接成分区名;
写入触发模块,用于将所述分区名发送至消息队列,以触发所述消息队列根据所述分区名将所述消息内容写入所述消息队列。
Priority Applications (1)
Application Number | Priority Date | Filing Date | Title |
---|---|---|---|
CN202111065794.6A CN113505012B (zh) | 2021-09-13 | 2021-09-13 | 一种消息队列的处理方法、介质、设备和*** |
Applications Claiming Priority (1)
Application Number | Priority Date | Filing Date | Title |
---|---|---|---|
CN202111065794.6A CN113505012B (zh) | 2021-09-13 | 2021-09-13 | 一种消息队列的处理方法、介质、设备和*** |
Publications (2)
Publication Number | Publication Date |
---|---|
CN113505012A true CN113505012A (zh) | 2021-10-15 |
CN113505012B CN113505012B (zh) | 2021-11-19 |
Family
ID=78017160
Family Applications (1)
Application Number | Title | Priority Date | Filing Date |
---|---|---|---|
CN202111065794.6A Active CN113505012B (zh) | 2021-09-13 | 2021-09-13 | 一种消息队列的处理方法、介质、设备和*** |
Country Status (1)
Country | Link |
---|---|
CN (1) | CN113505012B (zh) |
Cited By (4)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN114172821A (zh) * | 2022-02-08 | 2022-03-11 | 树根互联股份有限公司 | 服务状态的同步方法、装置及服务器 |
CN114253747A (zh) * | 2021-12-27 | 2022-03-29 | 北京宇信科技集团股份有限公司 | 一种分布式消息管理***和方法 |
CN115576714A (zh) * | 2022-10-19 | 2023-01-06 | 深圳市中兴新云服务有限公司 | 基于mq框架确保消息队列消费顺序准确性的方法 |
CN116304390A (zh) * | 2023-04-13 | 2023-06-23 | 北京基调网络股份有限公司 | 时序数据处理方法、装置、存储介质及电子设备 |
Citations (4)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
US20150195385A1 (en) * | 2014-01-08 | 2015-07-09 | Cavium, Inc. | Methods and systems for distribution of packets among parsing clusters |
CN107479829A (zh) * | 2017-08-03 | 2017-12-15 | 杭州铭师堂教育科技发展有限公司 | 一种基于消息队列的Redis集群海量数据快速清理***及方法 |
CN110012050A (zh) * | 2018-12-04 | 2019-07-12 | 阿里巴巴集团控股有限公司 | 消息处理、存储方法、装置及*** |
CN111818112A (zh) * | 2019-04-11 | 2020-10-23 | ***通信集团四川有限公司 | 一种基于Kafka***的发送消息的方法和装置 |
-
2021
- 2021-09-13 CN CN202111065794.6A patent/CN113505012B/zh active Active
Patent Citations (4)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
US20150195385A1 (en) * | 2014-01-08 | 2015-07-09 | Cavium, Inc. | Methods and systems for distribution of packets among parsing clusters |
CN107479829A (zh) * | 2017-08-03 | 2017-12-15 | 杭州铭师堂教育科技发展有限公司 | 一种基于消息队列的Redis集群海量数据快速清理***及方法 |
CN110012050A (zh) * | 2018-12-04 | 2019-07-12 | 阿里巴巴集团控股有限公司 | 消息处理、存储方法、装置及*** |
CN111818112A (zh) * | 2019-04-11 | 2020-10-23 | ***通信集团四川有限公司 | 一种基于Kafka***的发送消息的方法和装置 |
Cited By (7)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN114253747A (zh) * | 2021-12-27 | 2022-03-29 | 北京宇信科技集团股份有限公司 | 一种分布式消息管理***和方法 |
CN114253747B (zh) * | 2021-12-27 | 2023-04-28 | 北京宇信科技集团股份有限公司 | 一种分布式消息管理***和方法 |
CN114172821A (zh) * | 2022-02-08 | 2022-03-11 | 树根互联股份有限公司 | 服务状态的同步方法、装置及服务器 |
CN114172821B (zh) * | 2022-02-08 | 2022-05-24 | 树根互联股份有限公司 | 服务状态的同步方法、装置及服务器 |
CN115576714A (zh) * | 2022-10-19 | 2023-01-06 | 深圳市中兴新云服务有限公司 | 基于mq框架确保消息队列消费顺序准确性的方法 |
CN116304390A (zh) * | 2023-04-13 | 2023-06-23 | 北京基调网络股份有限公司 | 时序数据处理方法、装置、存储介质及电子设备 |
CN116304390B (zh) * | 2023-04-13 | 2024-02-13 | 北京基调网络股份有限公司 | 时序数据处理方法、装置、存储介质及电子设备 |
Also Published As
Publication number | Publication date |
---|---|
CN113505012B (zh) | 2021-11-19 |
Similar Documents
Publication | Publication Date | Title |
---|---|---|
CN113505012B (zh) | 一种消息队列的处理方法、介质、设备和*** | |
CN109739935B (zh) | 数据读取方法、装置、电子设备以及存储介质 | |
US9779128B2 (en) | System and method for massively parallel processing database | |
US5530802A (en) | Input sequence reordering method for software failure recovery | |
EP0772136A2 (en) | Method of commitment in a distributed database transaction | |
EP0351969A2 (en) | Exactly-once semantics in a TP queuing system | |
JPH02287858A (ja) | 分散処理システムのリスタート方式 | |
US20100333094A1 (en) | Job-processing nodes synchronizing job databases | |
CN110677280B (zh) | 服务节点切换方法、装置、设备及计算机可读存储介质 | |
US11436110B2 (en) | Distributed database remote backup | |
CN110895488B (zh) | 任务调度方法及装置 | |
CN113760513B (zh) | 一种分布式任务调度方法、装置、设备和介质 | |
US6842763B2 (en) | Method and apparatus for improving message availability in a subsystem which supports shared message queues | |
CN112486707A (zh) | 基于Redis的消息异步消费方法及装置 | |
CN113112344B (zh) | 业务处理方法、设备、存储介质及计算机程序产品 | |
CN113946427A (zh) | 用于多操作***的任务处理方法、处理器及存储介质 | |
CN110196788B (zh) | 一种数据读取方法、装置、***及存储介质 | |
CN112148436B (zh) | 去中心化的tcc事务管理方法、装置、设备及*** | |
US9870402B2 (en) | Distributed storage device, storage node, data providing method, and medium | |
EP3377970B1 (en) | Multi-version removal manager | |
US20210248157A1 (en) | System and method for a distributed database | |
US20080002743A1 (en) | System and method for synchronizing in-memory caches while being updated by a high rate data stream | |
Pankowski | Consistency and availability of Data in replicated NoSQL databases | |
CN114265900A (zh) | 一种数据处理方法、装置、电子设备及存储介质 | |
CN113032477B (zh) | 基于gtid的长距离数据同步方法、装置及计算设备 |
Legal Events
Date | Code | Title | Description |
---|---|---|---|
PB01 | Publication | ||
PB01 | Publication | ||
SE01 | Entry into force of request for substantive examination | ||
SE01 | Entry into force of request for substantive examination | ||
GR01 | Patent grant | ||
GR01 | Patent grant |