整合 Redis 统计在线人数
在 Spring Boot 中整合 Redis 统计在线人数可以通过 Redis 的 SET
或 ZSET
数据结构实现。以下是具体实现步骤:
添加依赖
在 pom.xml
文件中添加 Redis 相关依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
确保 Redis 配置在 application.properties
或 application.yml
中正确设置:
spring.redis.host=localhost
spring.redis.port=6379
实现在线人数统计
使用 SET
或 ZSET
存储用户唯一标识(如用户 ID 或 Session ID),并利用过期时间清理无效数据。
方法 1:使用 SET
存储用户标识
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
public class OnlineUserService {
private final StringRedisTemplate redisTemplate;
private static final String ONLINE_USERS_KEY = "online_users";
public OnlineUserService(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
// 用户上线
public void userOnline(String userId) {
redisTemplate.opsForSet().add(ONLINE_USERS_KEY, userId);
redisTemplate.expire(ONLINE_USERS_KEY, 30, TimeUnit.MINUTES); // 设置过期时间
}
// 用户下线
public void userOffline(String userId) {
redisTemplate.opsForSet().remove(ONLINE_USERS_KEY, userId);
}
// 获取在线人数
public long getOnlineUserCount() {
return redisTemplate.opsForSet().size(ONLINE_USERS_KEY);
}
}
方法 2:使用 ZSET
存储用户标识(带时间戳)
@Service
public class OnlineUserService {
private final StringRedisTemplate redisTemplate;
private static final String ONLINE_USERS_KEY = "online_users_zset";
public OnlineUserService(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
// 用户上线
public void userOnline(String userId) {
redisTemplate.opsForZSet().add(ONLINE_USERS_KEY, userId, System.currentTimeMillis());
redisTemplate.expire(ONLINE_USERS_KEY, 30, TimeUnit.MINUTES);
}
// 用户下线
public void userOffline(String userId) {
redisTemplate.opsForZSet().remove(ONLINE_USERS_KEY, userId);
}
// 获取在线人数(清除超时用户)
public long getOnlineUserCount() {
long timeout = System.currentTimeMillis() - 30 * 60 * 1000; // 30分钟超时
redisTemplate.opsForZSet().removeRangeByScore(ONLINE_USERS_KEY, 0, timeout);
return redisTemplate.opsForZSet().size(ONLINE_USERS_KEY);
}
}
结合 Web 拦截器
通过拦截器在用户访问时更新在线状态:
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class OnlineUserInterceptor implements HandlerInterceptor {
private final OnlineUserService onlineUserService;
public OnlineUserInterceptor(OnlineUserService onlineUserService) {
this.onlineUserService = onlineUserService;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String userId = getUserIdFromRequest(request); // 从请求中获取用户标识
if (userId != null) {
onlineUserService.userOnline(userId);
}
return true;
}
private String getUserIdFromRequest(HttpServletRequest request) {
// 实现从请求头、Session 或 Token 中提取用户 ID 的逻辑
return request.getSession().getId();
}
}
注册拦截器:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
private final OnlineUserInterceptor onlineUserInterceptor;
public WebConfig(OnlineUserInterceptor onlineUserInterceptor) {
this.onlineUserInterceptor = onlineUserInterceptor;
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(onlineUserInterceptor);
}
}
测试与优化
测试接口:创建一个测试接口返回在线人数:
@RestController @RequestMapping("/api/online") public class OnlineUserController { private final OnlineUserService onlineUserService; public OnlineUserController(OnlineUserService onlineUserService) { this.onlineUserService = onlineUserService; } @GetMapping("/count") public long getOnlineCount() { return onlineUserService.getOnlineUserCount(); } }
优化性能:
- 使用
ZSET
可以更精确地清理超时用户。 - 通过定时任务定期清理无效数据,避免实时清理的性能开销。
- 使用
注意事项
- 用户标识需唯一(如 Session ID 或用户 ID)。
- 超时时间根据业务需求调整。
- 高并发场景下可考虑分布式锁或 Lua 脚本保证原子性。