Bitmap 实现当前在线用户数量

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、查看某一日期登陆人数

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【CocosCreator入门】CocosCreator组件 | ProgressBar(进度条)组件

Cocos Creator 是一款流行的游戏开发引擎&#xff0c;具有丰富的组件和工具&#xff0c;其中的ProgressBar组件是一种用于实现进度条效果的重要组件。它可以让我们在游戏中展示各种进度条效果&#xff0c;例如加载进度条、血条等。 目录 一、组件介绍 二、组件属性 三、脚本…

12. 图的进阶

12. 图的进阶 12.1 有向图 在实际生活中&#xff0c;很多应用相关的图都是有方向性的&#xff0c;最直观的就是网络&#xff0c;可以从A页面通过链接跳转到B页面&#xff0c;那么a和b连接的方向是a->b,但不能说是b->a,此时我们就需要使用有向图来解决这一类问题&#x…

【jvm系列-09】垃圾回收底层原理和算法以及JProfiler的基本使用

JVM系列整体栏目 内容链接地址【一】初识虚拟机与java虚拟机https://blog.csdn.net/zhenghuishengq/article/details/129544460【二】jvm的类加载子系统以及jclasslib的基本使用https://blog.csdn.net/zhenghuishengq/article/details/129610963【三】运行时私有区域之虚拟机栈…

为什么许多人吐槽C++11,那些语法值得我们学习呢?

致前行的人&#xff1a; 人生像攀登一座山&#xff0c;而找寻出路&#xff0c;却是一种学习的过程&#xff0c;我们应当在这过程中&#xff0c;学习稳定冷静&#xff0c;学习如何从慌乱中找到生机。 目录 1.C11简介 2.统一的列表初始化 2.1 &#xff5b;&#xff5d;初始化 …

git 常用命令及遇到问题

自己没事&#xff0c;把git常用命令做个记录总结。方便自己和初学者查看&#xff0c;本文针对初学者&#xff0c;如果你已经是工作多年高手&#xff0c;请跳过。 git的几个区认识&#xff0c;分别为工作区&#xff0c;缓存区&#xff0c;版本库。 工作区&#xff1a;包含.git…

【Unity VR开发】结合VRTK4.0:添加碰撞忽略器

语录&#xff1a; 最远的旅行&#xff0c;是从自己的身体到自己的心&#xff0c;是从一个人的心到另一个人的心。坚强不是面对悲伤不流一滴泪&#xff0c;而是擦干眼泪后微笑面对以后的生活。 前言&#xff1a; 模块化提供了一种允许两个或者多个对象忽略彼此碰撞的方法&#x…

揭秘移动云大会展区前沿科技

2023年4月25日-26日 我们苏州金鸡湖国际会议中心见&#xff01; 1场重磅主论坛、10场分论坛、2600㎡展区 数字中国新未来 尽在2023移动云大会 2023移动云大会设有中国移动和合作伙伴两大展区&#xff0c;联合40余家优质合作伙伴&#xff0c;全方位展示移动云在自主能力、行…

vue yarn npm

2016年左右 &#xff0c;facebook针对npm包管理工具存在的性能问题进行了针对性开发并发布了yarn新的node包开发管理工具&#xff0c;具体对比&#xff0c;同学们自行网上搜索资料对比。 配置 1、先下载好NodeJS&#xff0c;然后输入如下命令安装yarn npm install -g yarn 2、…

如何微调Segment Anything Model

文章目录 什么是SAM&#xff1f;什么是模型微调&#xff1f;为什么要微调模型&#xff1f;如何微调 Segment Anything 模型背景与架构创建自定义数据集输入数据预处理训练设置循环训练保存检查点并从中启动模型 下游应用程序的微调 随着 Meta 上周发布的 Segment Anything Mode…

线程等待其他线程执行同步类CountDownLatch

文章目录 前言核心原理源码解析同步源码分析await源码分析countDown源码分析 实战演示1、创建演示代码2、创建测试用例3、测试结果演示 写在最后 前言 大家都知道多线程在我们实际编码过程中运用很多&#xff0c;很多情况我们需要靠多线程来提升系统性能。但是有些时候我们需要…

