具体实施方式
在下面的描述中阐述了很多具体细节以便于充分理解本申请。但是本申请能够以很多不同于在此描述的其它方式来实施,本领域技术人员可以在不违背本申请内涵的情况下做类似推广,因此本申请不受下面公开的具体实施的限制。
在本申请一个或多个实施例中使用的术语是仅仅出于描述特定实施例的目的,而非旨在限制本申请一个或多个实施例。在本申请一个或多个实施例和所附权利要求书中所使用的单数形式的“一种”、“所述”和“该”也旨在包括多数形式,除非上下文清楚地表示其他含义。还应当理解,本申请一个或多个实施例中使用的术语“和/或”是指并包含一个或多个相关联的列出项目的任何或所有可能组合。
应当理解,尽管在本申请一个或多个实施例中可能采用术语第一、第二等来描述各种信息,但这些信息不应限于这些术语。这些术语仅用来将同一类型的信息彼此区分开。例如,在不脱离本申请一个或多个实施例范围的情况下,第一也可以被称为第二,类似地,第二也可以被称为第一。
首先,对本发明一个或多个实施例涉及的名词术语进行解释。
读写锁:一种特殊的自旋锁,它把对共享资源的访问者划分成读者和写者,读者只对共享资源进行读访问,写者则需要对共享资源进行写操作。允许同时有多个读者来访问共享资源,最大可能的读者数为实际的逻辑CPU数。写者是排他性的,一个读写锁同时只能有一个写者或多个读者(与CPU数相关),但不能同时既有读者又有写者。
在本申请中,提供了一种消息处理方法。本申请同时涉及一种消息处理装置、一种计算设备,以及一种计算机可读存储介质,在下面的实施例中逐一进行详细说明。图1示出了根据本申请一实施例提供的一种消息处理方法的流程图,具体包括以下步骤:
S102:获取生产者的第一写入请求,基于所述第一写入请求在目标队列文件的开始位置建立文件标识符。
应用于消息队列,由消息队列程序接收生产者的写入请求,并执行后续任务步骤。
其中,生产者可以是数据收集器、传感器等具有消息存储需求的设备及装置。消息的存储位置为消息队列中的队列文件,生产者将消息信息写入的当前队列文件被称作为目标队列文件。文件标识符存在于队列文件的首部,在消息读取的情况下,消费者可以对文件标识符进行验证,以保证队列文件没有出现数据异常的情况。
具体的,在生产者进行消息写入的过程中,首先需要发送写入请求,消息队列程序接收到这个写入请求,写入进程开始,在消息队列中选定好写入消息的队列文件后,此时根据此消息队列以及写入消息的具体情况,在目标队列文件的首部建立文件标识符。
基于此,生产者向消息队列中写入消息的过程中,可以得到具有文件标识符的队列文件,在消费者消费消息队列中的消息时,消息队列程序接收消费请求后,首先需要读取处在队列文件首部的文件标识符,而后可以凭借文件标识符对队列文件进行验证,极大避免了队列文件读取过程中因为数据异常或是读取方式与存储方式不符而产生的无法读取现象。
进一步的,考虑到消费者在读取消息队列中的消息时,在队列文件的开始位置建立了包含文件版本信息与附属信息的文件标识符,在消息读取过程中可以对消息队列中的队列文件进行检验,减少因读取方式与队列版本不匹配而引起的消息读取异常,本实施例中,具体实现方式如下:
基于所述第一写入请求中携带的待执行消息读取队列文件版本字符与附属信息字符;根据所述队列文件版本字符与所述附属信息字符建立所述文件标识符;其中,所述队列版本字符用于标记当前文件的版本信息,所述附属信息字符用于标记消息队列中特有的队列文件。
具体的,读取队列文件版本字符可以得知当前队列文件的版本信息,读取附属信息字符可以得知当前队列文件在消息队列中是否是特殊队列文件。
基于此,文件的版本信息决定着消息在队列文件中的写入与读取方式,不同版本的队列文件之间的写入读取方式会受到版本信息的影响,随着版本关于读取或存储结构的优化,依靠旧有的消息读取方式就无法实现对消息队列中的消息进行读取,或者版本关于读取或存储结构的部分在优化后未变化,还可以进行消息的读取,因此考虑到版本对消息读取的影响,在创建文件标识符时,可以结合消息队列中队列消息的版本信息,若写入读取方式与版本不符,可能会出现写入读取的消息出现遗失或错误。附属信息字符中包含一个字符串,用于标记消息队列中的特有队列文件,使得读取消息队列中的消息更有针对性。
举例说明,在APP日志的处理场景中,获取用户的登录信息,APP需将用户的登录信息进行存储,此时APP将写入请求发送至用于存储的消息队列中,基于写入请求中携带的用户登录消息读取队列文件的版本字符为B_i,附属信息字符F_i,结合版本字符B_i和属信息字符F_i创建16位的文本标识符W_16,以用于后续起到的标识作用。
综上,基于此,使用队列文件版本字符对队列文件的版本进行标记,不仅仅可以避免上述问题,同时也为队列版本的升级预留了空间。附属信息则使得读取消息队列中的消息过程更加精准。文件版本字符与附属信息字符创建文件标识符,可以考虑到版本和附属信息的特性,实现文件标识符具有唯一性。
进一步的,消息队列中关于多个生产者同时进行写入时,此时消息队列中的消息处于动态的过程,若不止一个生产者对消息队列中同一位置进行写入,消息队列中记录的消息因生产者写入的内容不同而出现错误的问题,所以可以在消息队列的写入读取接口处实现加锁,本实施例中,具体实现方式如下所述:
对消息队列的写入接口进行加锁处理;获取所述生产者通过加锁后的写入接口提交的所述写入请求;其中,若存在至少两个生产者通过加锁后的写入接口提交写入请求,则按每个写入请求的写入时间依次处理。
具体的,加锁是给读消息队列的消费者与写消息队列的生产者提供读写锁,读锁与写锁之间互斥,且二者不能同时进行;即在写入线程进行写入的过程中,读取线程无法对消息队列进行读取;写锁在消息队列中也是互斥的,消息队列中同一时刻只能有一个写锁;读锁之间则没有任何限制,多个读锁可以并发执行,读取消息队列。依据以上规则,消息队列中的消息进行写入与读取时,若是多个生产者对消息队列发送写入请求,即有多个写入线程请求写锁加锁,此时需要根据多个写入线程请求加锁的时间顺序,依次给予写锁。在一个写入线程完成写入后,释放写锁,其他写入线程在此时可以根据时间顺序请求加锁;多个写入读取线程同时存在,则按照读写顺序获取读锁与写锁,读写过程分开顺序进行,在写锁实现加锁时,读锁会被阻塞,实现只写不读;多个读取线程同时存在,同时获取读锁并发读取消息队列中的消息信息。
其中,写锁读锁之间排斥,实现了读取与写入之间的分离。生产者对消息队列写入消息的过程,消息队列中的消息是变化的,是动态的,此时同步进行读取操作,很容易出现读取出错,或是读取的消息在写入的最新消息之前,使读取的消息失去其实时性,通过读取写入的分离,很好的解决了这个问题。
写入过程中,消息队列中的消息是动态的,多个写入线程同步实施,很容易出现写入的消息出错,写锁的互斥,使得同一时间内只存在一个写入线程,写入变得更加清晰,减少错误产生。
读取消息队列中的消息,消息内容是静态的,是不会发生变化的,此时对于读取线程就没有如写入线程那样的要求,此时读锁的并发读取可以极大增加读取速度,提高工作效率。
综上,通过对消息队列的写入读出接口进行加锁,实现了同一时间内只有一个写入,写入的过程中不存在读出,减少了消息队列中消息的写入读出错误产生。
此外,在消息队列中写入消息的实际应用中,只有单个的消息写入情况较少,更多时候是多个消息都需要写入到消息队列之中,此时采用对多个消息顺序写入,即在当前写入请求执行完成后,下一个写入请求开始执行,本实施例中,具体实现方式如下:
若消息写入成功,则将写入消息的写入位置与写入长度返回给当前生产者;若写入失败,则将失败信息返回给当前生产者。
具体的,在消息写入成功时,将写入消息的位置与写入长度返回当前生产者,这可以帮助当前写入者下次写入时,能快速定位,提升写入速度,提高工作效率。消息写入失败时,将失败信息返回当前生产者,便于当前生产者分析写入失败的原因。无论在写入成功或失败的情况下,当前生产者都会接收到反馈信息,当前生产者在接受了反馈的信息后,立即释放写锁,结束写入流程后续的生产者继续写入,使得多个生产者写入过程更为流畅,不出现卡顿。
沿用上例,在APP日志的处理场景中,获取到用户的登录信息和用户的退出登录的信息,此时需要将这两个信息存入到消息队列中关于存储用户状态的消息位置,若将两个信息同时写入,则会出现用户状态不明朗的情况。通过对写入的加锁处理,按照用户登录和退出的时间先后进行排序,若用户登录在前,退出登录在后,那么用户登录消息写入的写入线程率先获得写锁,由于写锁的互斥性,不会同时出现两个写锁,所以用户登录信息的写入线程在消息队列中将信息写入。在写入完成后,将用户登录信息写入到消息队列的位置和写入长度返回,然后用户的退出登录信息开始写入,用户退出登录信息的写入线程也需要先获得写锁,获得写锁并将用户退出登录信息写入消息队列后,将用户退出登录信息写入到消息队列的位置和写入长度返回。以上方法将用户的登录与退出登录的信息写入消息队列,二者的写入时间错开,避免了数据的冲突。
综上,在消息队列中进行连续的消息写入时,采用在消息队列进行加锁的操作,可以使在消息队列中连续的两个写入线程不会同时进行写入,且写入线程与读取线程不会同时进行,保证了两个写入线程之间不会因同时写入而使写入的消息出限写入消息混乱的情况,也不会出现读取线程因为写入线程对消息队列中的消息进行改写而读取错误的情况。在写入线程结束工作后,给生产者返回反馈信息,后续的生产者继续写入,使得多个生产者写入过程更为流畅,不出现卡顿。
S104:根据所述第一写入请求中携带的待执行消息确定对应的消息字节,并根据所述消息字节的长度生成对应的长度字节。
具体的,在上述基于写入请求完成文件标识符的建立之后,在此基础上,为了能在不创建索引文件的前提下完成消息的写入,且在消息读取时可以满足消费者的读取需求,不会出现消息的错乱、拥塞问题,可以根据所述写入请求中携带的待执行消息确定对应的消息字节,并根据所述消息字节的长度生成对应的长度字节,以实现后续可以以此为基础进行消息的写入操作。
其中,待执行消息具体是指写入请求中,需要写入进消息队列的消息。消息字节具体是指根据待处理消息的信息,所生成的在消息队列中存储与读取的固定格式的消息字节。长度字节是记载消息字节占用字节长度的字节。
基于此,生产者可以使用预定格式将消息写入至消息队列,且生成的长度字节可以在确定消息队列中的每一条存储的消息所在范围,极大的减轻了消费者读取消息时的难度。
进一步的,为了能够成功将消息写入队列,且不需要维护索引文件,可以通过确定消息字节的方式实现后续的消息写入操作,本实施例中,具体实现方式如下所述:
确定所述待执行消息对应的消息头字符串以及消息内容数据字符串;将所述消息头字符串与消息内容数据字符串拼接组成所述待执行消息对应的消息字节。其中,所述消息头字符串包括:消息ID、消息类型、消息编码、消息生成时间、队列程序接收时间、校验码、消息内容数据字符串长度中的部分或全部。
具体的,消息头字符串包含着消息的属性信息,消息内容字符串则记载着消息的具体内容。消息头字符串与消息内容字符串共同组成了所述消息字节,消息头字符串可以包含着多种属性信息,这些属性信息在消息头字符串中不必都出现,这些属性信息会根据消息队列中存储的消息内容,或是消息队列的读取写入请求的不同需求进行随意的选取,一种或多种属性信息在消息头字符串中的出现不是固定的,以此适应不同环境下的消息读写需求。
基于此,根据待处理消息获取待处理信息的消息ID、消息类型、消息编码、消息生成时间、队列程序接收时间、校验码、消息内容数据字符串长度中的至少一种,形成消息头字符串,将消息头字符串与消息内容形成的消息内容字符串拼接形成消息字节。
举例说明,在APP日志的处理场景中,将用户的登录信息写入到消息队列,若在此写入过程中,若此消息头字符串需要记载用户登录信息的消息ID、消息类型、消息生成时间、队列程序接收时间、消息内容数据字符串长度,首先确定写入的用户登录信息的消息ID为ID_i,消息类型为S_i,消息生成时间Tb,队列程序接收时间Tr,消息内容数据字符串长度Lc_i,此时将ID_i、S_i、Tb、Tr、Lc_i顺序拼接形成用户登录信息的消息头字符串,再将此消息头字符串与记载着用户登录消息的消息内容字符串拼接,生成登录消息的消息字节。
实际应用中,由于所述消息头字符串对应不同的内容,因此可以基于不同的场景选择不同的组合方式作为消息头字符串,与消息内容字符串共同组成了所述消息字节,此消息字节切合不同的场景,可以延展出众多的不同的读取与写入时的功能,所述消息头字符串的选择可以根据实际应用场景确定,本实施例在此不作任何限定。
综上,上述形式存储的消息字节,其头部包含了消息内容的大量信息,在消费者对消息进行读取的过程中,节省了许多计算与查找工序,极大的提升了工作效率,尤其消费者可以并行读取的情况下,多个读取进同时进行,此时也节约了算力成本。
S106:在所述文件标识符对应的标识字节之后,顺序拼接所述长度字节和所述消息字节作为所述第一写入请求的响应。
具体的,在上述过程中创建了消息字节和长度字节,而要将消息完整的写入消息队列中,还需要将二者顺序拼接,其中长度字节标识了消息字节的长度,所以长度字节应拼接在消息字节的前面,在消息读取的过程中可以首先确定消息字节的范围,以免读取错误。
进一步的,为了保证消息队列中消息的连续写入,在当前的写入进程结束后,还需要继续执行后续写入线程对消息队列的写入,本实施例中,具体实现方式如下:
接收第二写入请求,其中所述第二写入请求为时序相邻且在第一写入请求之后的写入请求;获取所述第二写入请求中携带的待执行消息对应的第二长度字节与第二消息字节;判断所述第二长度字节与所述第二消息字节的字节长度加上当前文件中的写入消息长度,是否大于所述目标队列文件预设的长度阈值;若否,将所述第二长度字节和第二消息字节写入目标队列文件;若是,创建新建文件,在新建文件中写入所述第二写入请求中携带的待执行消息。
具体的,第一写入请求与第二写入请求为对应的两个写入请求,第一写入请求标识在前,第二写入请求在后,提出第二写入请求的生产者可以为之前的生产者,也可以为其他生产者。目标队列文件预设的长度阈值为目标队列文件长度的最大值,在写入消息的长度超过了这个值,会将当前写入的消息写入下一个队列文件之中。
基于此,第一次的写入请求处理完成后,顺序执行之后的写入任务。在接收到第二写入请求后,根据第二写入请求中携带的待执行消息,将待执行消息的属性信息提取,并将所需的属性信息组合成消息头字符串,将消息头字符串与消息内容进行拼接,拼接成消息字节,然后根据消息字节,计算出消息字节的长度,根据此长度生成固定格式的长度字节,然后按照长度字节在前,消息字节在后的顺序拼接。然后计算这拼接出的字符串的总长度,加上当前队列文件已经占用的字符长度,判读是否大于消息队列的长度阈值,若大于,则说明此待执行消息无法存储至当前队列文件,需要新建文件将其写入;若不大于,则说明此待执行消息可以存储至当前队列文件,可以在上一个写入的消息后顺序写入此待执行消息。
举例说明,在APP日志的处理场景中,将用户的登录信息和用户购买APP中会员功能的信息依次写入消息队列。在将用户的登录信息写入完成之后,将用户购买APP中会员功能的信息继续写入之前,需要判断消息队列中的当前队列文件的空间是否可以存入用户购买APP中会员功能的信息。此时将用户购买APP中会员功能的信息的消息字节和长度字节的总长度相加,再加上当前队列文件已经写入的长度,若是大于设定的队列文件的长度阈值100M,则新建文件进行存储,若没有超过100M,则在当前文件中存储。
综上,提供了一种连续的消息写入请求的处理方法,使消息连贯的写入到消息队列之中,设置的目标队列文件的长度阈值,使得消息写入队列文件的长度不是无限的,可以更加合理的分配写入消息队列的消息,使得消息队列中的队列文件的结构更加合理,消费者读取消息队列中的消息时可以更加的快捷有序。
进一步的,在对消息队列进行连续的写入操作时,将待写入消息逐条写入消息队列中的队列文件中,为避免队列文件长度过长,需对队列文件的存储空间进行限定,那么在连续写入消息的情况下,若当前队列文件的空间足以存储第二写入请求对应的消息字节和长度字节,将第二写入请求中携带的待执行消息写入当前队列文件,本实施例中,具体对第二写入请求的写入过程具体如下:
根据所述第二写入请求中携带的待执行消息确定对应的第二消息字节,并根据所述第二消息字节的长度生成对应的第二长度字节;在当前消息字节之后,顺序拼接所述第二长度字节和所述第二消息字节,作为所述第二写入请求的响应。
具体的,第二消息字节为根据第二写入请求中携带的待执行消息生成的消息字节,第二长度字节为第二消息字节的长度字节。在队列文件中的字符空间足以存放新写入消息时,将新写入的消息存入此队列文件之中,存储的具体方式与之前的消息存放方法相同,都需要确定消息内容的对应消息字节和消息字节的长度字节,并将二者顺序拼接写入。
基于此,在第一次的写入请求已经处理完成,在之后顺序进行后续的写入任务,在接收到第二写入请求后,根据写入请求中携带的待执行消息,将待写入消息的属性信息提取,并将所需的属性信息组合成消息头字符串,将消息头字符串与消息内容进行拼接,拼接成消息字节。然后根据消息字节,计算出消息字节的长度,将此长度存入固定格式的长度字节内,然后按照长度字节在前,消息字节在后的顺序拼接,然后计算这拼接出的字符串的总长度,加上当前队列文件已经占用的字符长度,判读小于消息队列的长度阈值,将上述长度字节与消息字节拼接出的字符串写入当前队列文件。最后,将写入消息的地址信息返回生产者。
举例说明,在APP日志的处理场景中,用户的登录信息存储进消息队列成功,再将用户购买APP中会员功能的信息写入消息队列,开始需要确定用户购买APP中会员功能的信息的消息字节M_i以及记录此消息字节的长度字节Ls_i,若M_i以及Ls_i的总长度再加上队列文件中已经占用的字节长度相加为99M,此总长度小于队列文件预设的阈值长度100M,则用户购买APP中会员功能的信息足够存入当前队列文件,接下来将用户购买APP中会员功能的信息的消息字节M_i与长度字节Ls_i顺序拼接进消息队列。
与之相对的,如上述的对消息队列进行连续的写入操作情况,若当前队列文件的空间不足以存储第二写入请求对应的消息字节和长度字节,本实施例中,具体对第二写入请求的写入过程具体如下:
创建新建文件,基于所述第二写入请求在所述新建文件的开始位置建立第二文件标识符;根据所述第二写入请求中携带的待执行消息确定对应的第二消息字节,并根据所述第二消息字节的长度生成对应的第二长度字节;在第二文件标识符之后,顺序拼接第二长度字节和第二消息字节,作为所述第二写入请求的响应。
具体的,在队列文件中的字符空间不足以存放新写入的消息时,需要先新建一个队列文件,在新建的队列文件开头,依旧需要建立文件标识符,用以标识队列文件的版本等信息,后续将新写入消息存入新建消息队列的过程与上述实施例中描述内容相似,本实施在此不再赘述。
基于此,在第一次的写入请求已经处理完成,在之后顺序进行后续的写入任务,在接收到第二写入请求后,根据写入请求中携带的待执行消息,将待写入消息的属性信息提取,并将所需的属性信息组合成消息头字符串,将消息头字符串与消息内容进行拼接,拼接成消息字节,然后根据消息字节,计算出消息字节的长度,将此长度存入固定格式的长度字节内。然后按照长度字节在前,消息字节在后的顺序拼接,然后计算这拼接出的字符串的总长度,加上当前队列文件已经占用的字符长度,判断大于消息队列的长度阈值,创建新的队列文件,按预定命名规则对新建队列文件命名,在新建队列文件的首部按预定规则建立文件标识符,随后将上述长度字节与消息字节拼接出的字符串在文件标识符之后写入新建队列文件。最后,将写入消息的地址信息返回生产者。
举例说明,在APP日志的处理场景中,用户的登录信息存储进消息队列成功,再将用户购买APP中会员功能的信息写入消息队列,开始需要确定用户购买APP中会员功能的信息的消息字节M_i以及记录此消息字节的长度字节Ls_i,若M_i以及Ls_i的总长度再加上队列文件中已经占用的字节长度相加为101M,此总长度大于队列文件预设的阈值长度100M,则用户购买APP中会员功能的信息不足以存入当前队列文件。需新建队列文件,在新建队列文件的首部建立16位的文件标识符W_16,接下来将用户购买APP中会员功能的信息的消息字节M_i与长度字节Ls_i顺序拼接进消息队列。
综上,通过对消息队列中当前队列文件的剩余存储空间计算,以及与后续写入进程写入的消息长度进行对比,满足了队列文件的存储不超过预设的阈值长度,保证了队列文件不会出现冗长情况,避免了因队列文件冗长导致的写入读取进程缓慢,系统运行不流畅的情况。
进一步的,在创建新建文件的时候,各个文件之间没有明显的区分,这样会使数据读取过程中,对于队列文件的读取只能顺序的一一分辨,这极大的减慢了系统的运行时间,基于此需要给各个队列文件予以命名,本实施例中,具体实现方式如下:
获取所述目标队列文件的目标文件名与所述目标队列文件的目标文件长度;将所述目标文件名与所述目标队列长度相加的结果,作为所述新建文件的文件名。
其中,目标队列文件长度为目标队列文件在消息队列中所占的字节数量。
基于此,由上述新建文件的命名过程可知,每个队列文件在消息队列中的起始位置,都是此队列文件的文件名加1,而每个队列文件在消息队列中的终止位置,都是下一个队列文件的文件名,通过两个相邻队列文件的文件名可以很方便的得到队列文件在消息队列中所占据字符的范围,可以方便的查找队列文件的位置。其中,对首个队列文件的命名,是对方案的一个补充。例如预设的目标字符可以为0,以确保每一个队列文件的命名不脱离以上的设计思路。具体的命名形式请见说明书附图图2,其中首个队列文件的被命名为0.pqdata,首个队列文件占用的字节长度为104857600,那么紧邻首个队列文件的第二个队列文件被命名为104857600.pqdata,后续文件的命名以此类推。
举例说明,在APP日志的处理场景中,用户的登录信息存储进消息队列成功,再将用户购买APP中会员功能的信息写入消息队列。若用户购买APP中会员功能的信息不足以存入当前队列文件假设当前的队列文件已占用的字节长度为104857600,在新建队列文件以存储用户购买APP中会员功能的信息时,新建文件的文件名为上一个队列文件的文件名0加上上一个队列文件的长度104857600,再加上后缀名.pqdata,可以得到新建文件的文件名104857600.pqdata。
综上,通过队列文件的文件名便可以确定队列文件在消息队列中的位置,再结合消息在队列文件中的位置,就可以精确的定位消息在消息队列中的位置。以此来查找消息位置,不需要单独的位置信息文件的指引,也不需要多余的索引文件进行查找。使消息的查找过程变得简洁,极大的提高了系统的运行速率,减少了不必要内存的占用。
进一步的,在对文件进行命名的过程中,由于新建文件都是基于上一个文件的文件名和文件长度的信息进行命名的,那么在消息队列中的第一个队列文件由于没有上一个队列文件,导致其命名方法不适用于上述方式,在本实施例中,解决此问题的具体方式如下:
基于预设的目标字符和文件后缀名对所述目标队列文件进行命名。
具体的,消息队列中的首个队列文件,由于没有前一个队列文件,所以它的命名是特殊的,在这里,队列文件命名的目的就是标识队列文件在消息队列中的位置,那么消息队列中的首个队列文件,就可以命名为0或者1,命名为0可以表明此队列文件的首个字节之前,在消息队列中还有0个字节;命名为1可以表明此消息队列文件的首个字节在消息队列中所处的位置。
举例说明,在APP日志的处理场景中,用户的登录信息存储进消息队列,由于用户的登录信息为第一个存储进消息队列的信息,此时将队列文件命名为0.pqdata。
综上,在消息队列中的首个队列文件,可以通过预设的目标字符进行命名,解决了首个队列文件没有前一队列文件而无法命名的问题。
本申请提供的消息处理方法,通过接收生产者发送的写入请求,并基于所述写入请求的内容在队列文件的开始位置建立文件标识符,根据写入请求中携带的待处理消息的内容,确定消息字节和消息字节的长度字节,将长度字节和消息字节顺序存储到消息队列之中,一个待存储的消息信息以这种形式进行存储。在读取目标消息时结合消息队列中的队列文件的文件名,可以直接找到目标消息对应的队列文件,省去了索引文件,减轻了系统的内存压力,在消息的读取过程中,也能省去引用索引文件的过程,使消息读取的过程更加简洁与快速,为消息的读取、查询提供了良好环境。
本实施例还提供一种消息读取方法的实施例,具体描述如下:
图3示出了本申请一实施例提供的消息读取方法的流程图,如图3所示,消息读取方法的具体实现如下步骤:
S302:接收消费者的消息读取请求。
本实施例提供与上述实施例相对应的消息读取方法,用于在采用上述消息处理方法完成消息写入后,可以与写入相对应进行消息的读取,具体实现如下:
消息写入完成后,消息在消息队列中保存,以供后续的消费者消费,而在消费者读取消息队列中的消息时,需要依靠生产者写入消息队列时对文件的命名,建立的文件标识符等等进行读取,在读取消息队列中的消息时,消息队列程序接收到消费者发送消息读取请求,根据消息读取请求中携带的消息位置信息,确定待读取消息在消息队列中的具体位置,再根据此具体位置自消息队列中的读取待读取消息,将待读取消息的内容和待读取消息的位置信息返回消费者。
此方案应用于消息队列,由消息队列程序接收消费者的读取请求,并执行后续任务步骤。
其中,消费者是对消息队列中的消息进行消费的设备、程序或进程。
具体的,在消费者进行消费消息队列的过程中,首先需要发送消息读取请求,消息队列程序接收到这个消息读取请求,读取进程开始。在消息读取请求之中,消费者有着自己对消息队列中的消息读取的诉求,此诉求可以包含在消费者发出的消息读取请求之中。
需要说明的是,本实施例提供的消息读取方法与上述实施例提供的消息处理方法相对应,其中,与上述实施例中相同或相应的描述内容,均可参见上述实施例,本实施例在此不作赘述。
S304:基于所述消息读取请求确定消息位置信息。
具体的,在上述接收消费者的消息读取请求之后,消息队列程序可以根据消费者发出的消息读取请求中消费者的消息读取诉求,进行相应的消息读取,需要说明的是,消费者的最终目的是消费消息队列中的消息,那么消费者对于消息读取的诉求便可以表现在消息读取请求中的对于所需读取的消息的消息位置选定方面上。
其中,消息位置信息为消息读取过程中,可以指定待读取消息在消息队列中的位置的信息,消息队列文件在接收到消费者发出的消息读取请求,之后便可以基于此消息读取请求,获得消息位置信息。
基于此,消息队列程序在得到了消息位置信息后,便可以基于此在消息队列中查找待读取消息所在的队列文件,并最终确定待读取消息的具体位置,将其读取。
S306:在消息队列中确定所述消息位置信息对应的目标任务文件。
具体的,在上述通过消费者的消息读取请求确定消息位置信息后,消息队列程序可以根据消息位置信息,在消息队里中查找确认待读取消息所在的队列文件。
进一步的,消费者在消费消息队列的过程中,需要对消息队列中的某个存储有特定消息的整个队列文件中的消息队列文件进行消费时,消息队列程序可以对消息队列中的所有队列文件的文件标识符中的附属信息字符进行验证,或是根据所需队列文件的位置信息直接查找,在本实施例中,解决此问题的具体方式如下:
确定所述消息位置信息中包含的任务文件名;基于所述任务文件名在消息队列中确定所述目标任务文件。
具体的,消费者需要读取的某个完整队列文件,此时将待读取队列文件的文件名放入消息读取请求中,消息队列程序在接收到此消息读取请求后将根据其中携带的队列文件名直接在消息队列中确定所述目标任务文件。
举例说明,在APP日志的处理场景中,用户的登录信息存储进消息队列,在实时计算程序需要读取用户的登录信息时,实时计算程序发送消费请求,消息队列程序接受消费请求,消费请求中包含待读取消息的消息位置信息,其中,用户登录信息存放在消息队列中的第一个队列文件中,假设在本例中,第一个队列文件的文件名为0.pqdata,消费者将文件名0.pqdata放置在消息读取请求中,消息队列程序接收到此消息读取请求后,根据文件名0.pqdata将第一个队列文件确定为目标任务队列。
综上,此消费流程省去了调用索引文件的过程,减轻了系统的存储压力,加快了任务处理速度,提升了系统的工作效率。
此外,消费者在消费消息队列的过程中,发送消息读取请求时基于待读取消息队列中的字节范围时,消息队列程序在接收到此消息读取请求时,需要以消息读取请求中携带的字节范围确定待读取消息所在的目标任务文件,在本实施例中,解决此问题的具体方式如下:
确定所述消息位置信息中记载的所述目标消息在消息队列中的字节位置;调用消息队列程序逐一确定消息队列中的队列文件的队列文件名;根据所述队列文件名确定各队列文件占据消息队列的字节范围;基于所述字节位置与所述字节范围,确定所述目标消息所在的目标任务文件。
具体的,字节位置代表着待读取消息在消息队列中所占据的字节。
基于此,消息队列程序根据消息位置信息中记载的信息,确定待读取消息在消息队列中的字节位置,消息队列程序逐一对确定消息队列中的队列文件的文件名,因队列文件的文件名在命名时是后一个队列文件的文件名等于前一个队列文件的文件名加上前一个消息队列的队列文件的字节长度,那么前一个队列文件的文件名加1为前一个文件在消息队列中的起始字节位置,后一个队列文件的文件名为前一个队列文件的结束字节位置。通过前后两个队列文件的文件名就可以得到前一个队列文件在消息队列中占据的字节范围,通过这样的规律再结合待读取消息的字节位置,可以找到待读取消息所在的目标任务文件。
沿用上例,在APP日志的处理场景中,实时计算程序需要读取用户的某个登录信息,此时,消息位置信息可以为待读取消息的首字节在消息队列中的位置,也可以是消息待读取消息在消息队列中占据的字节范围等。假设在本例中,待读取消息的首字节在消息队列中的位置为消息位置信息,此位置信息为101,根据消息队列程序对消息队列中的队列文件的文件名进行查找,查到第一个队列文件的文件名为0,第二个队列文件的文件名为104857600,根据上述消息队列中的队列文件的命名方式可知,消息队列中的第一个队列文件在消息队列中占据的字节范围为1-104857600,位置信息101在1-104857600之内,此时可以知道待读取消息所在的队列文件为第一个队列文件,将第一个队列文件确定为目标任务队列。
综上,此消费流程省去了调用索引文件的过程,减轻了系统的存储压力,加快了任务处理速度,提升了系统的工作效率。
除此之外,消费者在消费消息队列进行实时计算的过程中,经常需要回溯到某个时间点来重新消费数据,达到重新计算数据的目的,所以本队列程序提供通过指定消息接收时间来获取最接近这个时间的一个消费位置,在本实施例中,具体实现如下:
确定所述消息位置信息中记载着所述目标消息的目标时间戳;消息队列程序逐一确定消息队列中的队列文件的创建时间;基于所述目标时间戳与各个队列文件的创建时间,确定所述目标消息所在的目标任务文件。
具体的,目标时间戳为消费者在利用时间回溯的方式消费消息队列过程中,所要读取的消费队列中的消息的写入时间信息;目标时间文件为对应所述目标时间戳的消息信息所在队列文件。
基于此,消费者利用时间回溯方式进行消息队列的消费时,首先发送消费请求,此消费请求中携带的目标时间戳记录了待读取消息的时间信息,消息队列程序在接收到消费请求后,遍历消息队列中的队列文件,获取消息队列中队列文件的各个时间信息,队列文件的时间信息为在此队列文件中完成最后一个消息信息写入的时间点,寻找到各队列文件中时间信息最接近目标时间戳且大于等于目标时间戳记录的时间信息的队列文件,此时可以确定待读取消息就被存储在此文件内。首先选择队列文件的时间信息最接近目标时间戳记载的时间信息的队列文件,将范围缩小到了两个或一个队列文件,一个队列文件的情况是待读取消息被存储在第一个消息队列之中,所有队列文件的时间信息都大于或大于等于目标时间戳中记载的时间信息,选取最接近的队列文件就直接选取出了第一个队列文件。
出现两个队列文件的情况,此时选出的队列文件的时间信息一个大于目标时间戳中记载的时间信息,另一个为小于,因为队列文件的时间信息为此文件中最后一个完成写入的消息信息的时间点,若目标时间戳中记载的时间信息对应的消息存在于一个队列文件中,那么队列文件中最后一个完成写入的消息信息的时间点应该大于等于存在于这个队列文件中的待读取消息信息的写入时间,也就得出了队列文件最后被写入的消息的时间点大于目标时间戳信息。
举例说明,在APP日志的处理场景中,用户的登录信息存储进消息队列,在APP运行一段时间后,用户使用APP中的某个功能,此功能需要用到用户历史登录信息中某个登录信息,此时需要使用时间回溯方式读取所需的用户登录信息。消息队列程序接收到消费者的消费请求后,获得消费请求中的目标时间戳中记载的时间信息T1,消息队列程序遍历消息队列中的队列文件,获取消息队列中队列文件的各个时间信息,队列文件中时间信息最接近目标时间戳且大于等于目标时间戳记录的时间信息的队列文件,此为目标时间文件。
综上,在消费者消费消息队列中的消息过程中,通过时间回溯的方式进行消费使消费方式更加多元化,能满足不同的消费环境,适应更多场景。
进一步的,在读取队列文件中的待读取消息之前,对于读取方式的选定也是必要的,需要保证读取方式与队列文件的版本相匹配,以保证在队列文件中读取到的消息是正确的,那么对队列文件的版本信息进行读取就是必要的,在本实施例中,具体实现方式如下:
获取所述目标队列文件的所述文件标识符;对所述文件标识符中的文件版本字符和附属信息字符进行验证;在验证通过的情况下,顺序执行后续消息读取任务;在验证未通过的情况下,结束进程,并将错误信息返回给消费者。
具体的,对文件标识符的检验中,包含了对文件版本信息的查询,根据文件版本字符的指示,可以清楚的了解队列文件的版本信息,方便确定对此版本文件的读取方式,避免读取方式与队列文件版本不一致产生的错误。
若验证成功,那么证明此队列文件符合读取条件,此时可以继续进行读取,若验证失败,那么说明队列文件出现未知问题,此时继续读取队列文件中的消息,已经不能保证消息读取的正确,所以立即结束进程,并将错误信息返回消费者,方便后期的检查与维护。
举例说明,在APP日志的处理场景中,用户的登录信息存储进消息队列,此时需要读取用户的登录信息。消息队列程序接收到消费者的消费请求后,根据消费请求中的消息位置信息以及队列文件的文件名确定了用户的登录信息存储的队列文件,在确定的队列文件中,自队列文件的首部顺序读取前16位,此16位为本队列文件的文件标识符,之后验证文件标识符是否正确,并根据文件标识符中的文件版本信息决定读取方式。
综上所述,通过接收生产者发送的写入请求,并基于所述写入请求的内容在队列文件的开始位置建立文件标识符,根据写入请求中携带的待处理消息的内容,确定消息字节和消息字节的长度字节,将长度字节和消息字节顺序存储到消息队列之中,一个待存储的消息信息以这种形式进行存储,省去了索引文件,减轻了系统的内存压力,使消息读取的过程更加简洁与快速,为消息的读取、查询提供了良好环境。
S308:响应于所述消息读取请求在所述目标任务文件中读取所述目标消息,并发送至消费者。
在读取消息队列中的消息时,消息队列程序确定了待读取消息所在的目标任务文件仅仅只是前置步骤,还需要在目标任务文件中确定待读取消息的具体位置,并将其读取后返回消费者。
进一步的,通过待读取消息的位置信息,可以确定待处理消息所在的队列文件以及在队列文件的具体位置,但是在读取待读取消息的时候,读取出的消息并不是一个位置信息所能表达的,待读取消息存储在队列文件的一段空间内,若要读取待读取消息,还需要对队列文件中的内容进行具体的甄别,本实施例中,具体实现方式如下:
在所述目标任务文件中读取所述目标消息的长度字节,并基于所述长度字节确定所述目标消息的目标长度信息;基于所述目标长度信息在所述目标任务文件中读取相应的字节序列组成所述目标消息。
在步骤S306中记述了两种判断待读取消息所在的目标任务文件的方法,在此基础上,也延伸出两种待读取消息在目标任务文件中具体位置的判断方法,具体方式如下:
若根据消息读取请求中的待读取消息的字节范围确定的待读取消息所在的目标任务文件,在读取目标任务文件的中的待读取消息时,也需要继续依据此字节范围信息在目标任务文件中的查询待读取消息的具体位置信息。
具体的,在验证完队列文件首部存在的文件标识符并根据队列文件的版本信息确定读取队列文件的方式后,在文件标识符之后顺序读取预定长度字节的字节长度的字节,以此确定当前消息在队列文件的所处范围,并根据此范围与待处理消息的字节范围进行对比,若不同,则证明此消息不是待处理消息,跳过此消息,重复以上验证过程,直到选取到待处理消息,将待处理消息读取,并将最新的位置信息返回消费者。
举例说明,在APP日志的处理场景中,用户的登录信息存储进消息队列,此时实时计算程序需要消费用户的登录信息。消息队列程序接收实时计算程序的消费请求,在确定了用户的登录信息存储的队列文件后,在此队列文件中,消息队列程序先读取队列文件的前16位,这16位为队列文件的文件标识符,用以确定队列文件的版本信息。根据文件标识符中的队列文件版本信息选定读取方式,确定与当前队列文件的版本想匹配的读取方式后,在16位的文件标识符后继续读取4位,这4位为队列文件中存储的第一个消息信息的长度字节,此长度字节确定消息字节所在范围,若此范围并不是待读取消息的地址,则在长度字节后跳过长度字节标定的字节长度,即将此消息的消息字节跳过,读取下一个消息,直到选中待读取消息,根据待读取消息长度字节确定待读取消息的消息字节范围,根据此范围读取消息字节并返回实时计算程序。
若根据消息读取请求中的待读取消息的目标时间戳确定的待读取消息所在的目标任务文件,在读取目标任务文件的中的待读取消息时,也需要继续依据此目标时间戳在目标任务文件中的查询待读取消息的具体位置信息。
具体的,在选出目标时间文件后,消息队列程序遍历目标时间文件中的存储的各消息,首先需要验证队列文件的文件标识符并根据文件标识符选定与队列文件版本相匹配的读取方式,之后读取第一个消息的长度字节,确定第一个消息的存储范围,确定范围后顺序读取消息字节。在消息字节的消息头字符串中确定消息生成时间,与目标时间戳中记录的时间信息进行对比,若相同则确定此消息为待读取消息;若不同则跳过此消息顺序读取后续消息,直到找到队列文件中与目标时间戳中记载的时间信息相同的消息信息。找到待读取消息后,读取待读取消息,将待读取消息的消息内容信息、消息位置信息、时间信息返回消费者。
举例说明,在APP日志的处理场景中,若需要使用时间回溯方式读取所需的用户登录信息。在确定的队列文件中,自队列文件的首部顺序读取前16位,此16位为本队列文件的文件标识符,验证文件标识符是否正确,并根据文件标识符中的文件版本信息决定读取方式。之后在16位的文件标识符后继续读取4位,这4位为队列文件中存储的第一个消息信息的长度字节,此长度字节确定消息字节所在范围。继续读取长度字节后的消息字节,在消息字节的首部是消息头字符串,若消息头字符串的前16位记载了此消息的消息生成时间,则读取此16位,并与目标时间戳记载的时间信息T1对比,确定此消息是否是待读取的用户登录信息,若不是则在长度字节后跳过长度字节标定的字节长度,即将此消息的消息字节跳过,读取下一个消息,直到选中待读取消息,根据待读取消息长度字节确定待读取消息的消息字节范围,根据此范围读取消息字节并返回实时计算程序,同时返回此消息的位置信息和时间信息。
综上,目标消息的长度字节显示了消息存储在队列文件中的具体字符范围,根据此范围就能精确的定位目标消息,减少了消息位置索引的流程,提高了索引的精度。
本申请提供的消息读取方法,通过接收消费者的消息读取请求,基于所述消息读取请求中的消息位置信息在消息队列中确定所述消息位置信息对应的目标任务文件,响应于所述消息读取请求在所述目标任务文件中读取目标消息,并发送至消费者,在读取目标消息时使用待读取消息在消息队列中的字节范围或使用时间回溯法,可以直接找到目标消息对应的队列文件,省去了索引文件,减轻了系统的内存压力,使消息读取的过程更加简洁与快速。
本实施例结合图4,以游戏数据收集器将接受到的游戏数据发送到消息队列中的整个过程为例,对所述消息处理方法进一步说明。图4是本申请一实施例提供的一种应用于数据存储场景的消息处理方法的处理流程图,具体包括以下步骤;
S402:对消息队列的写入接口进行加锁处理。
具体的,加锁可以使用java自带的读写锁ReentrantReadWriteLock实现,这个锁可以实现写锁加锁时,读锁会被阻塞,写锁释放后,多个读取线程可以并发加读锁,并发读取消息队列文件,多个消费者可以并发读取消息队列。多个写入线程请求写入锁加锁时,根据请求加锁时间先后顺序,依次给予写入锁,写入锁是互斥的,同一时刻只有一个写入线程拥有写入锁。写入完成后释放写入锁,其他写入锁请求线程或者读取锁请求线程在此时可以继续请求加锁。读锁和写锁是互斥的,可以存在多个并发读锁,但只有一个写锁。
S404:获取生产者的写入请求。
具体的,游戏数据收集器收集到了游戏内的用户登录信息,此时需要将用户的登录信息写入到消息队列之中,需首先经过加锁处理的消息队列写入接口发送写入请求。
S406:基于所述写入请求中携带的待执行消息读取队列文件版本字符与附属信息字符,根据所述队列文件版本字符与所述附属信息字符建立所述文件标识符。
具体的,队列文件版本字符可以是一段16个字符的特殊字符串,前8个字节记载的是附属信息字符,是一段8个字符的特殊字符串,如:WhaleQQQ,用于标记这个消息队列程序特有的队列文件。后8个字节记载的是文件版本字符,后8个字节的前4个字节是一个int类型,代表文件版本号,每次队列程序读取文件部分发生改动此部分加1,从1开始递增,后4个字节也是一个int类型,作为后续升级扩展预留使用,暂时设置值为-1。
其中,文件标识符的位置处在消息队列中每个队列文件的开始部分,详情请见说明书附图图2。
S408:根据所述写入请求中携带的待执行消息确定对应的消息字节。
具体的,确定用户登录信息对应的消息头字符串以及消息内容数据字符串,将所述消息头字符串与消息内容数据字符串拼接组成所述待执行消息对应的消息字节,其中,消息头字符串包括以下至少一种:消息ID、消息类型、消息编码、消息生成时间、队列程序接收时间、校验码、消息内容数据字符串长度。消息内容数据字符串是写入的用户登录信息的具体内容。
S410:根据所述消息字节的长度生成对应的长度字节。
计算上述消息字节的长度,即消息字节占用的字节数量,并将计算得出的消息字节占用的字符数量输入到占用4个字节的长度字节之中。
S412:在所述文件标识符对应的标识字节之后,顺序拼接所述长度字节和所述消息字节作为所述写入请求的响应。
其中,长度字节在消息字节之前,方便读取消息过程中,先读取到长度字节,确定消息字节所在范围,详情请见说明书附图图2。
S414:判断消息写入结果。
具体的,若消息写入成功,则将写入消息的写入位置与写入长度返回给当前游戏数据收集器;若写入失败,则将失败信息返回给当前游戏数据收集器。
S416:若当前写入请求写入成功,顺序执行后续的第二写入请求。
具体的,接收第二用户登录消息的写入请求,获取所述第二写入请求中携带的待执行消息对应的第二长度字节与第二消息字节。
S418:判断当前队列文件内的空间能否存入第二写入请求中携带的待执行信息。
具体的,判断所述第二长度字节与所述第二消息字节的字节长度加上当前文件中的写入消息长度,是否大于所述目标队列文件预设的长度阈值;其中长度阈值是人为设定的,此处设定为100M。若队列文件内的空间可以存入,则执行步骤S420;若队列文件内的空间不能存入,则执行步骤S424。
S420:将所述第二长度字节和第二消息字节写入目标队列文件。
具体的,根据所述第二写入请求中携带的待执行消息确定对应的第二消息字节,并根据所述第二消息字节的长度生成对应的第二长度字节;在当前消息字节之后,顺序拼接所述第二长度字节和所述第二消息字节,作为所述第二用户登录信息写入请求的响应,其中,将第二写入请求中携带的待执行消息写入队列文件的过程与之前步骤相似,只是产生的第二长度字节与第二消息字节顺序拼接在之前的写入请求响应之后,详情请参照说明书附图图2。
S422:创建文件之前,对文件赋予文件名。
具体的,获取所述目标队列文件的目标文件名与所述目标队列文件的目标文件长度;将所述目标文件名与所述目标队列长度相加的结果,作为所述新建文件的文件名,其中,消息队列中的第一个队列文件的文件名,可以将预设的目标字符设定为Long类型的数字0,后缀名为.pqdata。后续队列文件的文件名根据之前队列文件的文件名以及之前文件的文件长度确定。其中各个队列文件的命名的详情请参照说明书附图图2。
S424:创建新建文件,在新建文件中写入所述第二写入请求中携带的待执行消息。
具体的,创建新建文件,基于所述第二写入请求在所述新建文件的开始位置建立第二文件标识符;根据所述第二写入请求中携带的待执行消息确定对应的第二消息字节,并根据所述第二消息字节的长度生成对应的第二长度字节;在第二文件标识符之后,顺序拼接第二长度字节和第二消息字节,作为所述第二写入请求的响应。
其中,相较于步骤S420,此时的第二写入请求需写入到新建文件内,根据第二写入请求中携带的待写入消息生成的长度字节与消息字节,顺序拼接在新建文件的第二文件标识符后。详情请见说明书附图图2中除第一个文件0.pqdata外的队列文件中的第一条消息的存储方式。
需要说明,游戏数据收集器依次将收集到的数据写入消息队列,消息队列内部保持打开当前写入文件的句柄,当前消息写入成功后,判断出不需要新建队列文件的情况下,下一个消息使用上次写入的文件句柄继续写入即可,也就是说不需要根据写入消息成功后反馈回的消息位置去查找下次写入的位置,直接在当前写入位置之后顺序写入即可。
在将待处理的消息写入队列文件之后,还包括具体的消息读取部分,在本实施例中,消息的读取是由实时计算程序作为消费者,顺序读取消息队列中的消息信息,在读取消息队列中的某个消息时,会记录下当前消息在消息队列中的位置信息,在下次读取消息时从所述位置信息标定的位置继续读取,具体的读取内容如下:
S426:接收消费者的消息读取请求。
具体的,实时计算程序作为消费者,对消息队列中的用户登录消息进行读取,首先通过读取接口的加锁获取读锁。
S428:基于所述消息读取请求确定消息接收位置信息;
具体的,在实时计算程序的消息读取请求被接收之后,消息队列程序开始进行消息队列中的用户登录消息读取,消息队列程序首次读取消息队列中的消息时,从0.pqdata开始读取数据,若实时计算程序并不是第一次消费消息队列中的数据,则根据上一次消费时保存的消息位置信息所指示的位置,在消息队列中顺序读取消息队列中的下一个消息。
在实际应用中,假设消息队列中的第一个队列文件为0.pqdata,前16字节是文件标识符,第17-20字节是第一个消息的长度字节,若此字节长度的值为80,那么第21-100是第一个消息的消息字节,随后的4个字节,即第101-104字节是第二个消息的长度字节,若此字节长度的值为96,那么第105-200是第二个消息的消息字节。若实时计算程序第一次消费消息队列中的消息,此时接收到实时计算程序消费请求的消息队列程序从0.pqdata文件开始读取,先读取前16位的文件标识符并进行验证,随后读取第17-20字节,通过这4位的长度字节的值80确定队列文件中第一个消息在队列文件中占用的字节区间为17-100字节,读取第21-100字节范围内的消息字节,并将消息信息和消息位置返回给实时计算程序。
实时计算程序顺序消费接下来的消息,此时的实时计算程序明显不再是第一次消费消息队列中的数据,根据保存的上一次消费的消息的消息位置17-100字节,顺序消费下一个消息,在消息队列程序接收到实时计算程序的消费请求后,消息队列程序从101字节开始读取,先读取101-104字节范围内的四个字节,根据这四个字节所记载的第二个消息的字节长度,若此四个字节的值为96,那么第二个消息的消息字节在队列文件中占据的字节范围为105-200,读取第105-200字节内的第二个消息的消息字节内容,返回第二个消息的内容和位置至实时计算程序。
S430:在消息队列中确定所述消息位置信息对应的目标任务文件;
具体的,实时计算程序在确定消息位置信息之后,将消费的位置信息发送至消息队列程序,然后消息队列程序需要遍历消息队列中的队列文件的文件名,由于队列文件的文件名记录了队列文件在消息队列中所占字符的区间,判断消息位置信息指示的位置存在于哪个队列文件占用的字符区间之内,将此队列文件确定为目标任务文件。
在实际应用中,假设消息队列中的第一个文件的文件名为0.pqdata,第二个文件的文件名为104857600.pqdata,根据两个文件的文件名可知,第一个队列文件在消息队列中所占的字节范围是1-104857600字节,若消息位置信息标识消息在消息队列中占据的字节为101-200,其范围明显在第一个队列文件的字节范围之内,可以得知此消息存在于第一个队列文件之中,确定第一个队列文件为目标任务文件。
S432:自队列文件中找出指定位置的消息
具体的,消息队列程序顺序读取队列文件,在读取过程中,先顺序读取队列文件的前16位,以验证队列文件的文件标识符,确定队列文件正常且根据队列文件的版本选定读取方式,之后根据实时计算程序中保存的消息位置信息,由消息队列程序顺序读取待读取消息,读取完成后将得到的最新消息位置和消息长度返回实时计算程序。
需要说明的是,消息的读取与写入类似,在连续消息读取过程中,队列程序同样保持打开每个消费者的读取文件的句柄,在判断出上一个消息读取结束后传回的消费位置与当前读取位置相同,不需要从头开始查找消息位于哪个文件的哪个位置,顺序读取后续消息即可,只有在判断出传回的消费位置与当前读取位置不同,才需要从新确定文件以及文件中的消息位置。
在实际应用中,若实时计算程序在消费队列文件的中途去执行了另外的任务,在另外任务执行完成后继续消费消息队列文件,消息队列程序查询到实时计算程序中保存的上一次读取的消息的消息位置为17-100,经过判断确定其在消息队列的第一个队列文件中,首先读取消息队列的中的前16位,验证队列文件的文件标识符并确定文件版本,根据文件版本确定读取方式,随后读取消息队列的第101-104位,确定待读取消息的占用字节范围,若字节占用范围为101-200,读取此范围内的消息内容并将此消息内容与消息长度返回实时计算程序。
此外,实时计算程序除了以上的消息消费方式外,还存在基于时间回溯消费位置对消息进行消费的方法,当实时计算程序存在需要回溯到某个时间点来重新消费数据时,即可按照时间回溯消费位置进行消息的读取操作,以响应于实时计算程序回溯到某个时间点重新消费。
举例说明,依旧是对消息队列中的用户登录信息进行消费,但此时发现,用户登录异常,需要使用基于时间回溯消息位置,读取之前的用户登录信息,以此分析用户登录异常的原因。
具体的,实时计算程序是消费者,消息队列程序接收到消息读取请求后,先通过读取接口的加锁获取读锁,随后根据消费请求获得目标时间戳T1,将所有队列文件中将最接近目标时间戳T1且最后修改时间TX大于目标时间戳T1的队列文件选出。
接下来先读取此队列文件的前16位,验证文件标识符并根据文件标识符后8位的文件版本信息选取读取方法,在验证通过的情况下,逐条读取队列文件中的消息。
在逐条读取队列文件的消息的过程中,先读取前4位字节,确定消息的长度字节,得出消息在队列文件中占用字符的范围为17-100,随后读取消息字节中的消息头字符串,找出消息头字符串中的消息接收时间信息T2,对比消息接收时间信息T2与目标时间戳T1,若消息接收时间信息记载的时间小于目标时间戳的时间,则跳过这条消息。读取101-104字节,此4个字节是此第二条消息的长度字节,若此长度字节的值为96,确定第二条消息的在队列文件中占用的字符范围为101-200,随后读取第二条消息的消息字节中的消息头字符串,找出消息头字符串中的消息接收时间信息,若消息接收时间信息记载的时间T3大于目标时间戳的时间T1,返回这条消息的上一条消息的位置,所述的上一条消息就是实时计算程序寻找的用户登录信息,消息队列程序对此用户登录信息进行读取,读取完成后将消息内容和位置信息返回实时计算程序。
综上所述,一个待存储的消息信息以这种形式进行存储和读取,省去了索引文件,减轻了系统的内存压力,在消息的读取过程中,也能省去引用索引文件的过程,使消息读取的过程更加简洁与快速,为消息的读取、查询提供了良好环境。
与上述方法实施例相对应,本申请还提供了消息处理装置实施例,图5示出了本申请一实施例提供的一种消息处理装置的结构示意图。如图5所示,该装置包括:
获取模块502,被配置为获取生产者的第一写入请求,基于所述第一写入请求在目标队列文件的开始位置建立文件标识符;
确定模块504,被配置为根据所述第一写入请求中携带的待执行消息确定对应的消息字节,并根据所述消息字节的长度生成对应的长度字节;
拼接模块506,被配置为在所述文件标识符对应的标识字节之后,顺序拼接所述长度字节和所述消息字节作为所述第一写入请求的响应。
一个可选的实施例中,所述消息处理装置,还包括:
写入模块,被配置为接收第二写入请求,其中所述第二写入请求为时序相邻且在第一写入请求之后的写入请求;获取所述第二写入请求中携带的待执行消息对应的第二长度字节与第二消息字节;判断所述第二长度字节与所述第二消息字节的字节长度加上当前文件中的写入消息长度,是否大于所述目标队列文件预设的长度阈值;若否,将所述第二长度字节和第二消息字节写入目标队列文件;若是,创建新建文件,在新建文件中写入所述第二写入请求中携带的待执行消息。
一个可选的实施例中,所述写入模块进一步被配置为:
根据所述第二写入请求中携带的待执行消息确定对应的第二消息字节,并根据所述第二消息字节的长度生成对应的第二长度字节;在当前消息字节之后,顺序拼接所述第二长度字节和所述第二消息字节,作为所述第二写入请求的响应。
一个可选的实施例中,所述写入模块进一步被配置为:
获取所述目标队列文件的目标文件名与所述目标队列文件的目标文件长度;
将所述目标文件名与所述目标队列长度相加的结果,作为所述新建文件的文件名,其中,所述目标队列文件基于预设的目标字符和文件后缀名进行命名。
创建新建文件,基于所述第二写入请求在所述新建文件的开始位置建立第二文件标识符;根据所述第二写入请求中携带的待执行消息确定对应的第二消息字节,并根据所述第二消息字节的长度生成对应的第二长度字节;在第二文件标识符之后,顺序拼接第二长度字节和第二消息字节,作为所述第二写入请求的响应。
一个可选的实施例中,所述获取模块502进一步被配置为:
基于所述第一写入请求中携带的待执行消息读取队列文件版本字符与附属信息字符;根据所述队列文件版本字符与所述附属信息字符建立所述文件标识符;其中,所述队列版本字符用于标记当前文件的版本信息,所述附属信息字符用于标记消息队列中特有的队列文件。
一个可选的实施例中,所述确定模块504进一步被配置为:
确定所述待执行消息对应的消息头字符串以及消息内容数据字符串;将所述消息头字符串与消息内容数据字符串拼接组成所述待执行消息对应的消息字节。
一个可选的实施例中,所述消息处理装置,还包括:
加锁模块,被配置为对消息队列的写入接口进行加锁处理;相应的,所述获取生产者的写入请求,包括:获取所述生产者通过加锁后的写入接口提交的所述写入请求;其中,若存在至少两个生产者通过加锁后的写入接口提交写入请求,则按每个写入请求的写入时间依次处理。
本申请提供的消息处理装置,通过接收生产者发送的写入请求,并基于所述写入请求的内容在队列文件的开始位置建立文件标识符,根据写入请求中携带的待处理消息的内容,确定消息字节和消息字节的长度字节,将长度字节和消息字节顺序存储到消息队列之中。一个待存储的消息信息以这种形式进行存储,在读取目标消息时结合消息队列中的队列文件的文件名,可以直接找到目标消息对应的队列文件,省去了索引文件,减轻了系统的内存压力,在消息的读取过程中,也能省去引用索引文件的过程,使消息读取的过程更加简洁与快速,为消息的读取、查询提供了良好环境。
需要说明的是,该消息处理装置的技术方案与上述的消息处理方法的技术方案属于同一构思,消息处理装置的技术方案未详细描述的细节内容,均可以参见上述消息处理方法的技术方案的描述。此外,装置实施例中的各组成部分应当理解为实现该程序流程各步骤或该方法各步骤所必须建立的功能模块,各个功能模块并非实际的功能分割或者分离限定。由这样一组功能模块限定的装置权利要求应当理解为主要通过说明书记载的计算机程序实现该解决方案的功能模块构架,而不应当理解为主要通过硬件方式实现该解决方案的实体装置。
与上述方法实施例相对应,本申请还提供了消息读取装置实施例,图6示出了本申请一实施例提供的一种消息读取装置的结构示意图。如图6所示,该装置包括:
接收模块602,被配置为接收消费者的消息读取请求;
位置确定模块604,被配置为基于所述消息读取请求确定消息位置信息;
文件确定模块606,被配置为在消息队列中确定所述消息位置信息对应的目标任务文件;
读取模块608,被配置为响应于所述消息读取请求在所述目标任务文件中读取所述目标消息,并发送至消费者。
一个可选的实施例中,所述读取模块608进一步被配置为:
在所述目标任务文件中读取所述目标消息的长度字节,并基于所述长度字节确定所述目标消息的目标长度信息;基于所述目标长度信息在所述目标任务文件中读取相应的字节序列组成所述目标消息。
一个可选的实施例中,所述消息读取装置,还包括:
标识符验证模块,获取所述目标队列文件的所述文件标识符;对所述文件标识符中的文件版本字符和附属信息字符进行验证;在验证通过的情况下,顺序执行后续消息读取任务;在验证未通过的情况下,结束进程,并将错误信息返回给消费者。
一个可选的实施例中,所述文件确定模块606进一步被配置为:
确定所述消息位置信息中包含的任务文件名;基于所述任务文件名在消息队列中确定所述目标任务文件,或者
确定所述消息位置信息中记载的所述目标消息在消息队列中的字节位置;调用消息队列程序逐一确定消息队列中的队列文件的队列文件名;根据所述队列文件名确定各队列文件占据消息队列的字节范围;基于所述字节位置与所述字节范围,确定所述目标消息所在的目标任务文件或者
确定所述消息位置信息中记载着所述目标消息的目标时间戳;调用消息队列程序逐一确定消息队列中的队列文件的创建时间;基于所述目标时间戳与各个队列文件的创建时间,确定所述目标消息所在的目标任务文件。
需要说明的是,该消息读取装置的技术方案与上述的消息读取方法的技术方案属于同一构思,消息读取装置的技术方案未详细描述的细节内容,均可以参见上述消息读取方法的技术方案的描述。此外,装置实施例中的各组成部分应当理解为实现该程序流程各步骤或该方法各步骤所必须建立的功能模块,各个功能模块并非实际的功能分割或者分离限定。由这样一组功能模块限定的装置权利要求应当理解为主要通过说明书记载的计算机程序实现该解决方案的功能模块构架,而不应当理解为主要通过硬件方式实现该解决方案的实体装置。
图7示出了根据本申请一实施例提供的一种计算设备700的结构框图。该计算设备700的部件包括但不限于存储器710和处理器720。处理器720与存储器710通过总线730相连接,数据库750用于保存数据。
计算设备700还包括接入设备740,接入设备740使得计算设备700能够经由一个或多个网络760通信。这些网络的示例包括公用交换电话网(PSTN)、局域网(LAN)、广域网(WAN)、个域网(PAN)或诸如因特网的通信网络的组合。接入设备740可以包括有线或无线的任何类型的网络接口(例如,网络接口卡(NIC))中的一个或多个,诸如IEEE802.11无线局域网(WLAN)无线接口、全球微波互联接入(Wi-MAX)接口、以太网接口、通用串行总线(USB)接口、蜂窝网络接口、蓝牙接口、近场通信(NFC)接口,等等。
在本申请的一个实施例中,计算设备700的上述部件以及图7中未示出的其他部件也可以彼此相连接,例如通过总线。应当理解,图7所示的计算设备结构框图仅仅是出于示例的目的,而不是对本申请范围的限制。本领域技术人员可以根据需要,增添或替换其他部件。
计算设备700可以是任何类型的静止或移动计算设备,包括移动计算机或移动计算设备(例如,平板计算机、个人数字助理、膝上型计算机、笔记本计算机、上网本等)、移动电话(例如,智能手机)、可佩戴的计算设备(例如,智能手表、智能眼镜等)或其他类型的移动设备,或者诸如台式计算机或PC的静止计算设备。计算设备700还可以是移动式或静止式的服务器。其中,处理器720用于执行如下计算机可执行指令时实现所述消息处理方法。
上述为本实施例的一种计算设备的示意性方案。需要说明的是,该计算设备的技术方案与上述的消息处理方法或消息读取方法的技术方案属于同一构思,计算设备的技术方案未详细描述的细节内容,均可以参见上述消息处理方法或消息读取方法的技术方案的描述。
本申请一实施例还提供一种计算机可读存储介质,其存储有计算机指令,该指令被处理器执行时以用于所述消息处理方法或消息读取方法。
上述为本实施例的一种计算机可读存储介质的示意性方案。需要说明的是,该存储介质的技术方案与上述的消息处理方法或消息读取方法的技术方案属于同一构思,存储介质的技术方案未详细描述的细节内容,均可以参见上述消息处理方法或消息读取方法的技术方案的描述。
本申请一实施例还提供一种芯片,其存储有计算机程序,该计算机程序被芯片执行时实现所述消息处理方法或消息读取方法的步骤。
上述对本申请特定实施例进行了描述。其它实施例在所附权利要求书的范围内。在一些情况下,在权利要求书中记载的动作或步骤可以按照不同于实施例中的顺序来执行并且仍然可以实现期望的结果。另外,在附图中描绘的过程不一定要求示出的特定顺序或者连续顺序才能实现期望的结果。在某些实施方式中,多任务处理和并行处理也是可以的或者可能是有利的。
所述计算机指令包括计算机程序代码,所述计算机程序代码可以为源代码形式、对象代码形式、可执行文件或某些中间形式等。所述计算机可读介质可以包括:能够携带所述计算机程序代码的任何实体或装置、记录介质、U盘、移动硬盘、磁碟、光盘、计算机存储器、只读存储器(ROM,Read-Only Memory)、随机存取存储器(RAM,Random Access Memory)、电载波信号、电信信号以及软件分发介质等。需要说明的是,所述计算机可读介质包含的内容可以根据司法管辖区内立法和专利实践的要求进行适当的增减,例如在某些司法管辖区,根据立法和专利实践,计算机可读介质不包括电载波信号和电信信号。
需要说明的是,对于前述的各方法实施例,为了简便描述,故将其都表述为一系列的动作组合,但是本领域技术人员应该知悉,本申请并不受所描述的动作顺序的限制,因为依据本申请,某些步骤可以采用其它顺序或者同时进行。其次,本领域技术人员也应该知悉,说明书中所描述的实施例均属于优选实施例,所涉及的动作和模块并不一定都是本申请所必须的。
在上述实施例中,对各个实施例的描述都各有侧重,某个实施例中没有详述的部分,可以参见其它实施例的相关描述。
以上公开的本申请优选实施例只是用于帮助阐述本申请。可选实施例并没有详尽叙述所有的细节,也不限制该发明仅为所述的具体实施方式。显然,根据本申请的内容,可作很多的修改和变化。本申请选取并具体描述这些实施例,是为了更好地解释本申请的原理和实际应用,从而使所属技术领域技术人员能很好地理解和利用本申请。本申请仅受权利要求书及其全部范围和等效物的限制。