Bitmap 实现当前在线用户数量

news2025/1/23 22:42:23

Bitmap是什么?

Bitmap是Redis中的一种数据结构,它是一个类似于位数组的数据结构,用于处理位数据。在Redis中,Bitmap是使用字符串来存储的,一个Byte可以存储8个二进制位,一个字符串可以存储232个二进制位,所以一个字符串最多可以表示232个用户的在线状态, 也就是它的偏移量offset。

在实际应用中,Bitmap常用于记录某个ID是否存在、统计某个时间段内的用户在线情况等等。通过对Bitmap进行位运算,可以快速高效地完成这些操作。

以下例子做了什么?

1、将用户标记为在线
2、将用户标记为离线
3、获取当前在线用户数
4、获取昨天在线用户数
5、获取某一天在线用户数量

1、 Controller层:

package com.lfsun.main.controller;

import com.lfsun.api.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.text.ParseException;

/**
 * @author Administrator
 */
@RestController
public class LoginController {

    @Autowired
    private LoginService loginService;

    /**
     * 处理登录请求
     *
     * @param userId 要登录的用户 ID
     * @return 返回登录结果, true 表示登录成功, false 表示登录失败。
     */
    @PostMapping("/login")
    public boolean login(@RequestParam String userId) {
        return loginService.login(userId);
    }

    /**
     * 处理退出登录请求
     *
     * @param userId 要退出登录的用户 ID
     * @return 返回退出登录结果,true 表示退出登录成功,false 表示退出登录失败。
     */
    @PostMapping("/logout")
    public boolean logout(@RequestParam String userId) {
        return loginService.logout(userId);
    }

    /**
     * 获取当前在线用户数量
     *
     * @return 返回当前在线用户数量。
     */
    @GetMapping("/onlineCount")
    public long getOnlineCount() {
        return loginService.getOnlineCount();
    }

    /**
     * 获取昨天在线用户数量
     *
     * @return 返回昨天在线用户数量。
     */
    @GetMapping("/yesterdayOnlineCounteCount")
    public long getYesterdayOnlineCount() {
        return loginService.getYesterdayOnlineCount();
    }

    /**
     * 获取某一天在线用户数量
     *
     * @return 返回某一天在线用户数量。
     */
    @GetMapping("/onedayOnlineCount")
    public long getOnedayOnlineCount(@RequestParam String date) throws ParseException {
        return loginService.getOnedayOnlineCount(date);
    }
}


2、 service层

package com.lfsun.api.service;

import java.text.ParseException;

public interface LoginService {
    boolean login(String userId);

    boolean logout(String userId);

    long getOnlineCount();

    long getYesterdayOnlineCount();

    long getOnedayOnlineCount(String date) throws ParseException;
}

3、serviceimpl层(做了主动退出进行将用户标记为离线,可附加登录过期从而根据userid重新计算偏移量对bitmap进行更新)

package com.lfsun.service.main;

import com.lfsun.api.service.LoginService;
import com.lfsun.common.constant.CommonConstant;
import com.lfsun.common.util.DateUtils;
import com.lfsun.common.util.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.text.ParseException;

/**
 * 登录服务实现类
 *
 * @author Administrator
 */
@Service
public class LoginServiceImpl implements LoginService {

    @Autowired
    private RedisUtils redisUtils;

