1 业务背景
随着公司产品的上线推向市场,运营的同事会关注产品的用户数、活跃程度、留存数等运营指标,以此指标来指导产品的未来规划。因此需要开发运营平台,来统计展示相关运营指标。
例如:需要统计展示最近7天每天的、不同产品、不同城市:
- 设备绑定用户数
- 活跃设备用户数(上线次数>=1的设备用户数)
- 绑定用户次日留存数(第1天绑定用户中,在第2天上线的设备用户数)
- 活跃用户次日留存数(第1天上线次数>=1的设备用户中,在第二天上线的设备用户数)
2 技术方案
既然是要展示最近7天每天的用户数,那么首先我们需要存储每天的用户数到数据表中,只要存储了这些数据,后面统计展示就直接查询DB就很简单了。
因此,首要任务就是需要得到每天的用户数,然后存储到DB。实际在我们的运营平台项目中,是要得到:每天的不同产品、不同城市的绑定用户数。
因此,我们的方案是:
-
存储原始数据,即设备绑定用户记录(dev_id, user_id, product_id,city,bind_time)
-
每天凌晨跑定时任务,去分组汇总聚合每天的绑定用户数
在开发运营平台之前,我们有device核心业务服务已经存储了原始绑定用户记录(但是没有city字段),目的是为了支撑设备绑定、设备列表查询、设备详情等核心业务需求。我们是否可以直接基于device服务的原始绑定数据,在device服务中开发分组汇总聚合每日绑定用户数呢?答案是不合适。
因为如果是基于device服务存储的原始绑定用户记录,分组汇总聚合每日绑定用户数会有如下问题:
-
需要添加city字段,如果未来还有其他维度,还需要不断的添加字段,统计业务和核心业务严重耦合
-
device服务在设计之初只是为了支撑设备绑定、查询等业务,不应承担汇总统计职责,违反模块单一职责
-
同时如果统计业务出现问题或者故障,会影响device核心业务功能
因此最终制定的架构设计方案是:
- 独立一个统计服务专门负责原始数据存储、汇总聚合统计数据,与device核心服务解耦
- 采用消息队列异步事件驱动架构传递绑定事件,由独立的统计服务处理聚合,并存储到数据库中。这样可以提高系统的可扩展性、稳定性和性能。
[设备服务] -- 写绑定业务 --> MySQL
|
|-- 发布事件 --> RocketMQ
|
+-- [统计服务] -- 消费处理 --> Mysql原始数据表
|
+-- 汇总聚合 --> Mysql汇总表/Redis缓存
3. 方案细节
3.1 采集原始数据
使用AOP拦截绑定接口收集设备绑定用户信息,发送到RocketMQ,统计服务消费储存原始数据。这样最大程度与device服务绑定业务逻辑解耦。
3.2 汇总聚合每日绑定用户数
统计服务每日凌晨定时汇总聚合每日绑定用户数。
3.3 表设计
基于绑定用户维度统计需求,我们进行如下的相关表设计。
绑定汇总聚合表:用于统计展示每天的不同产品、不同城市的绑定用户数。
字段名称 | 描述 |
---|---|
stats_date | 统计日期 |
product_id | 产品id |
area_code | 区域 |
bind_users | 设备绑定用户数 |
绑定信息原始表:基于此原始表,每天凌晨定时汇总聚合生成每天的不同产品、不同城市的绑定用户数。
字段 | 描述 |
---|---|
bind_time | 绑定时间 |
product_id | 产品id |
area_code | 区域 |
dev_id | 设备id |
user_id | 用户id |
bind_status | 绑定或者解绑 |
4 总结
实现查询展示每日绑定用户数,需要基于原始绑定数据,汇总聚合存储每日绑定用户数。
需要注意独立一个统计服务,专门负责收集原始绑定数据,然后定时调度汇总聚合生成每日绑定用户数。以此避免和device服务耦合。
在实现细节上使用AOP在绑定接口执行完毕之后,收集绑定信息数据,异步发送RocketMQ,以此最大程度保证和device服务的核心绑定逻辑解耦。
最后,具体实现细节,后面章节在详细说明。