Springboot+redis 做实时在线人数统计

介绍

利用redis 有序集合实现。

Redis 有序集合和集合一样也是 string 类型元素的集合,且不允许重复的成员。 不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。 有序集合的成员是唯一的,但分数(score)却可以重复。

zset命令

  1. 新增成员
    zadd:新增成员
  2. 删除成员
    zrem:根据指定key进行删除
    zremrangebylex:根据指定集合区间进行删除
    zremrangebyrank:根据指定排名区间进行删除
    zremrangebyscore:根据分数区间进行删除
  3. 查询成员
    zcard:查询集合成员数量
    zcount:分数区间成员数量
    zlexcount:成员区间成员数量
    zscore:指定key和值,获取分数
    zrange:获取成员信息
    zrank:指定key和值,获取下标
    zrangebylex:指定集合区间,获取列表
    zrangebyscore:指定分数区间,获取列表
    zrevrange:倒序展示列表
    zrevrangebyscore:根据分数区间,倒序展示列表
    zreverank:倒序获取成员下标

最终呈现效果
在这里插入图片描述
这里使用的工具是AnotherRedisDesktopManager

实现

因为zset中每个元素都有关联的分数,并且可以通过分数区间查询删除数据,所以我们可以将分数设置为时间戳。
1、后端编写一个用户心跳接口(注意这个接口需要放开权限


    /**
     * 用户心跳保持接口
     * @param :
     * @return boolean
     * @author LuckyDog 19:08 2021/12/10
     **/
    @GetMapping("heartbeat")
    @ResponseBody
    public boolean heartbeat(HttpServletRequest request) {
        User sessionUser = CommonUtil.getSessionUser(request);
        if(sessionUser!=null&&sessionUser.getId()!=null&&sessionUser.getId()!=0){
            fyStatisticsUtil.userOnline(sessionUser.getId());
            return true;
        }
        return false;
    }

fyStatisticsUtil 一个工具类


import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import javax.annotation.Resource;
import org.springframework.stereotype.Component;

/**
 * @author LuckyDog
 */
@Component
public class FyStatisticsUtil {

    private static final String ONLINE_USER = "online_user";
    @Resource
    private FyRedisUtil fyRedisUtil;

    //region 在线用户统计相关

    public void userOnline(Long userId) {
        fyRedisUtil.zsetSet(ONLINE_USER, userId, System.currentTimeMillis());
    }

    public Long userOnlineCount() {
        return fyRedisUtil.zsetCount(ONLINE_USER);
    }

    public Long userOfflineClean() {
        DateTime dateTime = DateTime.now();
        DateTime offset = dateTime.offset(DateField.MINUTE, -2);
        return fyRedisUtil.zsetRemove(ONLINE_USER, 0L, offset.getTime());
    }

    //endregion
}

fyRedisUtil 部分代码


    public Boolean zsetSet(String key, Object value, long score){
        try {
            return redisTemplate.opsForZSet().add(key, value, score);
        }
        catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    public Long zsetCount(String key){
        try {
            return redisTemplate.opsForZSet().size(key);
        }
        catch (Exception e) {
            e.printStackTrace();
            return 0L;
        }
    }

    public Long zsetRemove(String key,Long minScore,Long maxScore){
        try {
            return redisTemplate.opsForZSet().removeRangeByScore(key,minScore,maxScore);
        }
        catch (Exception e) {
            e.printStackTrace();
            return 0L;
        }
    }

定时器,定时清理已过期用户数量


/**
 * @author LuckyDog
 */
@Component
@ConditionalOnProperty(prefix = "website", name = "run", havingValue = "analysisTimer")
public class StatisticsTimer {

    @Resource
    FyStatisticsUtil fyStatisticsUtil;
    @Scheduled(cron = "0 0/2 * * * ?")
    public void clearOfflineUser() {
        Long aLong = fyStatisticsUtil.userOfflineClean();
        System.out.println("清除已过期用户数量:" + aLong);
    }
}

最后找一个前端页面最外层框架页里,写上一个定时请求心跳接口的js就行了,我这里设置的是一分钟心跳一次

 let timer;
 $(function(){
      beat();
      timer = setInterval("beat()",60000);
  })
  function beat(){
      $.get("/heartbeat",null,function(res){
          if(!res){
              clearInterval(timer);
          }
      });
  }

大功告成。这种方案实现的实时在线人数相对来说是比较准确的。只要用户访问网站,每分钟就会心跳一次。每次心跳都会更新redis里面的时间戳。如果用户心跳中断,就意味着用户已经离线。后端也有一个定时器,设置的每两分钟清理一次离线用户。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