    /**
     * 用户登录,将用户标记为在线
     *
     * @param userId 用户ID
     * @return 是否成功
     */
    @Override
    public boolean login(String userId) {
        try {
            // ... 处理登录逻辑
            redisUtils.setBitWithFixedOffset(CommonConstant.LOGIN_BITMAP_KEY + DateUtils.getCurrentDateStr(), userId, true);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 用户退出登录,将用户标记为离线
     *
     * @param userId 用户ID
     * @return 是否成功
     */
    @Override
    public boolean logout(String userId) {
        try {
            // ... 处理退出逻辑
            redisUtils.setBitWithFixedOffset(CommonConstant.LOGIN_BITMAP_KEY + DateUtils.getCurrentDateStr(), userId, false);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 获取当前在线用户数
     *
     * @return 在线用户数
     */
    @Override
    public long getOnlineCount() {
        return redisUtils.bitCount(CommonConstant.LOGIN_BITMAP_KEY + DateUtils.getCurrentDateStr());
    }

    /**
     * 获取昨天在线用户数
     *
     * @return 在线用户数
     */
    @Override
    public long getYesterdayOnlineCount() {
        return redisUtils.bitCount(CommonConstant.LOGIN_BITMAP_KEY + DateUtils.getYesterdayDateStr());
    }

    @Override
    public long getOnedayOnlineCount(String date) {
        // ... 日期类型判断
        return redisUtils.bitCount(CommonConstant.LOGIN_BITMAP_KEY + date);
    }


}

4、常量

/**
     * 登录人数key
     */
    public static final String LOGIN_BITMAP_KEY = "login:bitmap:";
    /**
     * 代表了二进制位的偏移范围最大值
     * 对于大多数情况下的紧凑编码字符串来说,MAX_BIT_OFFSET 的取值可以设置为字符串长度 * 8。
     */
    public static final Integer MAX_BIT_OFFSET = 32 * 8;

5、redis工具类

package com.lfsun.common.util;

import com.google.common.hash.Hashing;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import static com.lfsun.common.constant.CommonConstant.MAX_BIT_OFFSET;

@Component
public class RedisUtils {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    /**
     * Bitmap操作 - 设置指定偏移量的值
     *
     * @param key    缓存键
     * @param offset 偏移量
     * @param value  值,true表示1,false表示0
     * @return 原来偏移量上的值
     */
    public boolean setBit(String key, long offset, boolean value) {
        return redisTemplate.opsForValue().setBit(key, offset, value);
    }

    /**
     * Bitmap操作 - 获取指定偏移量的值
     *
     * @param key    缓存键
     * @param offset 偏移量
     * @return 值,true表示1,false表示0
     */
    public boolean getBit(String key, long offset) {
        return redisTemplate.opsForValue().getBit(key, offset);
    }

    /**
     * Bitmap操作 - 统计值为1的二进制位数量
     *
     * @param key 缓存键
     * @return 值为1的二进制位数量
     */
    public long bitCount(String key) {
        return redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount(key.getBytes()));
    }

    /**
     * 使用 MurmurHash 算法将用户 ID 转换为非负整数,并设置对应的位图值
     */
    public void setBitWithMurmurHash(String key, String userId, boolean value) {
        int bitOffset = Hashing.murmur3_32().hashBytes(userId.getBytes()).asInt() & 0x7fffffff;
        setBit(key, bitOffset, value);
    }

    /**
     * 在 Redis 中将指定位置 offset 上的二进制位设置为 value。
     *
     * @param key    Redis 键名
     * @param userId 用户id
     * @param value  要设置的值 true/false
     */
    public void setBitWithFixedOffset(String key, String userId, boolean value) {
        // MAX_BIT_OFFSET 是一个常量值,代表二进制位偏移范围的最大值。
        // 通过对 userId 字符串采用简单的哈希算法(hashCode()),然后使用 % 运算符取模运算得出固定长度的位偏移量,
        // 就可以在每次调用 setBitWithFixedOffset 方法时,针对该 userId 对应的固定位偏移量来精确地设置或清除相应的二进制位。
        int bitOffset = Math.abs(userId.hashCode()) % MAX_BIT_OFFSET;
        setBit(key, bitOffset, value);
    }
}

6、日期工具类

/**
     * 得到当前时间 yyyy-MM-dd 格式
     *
     * @return String
     */
    public static String getCurrentDateStr() {
        return getCurrentDate().format(DateTimeFormatter.ofPattern(DEFAULT_PATTERN));
    }

结果(apipost 测试)?

1、统计在线人数在这里插入图片描述

2、登录id用的mock的userid

在这里插入图片描述
在这里插入图片描述

3、由于userid是mock的,所以多点几次代表多个用户登录,去查看登录人数

在这里插入图片描述

4、退出需要调试后端看userid是什么,然后放到退出测试的参数里

在这里插入图片描述

5、再查看当前登陆人数(查看昨日登录人数同理)

在这里插入图片描述

6、查看某一日期登陆人数

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/433952.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

成功解决:NLTK包的安装错误

成功解决NLTK包的安装错误 创建环境NLP_Pre_train l环境 conda create -n NLP_Pre_train python3.8激活 conda activate NLP_Pre_train安装NLTK&#xff0c;套用以下命令 pip install NLTK之后是验证NLTK是否安装成功&#xff0c;采用的步骤如下所示&#xff1a; import n…

认识Java文件操作和IO流

文件操作&#xff08;一&#xff09; 文章目录 文件操作&#xff08;一&#xff09;文件的特性路径文件分类File类的使用文件的创建文件的删除文件创建加目录创建列出当前文件夹包含的文件文件重命名函数 文件的特性 我们所说的文件一般指的是硬盘文件&#xff0c;其实文件不止…

Minio对象存储服务介绍、单机和集群部署及其使用

Minio对象存储服务介绍、单机和集群部署及其使用 前言Minio简介Minio版本选择AGPL协议说明Minio优缺点优点缺点 Minio分server 和clientminio-client下载二进制文件查看 mc 版本 Minio分布式集群详细搭建基础环境操作系统Minio下载 准备工作1、机器资源2、创建相关目录3、创建启…

IOC容器与DI依赖注入示例

IOC容器与DI依赖注入示例 IOC深入理解IOC示例&#xff1a; DI深入理解DI示例 IOC深入理解 我们先通过几个问题来加深一下对IOC的理解 (1)Spring是使用IOC容器来管理bean对象的&#xff0c;我们主要管理什么? 主要管理项目中所使用到的类对象&#xff0c;比如(Service层对像和…

哈希表|STL使用

哈希表 时间复杂度为O(1) 拉链法 把X代表的数字映射到N所在区间&#xff0c;有可能会发生冲突俩个或多个数字映射到1个数 拉链法&#xff1a;把冲突的数字挂起来&#xff0c;用单链表挂起来 一般取模的数要取成质数&#xff0c;而且这个质数离二的n次幂要比较远&#xff0c;这样…

什么是深度学习?

目录 简介 深度学习的由来 深度学习未来的趋势 总结 简介 深度学习是在20世纪80年代被提出来的&#xff0c;主要是由加拿大的计算机科学家Geoffrey Hinton、Yoshua Bengio、Yann LeCun等人发起的。Geoffrey Hinton等人在经过多年的研究和实践之后&#xff0c;…

C语言的指针(进阶)

目录 数组指针 数组指针的使用 函数指针 函数指针数组 指向函数指针数组的指针 回调函数 数组指针 数组指针是指针&#xff1f;还是数组&#xff1f; 答案是&#xff1a;指针 数组指针也就是指向一个数组的指针 看下面两条代码&#xff1a; int *p1[10]; int (*p2)[10]; …

Manjaro安装clash-for-windows-bin

安装 安装Clash for Windows yay -S clash-for-windows-bin 配置 命令行进入~/.config/clash/ &#xff0c;配置文件 config.yaml 和 Country.mmdb wget -O config.yaml [订阅链接]wget -O Country.mmdb https://gitee.com/mirrors/Pingtunnel/raw/master/GeoLite2-Country.…

领导给了一千多个需求,这个排序法救了我的命……

什么是MoSCoW排序法&#xff1f; 莫斯科排序法是一种优先级排序法&#xff0c;用于管理需求、任务或功能列表。该方法可以帮助团队确定哪些需求、任务或功能是最重要的&#xff0c;并决定在特定时间段内是否需要完成它们。 所以在对需求进行排序时&#xff0c;可以从以下维度…

优思学院|六西格玛常见问题有哪些?

要实现高质量、高效率和高客户满意度的目标&#xff0c;许多企业采用了六西格玛方法。然而&#xff0c;在实施过程中&#xff0c;往往会遇到各种各样的问题。优思学院会在这里探讨一下几个六西格玛常见问题&#xff0c;并提供解决方案&#xff0c;以帮助企业成功实施六西格玛方…

JSONP数据劫持漏洞

​介绍 JSONP&#xff08;JSON with Padding&#xff09;是 json 的一种"使用模式"&#xff0c;可以让网页从别的域名&#xff08;网站&#xff09;那获取资料&#xff0c;即跨域读取数据&#xff1b;它利用的是script标签的 src 属性不受同源策略影响的特性&#x…

GoNote第二章 Moudles

Go Modules 1. 介绍 Go modules是官方提供的go包管理工具&#xff0c;用于解决go包管理和依赖问题&#xff1b;从1.11开始引入&#xff0c;到现在1.14已经比较完善&#xff0c;1.16已经全面推荐使用&#xff0c;并且默认为开启&#xff1b;Go Modules类似于JS的NPM&#xff0…

校园小助手【GUI/Swing+MySQL】(Java课设)

系统类型 Swing窗口类型Mysql数据库存储数据 使用范围 适合作为Java课设&#xff01;&#xff01;&#xff01; 部署环境 jdk1.8Mysql8.0Idea或eclipsejdbc 运行效果 本系统源码地址&#xff1a; 更多系统资源库地址&#xff1a;骚戴的博客_CSDN_更多系统资源 更多系统…

Apple iWork(Pages、Numbers、Keynote)13.0 - 文档、电子表格、演示文稿

请访问原文链接&#xff1a;https://sysin.org/blog/apple-iwork-13/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org 苹果今天将其专为 iOS 和 macOS 设备设计的 iWork 应用套件更新为版本 12 (sysin)&#xff0c;引入了许多新…

高并发服务器之泄峰

文章目录 背景前言解决方案泄峰 泄峰结果总结代码示例 背景 行业&#xff1a; 车联网机器配置&#xff1a;阿里云服务 8核 16G内存 3M带宽 阿里云操作系统单台server接入设备&#xff1a;5w终端产品&#xff1a;GPS定位设备终端与平台通信方式&#xff1a;TCP长链 前言 近期…

Linux中的五种IO模型

Linux中有以下五种IO模型 一、同步阻塞IO&#xff08;Blocking IO, BIO&#xff09; 用户进程发起IO调用后就阻塞线程让出CPU&#xff0c;等待内核处理完毕返回结果再唤醒继续执行。 二、同步非阻塞IO&#xff08;Non-Blocking IO, NIO&#xff09; 用户进程发起IO调用后就…

前端研发提质增效利器,TypeScirpt成功迁移详解

点击蓝字&#x1f446; 关注Agilean&#xff0c;获取一手干货 直播预告&#xff1a;Adapt 系列直播又双叒叕来啦&#xff01;最新一期我们将围绕「版本分支与环境」进行深入探讨&#xff0c;欢迎大家来直播间和主播互动哟&#xff5e; 点击下方右上角红色按钮「预约」&#x1f…

ESP32设备驱动-PAJ7620手势传感器驱动

PAJ7620手势传感器驱动 文章目录 PAJ7620手势传感器驱动2、硬件准备3、软件准备4、驱动实现PAJ7620 将手势识别功能与通用 I2C 接口集成到单个芯片中,形成图像分析传感器系统。 可识别上、下、左、右、前、后、顺时针、逆时针、挥手等9种人手手势。 它还提供内置的接近检测,以…

网络编程及项目思路

计算机和计算机之间通过网络进行数据传输 常见的软件架构&#xff1a; C/S:客户端/服务器 画面可以做的非常精美&#xff0c;用户体验好需要开发客户端&#xff0c;也需要开发服务端用户需要下载和更新的时候太麻烦 B/S:浏览器/服务器 不需要开发客户端&#xff0c;只需要…

java IO流_1

目录 分类 字节流 InputStream OutputStream 文件拷贝 字符流 FileReader FileWriter 处理流 BufferedReader BufferedWriter 文本拷贝 流是从起源到接受的有序数据&#xff0c;通过流的方式允许程序使用相同的方式来访问不同的输入/输出源。 分类 按数据…