在Spring Boot里运用Redis统计用户在线数量。
项目依赖与配置
1. 引入依赖
首先,在pom.xml
文件中添加Spring Data Redis依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2. 配置Redis连接
在application.properties
中进行Redis连接的配置:
spring.redis.host=localhost
spring.redis.port=6379
方案1:借助Redis Set实现精确统计
1. 创建Redis操作Service
编写一个Redis操作Service,用于处理用户在线状态:
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Set;
@Service
public class OnlineUserService {
private static final String ONLINE_USERS_KEY = "online_users";
private final RedisTemplate<String, String> redisTemplate;
public OnlineUserService(RedisTemplate<String, String> redisTemplate) {
this.redisTemplate = redisTemplate;
}
// 用户登录
public void login(String userId) {
redisTemplate.opsForSet().add(ONLINE_USERS_KEY, userId);
}
// 用户退出
public void logout(String userId) {
redisTemplate.opsForSet().remove(ONLINE_USERS_KEY, userId);
}
// 获取在线用户数
public Long getOnlineCount() {
return redisTemplate.opsForSet().size(ONLINE_USERS_KEY);
}
// 获取所有在线用户ID
public Set<String> getOnlineUsers() {
return redisTemplate.opsForSet().members(ONLINE_USERS_KEY);
}
}
2. 控制器示例
创建一个控制器,用于测试上述功能:
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/online")
public class OnlineUserController {
private final OnlineUserService onlineUserService;
public OnlineUserController(OnlineUserService onlineUserService) {
this.onlineUserService = onlineUserService;
}
@PostMapping("/login/{userId}")
public String login(@PathVariable String userId) {
onlineUserService.login(userId);
return userId + " 已登录";
}
@PostMapping("/logout/{userId}")
public String logout(@PathVariable String userId) {
onlineUserService.logout(userId);
return userId + " 已退出";
}
@GetMapping("/count")
public Long getCount() {
return onlineUserService.getOnlineCount();
}
@GetMapping("/users")
public Set<String> getUsers() {
return onlineUserService.getOnlineUsers();
}
}
方案2:使用Redis Bitmap实现按位存储
1. Bitmap操作Service
创建一个专门用于Bitmap操作的Service:
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
@Service
public class OnlineUserBitmapService {
private static final String ONLINE_USERS_BITMAP_KEY = "online_users_bitmap";
private final RedisTemplate<String, Object> redisTemplate;
public OnlineUserBitmapService(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
// 用户登录(userId需为Long类型)
public void login(Long userId) {
redisTemplate.execute((RedisCallback<Boolean>) connection ->
connection.setBit(ONLINE_USERS_BITMAP_KEY.getBytes(), userId, true));
}
// 用户退出
public void logout(Long userId) {
redisTemplate.execute((RedisCallback<Boolean>) connection ->
connection.setBit(ONLINE_USERS_BITMAP_KEY.getBytes(), userId, false));
}
// 检查用户是否在线
public Boolean isOnline(Long userId) {
return redisTemplate.execute((RedisCallback<Boolean>) connection ->
connection.getBit(ONLINE_USERS_BITMAP_KEY.getBytes(), userId));
}
// 获取在线用户数
public Long getOnlineCount() {
return redisTemplate.execute((RedisCallback<Long>) connection ->
connection.bitCount(ONLINE_USERS_BITMAP_KEY.getBytes()));
}
// 统计指定范围内的在线用户数
public Long getOnlineCount(long start, long end) {
return redisTemplate.execute((RedisCallback<Long>) connection ->
connection.bitCount(ONLINE_USERS_BITMAP_KEY.getBytes(), start, end));
}
}
2. 控制器示例
创建对应的控制器:
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/online/bitmap")
public class OnlineUserBitmapController {
private final OnlineUserBitmapService onlineUserBitmapService;
public OnlineUserBitmapController(OnlineUserBitmapService onlineUserBitmapService) {
this.onlineUserBitmapService = onlineUserBitmapService;
}
@PostMapping("/login/{userId}")
public String login(@PathVariable Long userId) {
onlineUserBitmapService.login(userId);
return userId + " 已登录";
}
@PostMapping("/logout/{userId}")
public String logout(@PathVariable Long userId) {
onlineUserBitmapService.logout(userId);
return userId + " 已退出";
}
@GetMapping("/count")
public Long getCount() {
return onlineUserBitmapService.getOnlineCount();
}
@GetMapping("/{userId}")
public Boolean isOnline(@PathVariable Long userId) {
return onlineUserBitmapService.isOnline(userId);
}
}
使用建议
1. Set方案的适用场景
- 当需要精确统计在线用户数量,并且能够获取在线用户列表时,可以使用Set方案。
- 适合用户规模在百万级别以下的情况,因为Set会存储每个用户的ID。
2. Bitmap方案的适用场景
- 若用户ID是连续的整数(或者可以映射为连续整数),Bitmap方案会更节省内存。
- 对于大规模用户(比如亿级)的在线统计,Bitmap方案具有明显优势。
- 示例中使用
Long
类型的userId
,在实际应用中,你可能需要一个ID映射器,将业务ID转换为连续的整数。