一、业务背景
从技术的角度来说,技术方案的选型都是受限于实际的业务场景,都以解决实际业务场景为目标。
在我们的实际业务场景中,需要以游戏的维度收集和上报行为数据,考虑数据的量级,执行尽最大努力交付且允许数据的部分丢弃。
数据上报支持游戏的维度的批量上报,支持同一款游戏128个行为进行批量上报。
数据上报需要时效控制,上报的数据必须是上报时刻的前3分钟的数据。
整体数据的业务形态如下图所示:
二、技术选型
从业务的角度来说包含数据的收集和数据的上报,我们把数据的收集比作生产者,数据的上报比作消费者,是一个典型的生产消费模型。
生产消费模型在JVM进程内部通过队列+锁或者无锁的Disruptor来实现,在跨进程场景下通过MQ(RocketMQ/kafka)进行处理解耦。
但是细化到具体业务场景来看,消息的消费有诸多限制,包括:游戏维度的批量行为上报,行为上报的时效限制,细化到各个技术方案选型进行对比。
方案一
使用RocketMQ 或者Kafaka等消息队列来存储上报的消息,但是消费侧需要考虑在业务进程中按照游戏维度进行聚合,其中技术细节涉及按照游戏维度进行拆分,在满足消息时效性和批量性的前提下触发上报。在这种方案下消息中间件扮演的角色本质上消息的中转站,没有解决任何业务场景中提及的游戏维度拆分、批量性和时效性。
方案二
在方案一的基础上,寻求一种技术方案来解决游戏维度的消息分组、批量消费 、时效性。通过Redis的list结构来实现队列(进一步要求实现定长队列)来解决游戏维度的消息分组;通过Redis的list支持的Lrange来实现批量消费;通过业务侧的多线程来解决时效问题,针对高频的游戏使用单独的线程池进行处理,上述两个手段能够保证消费速度大于生产速度。
方案对比
对比两种方案后决定使用Redis的实现了一个伪消息中间件:
-
通过List对象实现定长队列来保存游戏维度的行为消息(以游戏作为key的List对象来保存用户行为);
-
通过List来保存所有的存在行为数据的游戏列表;
-
通过Set来进行去重判断来保证2中的List对象的唯一性。
整体的技术方案如下图所示:
生产过程
步骤一:游戏维度的某行为数据PUSH到游戏维度的队列当中。
步骤二:判断游戏是否在游戏的集合Set中,如果在就直接返回,如果不在进行步骤三。
步骤三:往游戏列表中PUSH游戏。
消费过程
步骤一: