美文网首页
消息中间件实践-基于IPC之共享内存封装的消息队列

消息中间件实践-基于IPC之共享内存封装的消息队列

作者: wangfeng1983 | 来源:发表于2018-12-12 16:20 被阅读0次

    消息队列早期通常是在实时类业务支持场景中必要的应用进程通信的手段之一,本文结合曾经参与过的实时计费系统来概括下曾经在基于IPC的共享内存通信方式基础上封装的消息队列实践。作为现如今互联网重要利器之一的消息中间件,早期基于本机的共享内存的消息队列通信能为今天理解中间件提供一些基础。(消息队列强调的是队列本身的构造,即使在消息中间件的今天,其实也是队列存储模型+生产、消费者网络通信模块实现构成了的中间件,核心还是那个队列存储模型)

    在电信行业以运营通信业务为主,通信业务由一个个网络域的网元为客户的手机终端提供服务。最早期,由于技术应用的限制,这些服务的计费计价是基于采集网元上面的文件话单来进行离线或准实时计费处理的。(这里一定要介绍下业务场景,任何脱离业务场景来谈技术应用的多少会有些抽象)业务场景如下。

    离线计算场景

    离线计费的场景中大概的流程如下:

    1)手机用户使用业务,比如语音业务,那么他使用业务通话结束或者是长时间使用通话,交换机会按照时间点切割话单,生成语音相关的消费记录,称为CDR(Call Detail Record)。用户的CDR会一条条的记录存放在文件中,文件以一定的规则命名,比如业务编码+时间+批次。

    2)核心网络域产生了用户的使用记录之后,这些存放着CDR的文件都放在各个网络设备的本地服务器上面。那么需要通过应用系统中的一个采集程序连上到网络设备按照间隔的时间配置去采集这些文件。(这些文件按照不同厂家的不同的文件格式存放)

    3)一旦采集应用将这些包含CDR的文件采集到本地之后,采集会进行统一的解码处理,即将各类厂家的网络设备的文件统一解码为内部标准处理的格式。

    4)之后就将这些解码的文件源源不断的往后续预处理模块传递,预处理模块进行必要的处理加工之后,会经过排重模块,将这些文件中的重复记录排除掉。

    5)最终这些CDR记录的文件会经过核心批价模块,批价模块则负责按照各种计价规则来计算用户产生的消费记录。最终形成扣减账户的费用的账单,然后这些计价后的详细话单和账单会通过入库模块统一入库,供外部查询。

    上述整个应用过程是一个离线式的计算过程,模块与模块之间通过批量文件传递来完成数据的通信。而并发处理则通过应用框架支持的多进程、多线程并发处理模式本地化处理。

    离线计费中的技术集成思路总结如下:

    1)模块之间文件作为主要的集成手段;

    2)模块部署支持多主机集群部署,主机内部本地文件系统通信,主机之间ftp程序作为传输手段;

    随着业务的变化,客户体验的提升,那么这种离线式的计费模式逐渐受到挑战,尤其是由于滞后式的计费带来的恶意欠费坏账等。加上3,4G业务要求的各类控制计费能力,这套架构开始向实时计费演进。

    在线计费的场景中大概的流程如下:

    在线计算场景

    实时意味着使用通信业务的同时,需要通过实时消息上报请求和控制应答的模式来完成业务的实时计费控制处理。此时业务模块之间的通信集成手段变化为消息,大致的业务场景如下:

    1)用户使用语音等业务,会经过网元设备上报用户使用业务请求;

    2)经过智能网管适配多种设备通信的协议接入,通过解码组件将请求转换为内部统一处理的消息格式;

    3)后续预处理模块在请求标准消息上进一步加工处理,为后续计费控制模块提供必要元素;

    4)计费控制模块负责请求的会话处理,基于全内存的数据库来维持业务请求的session,作出授权使用业务反算的分片,并应答回网元进行控制;

    5)一旦网元接收到应用系统应答请求,如果为业务分片授权,比如语音3分钟通话允许,则开始接续语音业务;

    6)如果接受应答请求因余额不足拒绝提供服务,则拒绝用户业务使用。

    当然整个业务流程中还有很多关于超时,数据一致性等诸多方面的解决方案,这里不一一说明,只是举例一个实时消息场景。

    在线计费中的技术集成思路总结如下:

    1)为了提升实时性,整个业务交互过程全消息化,同时制定使用该消息的业务协议;

    2)集群模式下,应用系统中多个模块进程本地通信采用基于共享内存的“消息队列”,跨主机之间采用统一的网络通信组件完成跨节点传输消息。

    基于共享内存队列集成

    3)处理上为了提升实时性,全基于内存的数据库操作,存放计算中间结果和最终结果。

    4)只有最终产生的详单等数据存放物理数据库供外部查询使用。

    至此,业务场景终于解释完了,开始进入主题基于共享内存的消息队列应用。

    在Unix/Linux系统上,进程之间的通信手段很多,消息队列、共享内存、信号量等等。在实际的模块之间数据实时传递上,使用更多的还是基于共享内存封装的消息队列。

    在Unix/Linux系统上共享内存被认为是最快的IPC通信手段,因此在实际的进程本地通信中采用的是基于共享内存这种存储介质的队列封装。整体队列封装组件的结构如下:

    整体队列结构

    基于共享内存封装的消息队列,最主要的实现是存储结构的封装定义。基于共享内存,定义的基础结构如下。

    1)首先会根据预估,开辟一块测算出来的业务量级大小的共享内存区域;

    2)该区域中最核心的存储结构为块存储的定义,块0,块1…..块n代表了队列的不同通道,可以根据业务需要自行配置创建,比如块0为语音业务队列,块1为短信业务队列,块2为优先级高的队列等。并且也可以根据配置确定块是正向的队列,还是反向的队列。

    3)块存储区域最前端记录了整个队列的基本信息,包括包含的块数、每块存放的记录数、记录的大小、以及队列的状态等信息。

    4)其中块的数据结构定义如下:

    第一种顺序队列结构:

    顺序队列

    对于每个块队列的数据结构定义主要包括访问进程的pid,以及操作的线程tid的存放,用于查询定位使用者。另外块头上的锁标记,用于多线程访问的轻量级加锁操作。

    最主要的块数据库的数据结构定义:

    主要为FIFO队列结构构成,由块队列头、队列尾构成滑动队列结构;

    当队列头==队列尾:队列为空

    当队列尾==-1时:队列为满

    第二种乱序的存储队列结构:

    乱序队列

    数据块区结构定义分为两个部分:

    1)数据区,主要存放进入队列的一个个消息请求,Data=Item[1],Item[2],…,Item[N]

    2)索引区,索引区中包括已使用的数据位置,以及未使用的数据位置的索引表。

    每个索引结构为:

    Index = MaxUse,pos[1],pos[2],,…,pos[N] (MaxUse=pos[0])

    该种结构为N个数据,2个索引表,每个索引表1+N个元素,索引表的第0个元素表示从第1个元素开始已经使用了多少元素(元素是连续的)

    两种缓冲结构上消息队列Queue的封装定义:

        根据上述两种数据缓冲结构的数据结构定义,按照不同的场景,封装出多通道的消息Queue。

        比如双工通信队列,那么就由两个QueueBlock在最外层封装的Queue中定义构建。

        比如双工通信队列基础上支持高优先级队列定义,那么就由3个奇数的QueueBlock在最外层封装的Queue中定义构建。

    上述基于内存缓冲区灵活定义组织进程之间通信消息队列方式同时会带来一系列的其它方面的设计考虑(同样,这些问题在独立的消息中间件中同时存在,这里抛个砖),这些方面主要包括:

    1)生产和消费端由于处理速度不对等,那么导致的内存队列溢出如何保护?同时考虑共享内存的持久化处理能力。(因为共享内存虽然不会随着进程的销毁而销毁,但是主机存在的故障会导致内存的故障,虽然早期小型机出现故障的概率相对较低,后续的x86化带来的这方面问题尤其的严峻)

    2)基于IPC的共享内存消息队列方式,如何能够动态的调整队列大小,根据不同的场景需要?

    带着这些问题,我们在后续整理的消息中间件上面,来看看后续的技术演进是如何来解决的!!!

    相关文章

      网友评论

          本文标题:消息中间件实践-基于IPC之共享内存封装的消息队列

          本文链接:https://www.haomeiwen.com/subject/zykphqtx.html