C语言开发环境搭建及调试

C简介 可移植 标准C C/C &#xff08;系统硬件操作的接口&#xff0c;windows&#xff0c;Linux不一样&#xff09; 跨平台 Java Python 下载 去官网选择Visual Studio 2019下载 安装过程中勾选使用C的桌面开发 安装好之后点击创建新项目——空项目 位置最好放在根目录下&…

【vue2】近期bug收集与整理02

⭐【前言】 在使用vue2构建页面时候&#xff0c;博主遇到的问题难点以及最终的解决方案。 &#x1f973;博主&#xff1a;初映CY的前说(前端领域) &#x1f918;本文核心&#xff1a;博主遇到的问题与解决思路 目录 ⭐数据枚举文件的使用⭐elementUI中分页组件使用的注意事项⭐…

OpenAI-ChatGPT最新官方接口《从0到1生产最佳实例》全网最详细中英文实用指南和教程,助你零基础快速轻松掌握全新技术(十一)(附源码)

Production Best Practices 生产最佳实例 前言Introduction 导言Setting up your organization 设置您的组织Managing billing limits 管理计费限额API keys API密钥Staging accounts 演示账户 Building your prototype 构建您的原型Additional tips 其它技巧 Techniques for i…

C++函数重载

目录 函数重载函数重载是怎样实现的 函数重载 函数重载&#xff1a;是函数的一种特殊情况&#xff0c;C允许在同一作用域中声明几个功能类似的同名函数&#xff0c;这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同&#xff0c;常用来处理实现功能类似数据类型不同的…

找PPT模板就上这5个网站~

分享几个可以永久免费下载PPT模板、素材的网站&#xff0c;上万个模板随便下载&#xff0c;赶紧收藏起来~ 1、菜鸟图库 https://www.sucai999.com/search/ppt/0_0_0_1.html?vNTYxMjky 网站素材非常全面&#xff0c;主要以设计类素材为主&#xff0c;办公类素材也很多&#x…

Spring MVC 接收 json 和返回 json (14)

目录 总入口 测试case 源码分析 1. 针对RequestBody的参数解析 2. 针对 ResponseBody 的返回值处理 总入口 通过上一篇Spring MVC 参数解析&#xff08;13&#xff09;_chen_yao_kerr的博客-CSDN博客的说明&#xff0c;相信大家对Sping MVC的参数解析有了一定的了解&…

8. 优先队列

8. 优先队列 普通的队列是一种先进先出的数据结构&#xff0c;元素在队列尾追加&#xff0c;而从队列头删除。在某些情况下&#xff0c;我们可能需要找出队列中的最大值或者最小值&#xff0c;例如使用一个队列保存计算机的任务&#xff0c;一般情况下计算机的任务都是有优先级…

【有功-无功协调优化】基于改进多目标粒子群优化算法(小生境粒子群算法)的配电网有功-无功协调优化研究(Matlab代码实现)

&#x1f4a5; &#x1f4a5; &#x1f49e; &#x1f49e; 欢迎来到本博客 ❤️ ❤️ &#x1f4a5; &#x1f4a5; &#x1f3c6; 博主优势&#xff1a; &#x1f31e; &#x1f31e; &#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 …

中断嵌套实验

使用汇编语言&#xff0c;要求&#xff1a; 外部中断1可以嵌套外部中断0 没有中断时&#xff0c;8个LED发光二极管以0.1s的速度闪烁。 有外部中断0时&#xff0c;8个LED发光二极管以0.1s的速度流水点亮。&#xff08;中断子程序0&#xff09; 有外部中断1时&#xff0c;会打断外…

gdb调试常用指令及案例讲解

文章目录 前言一、常用指令二、案例说明1、测试源文件2、编译和调试 三、其他指令四、案例说明 前言 GDB是一个由GNU开源组织发布的、UNIX/LINUX 操作系统下的、基于命令行的、功能强大的程序调试工具。 GDB 支持断点、单步执行、打印变量、观察变量、查看寄存器、查看堆栈等调…