在线人数统计业务设计(场景八股文)

 业务问题

 在当经的网站中,在线人数的实时统计已经是一个必不可少的模块了,并且该统计功能最好能够按不同的时间间隔做的统计,现在需要你设计一个在线人数统计的模块,你应该怎么进行设计的呢?

背景

一个网校下会有多个学员。目前平台大概有十个,平台对应的网校大概五十几个,平均一个网校会有5w个用户,预计总人数为200w,最该学员的在线人数在10w左右。

设计思路

最开始的时候,想到的就是使用mysql直接实现,但是明显在技术的选型上就不太合理,假设一天有十万人上下线,那么一天就会十万条以上的数据,一个月下来就是300w条数据,一年下来就是3000w的数据,虽然可以配合定时任务定期删除的旧数据。因为涉及到用户的上下线的操作,所以就存在大量的打db操作。在效率上还是太低了。

在设计上想到使用redis来临时存储到在线人员的数据,在mysql中存储在线的统计数据,减少直接打mysql的操作。因为项目采用的ddd的架构,通过事件的发送和消费来实现领域驱动,我们以用户观看视频和用户结束观看视频为在线人数的统计依据。在flink中监听到开始观看视频事件和继续观看视频事件为在线人数+1的情况,监听到完成视频观看和暂停视频观看为在线人数-1的情况。

1.在redis中记录对应在在线学员的用户信息,主要的信息为平台id、网校id

flink中消费到开始观看视频事件和继续观看视频事件的时候会将userInfo的数据清洗到redis的hash结构中,监听到完成视频观看和暂停视频观看会删除redis中对应userInfo的数据。

2.使用Redis的键空间通知功能 + Redis的Hash结果来记录实时的在线学员人数

我们会创建应该hash数据,大key为业务类型,通过大key我们来区分是对平台统计还是对网校统计,小key为业务id,可能是平台id也可能是网校的id。vlaue值就存储当前业务域的在线人数。redis会订阅userInfo,并监听其的新增事件和删除事件。在监听到新增事件的时候在hash结构中的值 + 1(平台的统计和网校的统计),并再使用一个hash值存储userInfo的数据做数据备份。此备份主要是为了后续删除事件需要网校id和平台id。并且在监听删除事件的时候,会将备份数据删除。

空间通知配置代码为下:

@Bean
RedisMessageListenerContainer redisContainer(RedisConnectionFactory factory) {
    RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    container.setConnectionFactory(factory);

    // 监听键空间事件(获取操作类型)
    container.addMessageListener((message, pattern) -> {
        String channel = new String(message.getChannel());
        String eventType = new String(message.getBody());
        String key = channel.split(":")[1];
        
        if ("del".equals(eventType)) {
            // 触发值获取流程
            Object value = valueCache.getIfPresent(key);
            System.out.println("删除值: " + value);
        }
    }, new ChannelTopic("__keyspace@0__:userInfo"));

    // 监听键事件(获取操作时间戳)
    container.addMessageListener((message, pattern) -> {
        String key = new String(message.getBody());
        // 在删除前立即缓存值
        Object value = redisTemplate.opsForValue().get(key); 
        valueCache.put(key, value);
    }, new ChannelTopic("__keyevent@0__:del"));

    return container;
}

// 使用Guava缓存(保留10秒)
private Cache<String, Object> valueCache = CacheBuilder.newBuilder()
    .expireAfterWrite(10, TimeUnit.SECONDS)
    .build();

3.通过定时任务每隔五分钟将统计得到的平台在线人数和网校在线人数和当前时间存储到mysql中。

在mysql中,会有一个type类型用来记录按不同时间间隔的统计类型。不同的类型也是通过不同的定时任务来计算出来的。其中如果类型为间隔五分钟则只会存储一周,间隔一小时则存储一个月,以此做自定义扩展,防止数据堆积。

4.数据一致性保障

因为网络等因素,删除和新增事件可能未能被及时的监听到,此时统计出来的实时在线人数和实际的线上人数有一定的误差,所以在我们做了一个数据矫正的策略。我们会在明天的凌晨低峰阶段到userInfo中直接统计出实时人数。也是因为网络的因素,可能会出现userInfo备份数据迟迟不被删除的情况,我们会给备份数据设置ttl过期时间,因为国家规定学员每天的最大学习时间不能超过8小时,所以备份数据ttl有效时间设置为8小时。

如果历史在线统计人数我们对做了热数据的缓存,减少直接打mysql的操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值