• 在线人数统计功能怎么实现?


    一、前言

    大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。

    在线人数统计这个功能相信大家一眼就明白是啥,这个功能不难做,实现的方式也很多,这里说一下我常使用的方式:使用Redis的有序集合(zset)实现。
    核心方法是这四个:zaddzrangeByScorezremrangeByScorezrem

    二、实现步骤

    1. 如何认定用户是否在线?

    认定用户在线的条件一般跟网站有关,如果网站需要登录才能进入,那么这种网站就是根据用户的token令牌有效性判断是否在线;
    如果网站是公开的,是那种不需要登录就可以浏览的,那么这种网站一般就需要自定一个规则来识别用户,也有很多方式实现如IPdeviceId浏览器指纹,推荐使用浏览器指纹的方式实现。

    浏览器指纹可能包括以下信息的组合:用户代理字符串 (User-Agent string)、HTTP请求头信息、屏幕分辨率和颜色深度、时区和语言设置、浏览器插件详情等。现成的JavaScript库,像 FingerprintJSClientJS,可以帮助简化这个过程,因为它们已经实现了收集上述信息并生成唯一标识的算法。

    使用起来也很简单,如下:

    // 安装:npm install @fingerprintjs/fingerprintjs
    
    // 使用示例:
    import FingerprintJS from '@fingerprintjs/fingerprintjs';
    
    // 初始化指纹JS Library
    FingerprintJS.load().then(fp => {
      // 获取访客ID
      fp.get().then(result => {
        const visitorId = result.visitorId;
        console.log(visitorId);
      });
    });
    
    

    这样就可以获取一个访问公开网站的用户的唯一ID了,当用户访问网站的时候,将这个ID放到访问链接的Cookie或者header中传到后台,后端服务根据这个ID标示用户。

    2. zadd命令添加在线用户

    (1)zadd命令介绍
    zadd命令有三个参数

    key:有序集合的名称。
    score1、score2 等:分数值,可以是整数值或双精度浮点数。
    member1、member2 等:要添加到有序集合的成员。
    例子:向名为 myzset 的有序集合中添加一个成员:ZADD myzset 1 "one"

    (2)添加在线用户标识到有序集合中

    // expireTime给用户令牌设置了一个过期时间
    LocalDateTime expireTime = LocalDateTime.now().plusSeconds(expireTimeout);
    String expireTimeStr = DateUtil.formatFullTime(expireTime);
    // 添加用户token到有序集合中
    redisService.zadd("user.active", Double.parseDouble(expireTimeStr), userToken);
    

    由于一个用户可能户会重复登录,这就导致userToken也会重复,但为了不重复计算这个用户的访问次数,zadd命令的第二个参数很好的解决了这个问题。
    我这里的逻辑是:每次添加一个在线用户时,利用当前时间加上过期时间计算出一个分数,可以有效保证当前用户只会存在一个最新的登录态。

    3. zrangeByScore命令查询在线人数

    (1)zrangeByScore命令介绍

    key:指定的有序集合的名字。
    min 和 max:定义了查询的分数范围,也可以是 -inf 和 +inf(分别表示“负无穷大”和“正无穷大”)。
    例子:查询分数在 1 到 3之间的所有成员:ZRANGEBYSCORE myzset 1 3

    (2)查询当前所有的在线用户

    // 获取当前的日期
    String now = DateUtil.formatFullTime(LocalDateTime.now());
    // 查询当前日期到"+inf"之间所有的用户
    Set userOnlineStringSet = redisService.zrangeByScore("user.active", now, "+inf");
    

    利用zrangeByScore方法可以查询这个有序集合指定范围内的用户,这个userOnlineStringSet也就是在线用户集,它的size就是在线人数了。

    4. zremrangeByScore命令定时清除在线用户

    (1)zremrangeByScore命令介绍

    key:指定的有序集合的名字。
    min 和 max:定义了查询的分数范围,也可以是 -inf 和 +inf(分别表示“负无穷大”和“正无穷大”)。
    例子:删除分数在 1 到 3之间的所有成员:ZREMRANGEBYSCORE myzset 1 3

    (2)定时清除在线用户

    // 获取当前的日期
    String now = DateUtil.formatFullTime(LocalDateTime.now());
    // 清除当前日期到"-inf"之间所有的用户
    redisService.zremrangeByScore(""user.active"","-inf", now);      
    

    由于有序集合不会自动清理下线的用户,所以这里我们需要写一个定时任务去定时删除下线的用户。

    5. zrem命令用户退出登录时删除成员

    (1)zrem命令介绍

    key:指定的有序集合的名字。
    members:需要删除的成员
    例子:删除名为xxx的成员:ZREM myzset "xxx"

    (2)定时清除在线用户

    // 删除名为xxx的成员
    redisService.zrem("user.active", "xxx");      
    

    删除 zset中的记录,确保主动退出的用户下线。

    三、小结一下

    这种方案的核心逻辑就是,创建一个在线用户身份集合为key,利用用户身份为member,利用过期时间为score,然后对这个集合进行增删改查,实现起来还是比较巧妙和简单的,大家有兴趣可以试试看。

    作者:不若为止
    欢迎任何形式的转载,但请务必注明出处。
    限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。

  • 相关阅读:
    vue——组件传值(高级)、属性传值、反向传值、跨级传值
    JavaScript进阶知识点——函数和对象详解
    Anaconda虚拟环境配置Python库与Spyder编译器
    复盘:Python内存管理&垃圾回收原理
    C++之typeid的介绍
    HTML常用标签
    物联网的应用——环境监测
    【算法】国庆加班,火锅与Linq.AddRange的奇妙螺旋
    vue3 + ant design vue项目svg格式的background-image使用配置
    java 多线程&interrupt()方法遇到wait()方法——71
  • 原文地址:https://www.cnblogs.com/wlovet/p/18126425