> 文章列表 > Bitmap 实现当前在线用户数量

Bitmap 实现当前在线用户数量

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 {@Autowiredprivate 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 {@Autowiredprivate RedisUtils redisUtils;/* 用户登录,将用户标记为在线 @param userId 用户ID* @return 是否成功*/@Overridepublic 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 是否成功*/@Overridepublic boolean logout(String userId) {try {// ... 处理退出逻辑redisUtils.setBitWithFixedOffset(CommonConstant.LOGIN_BITMAP_KEY + DateUtils.getCurrentDateStr(), userId, false);return true;} catch (Exception e) {return false;}}/* 获取当前在线用户数 @return 在线用户数*/@Overridepublic long getOnlineCount() {return redisUtils.bitCount(CommonConstant.LOGIN_BITMAP_KEY + DateUtils.getCurrentDateStr());}/* 获取昨天在线用户数 @return 在线用户数*/@Overridepublic long getYesterdayOnlineCount() {return redisUtils.bitCount(CommonConstant.LOGIN_BITMAP_KEY + DateUtils.getYesterdayDateStr());}@Overridepublic 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 {@Autowiredprivate 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、统计在线人数Bitmap 实现当前在线用户数量

2、登录id用的mock的userid

Bitmap 实现当前在线用户数量
Bitmap 实现当前在线用户数量

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

Bitmap 实现当前在线用户数量

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

Bitmap 实现当前在线用户数量

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

Bitmap 实现当前在线用户数量

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

Bitmap 实现当前在线用户数量

聋人网