Redis 统计在线人数

发布于:2025-08-13 ⋅ 阅读:(14) ⋅ 点赞:(0)

整合 Redis 统计在线人数

在 Spring Boot 中整合 Redis 统计在线人数可以通过 Redis 的 SETZSET 数据结构实现。以下是具体实现步骤:


添加依赖

pom.xml 文件中添加 Redis 相关依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

确保 Redis 配置在 application.propertiesapplication.yml 中正确设置:

spring.redis.host=localhost
spring.redis.port=6379


实现在线人数统计

使用 SETZSET 存储用户唯一标识(如用户 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);
    }
}


测试与优化

  1. 测试接口:创建一个测试接口返回在线人数:

    @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();
        }
    }
    

  2. 优化性能

    • 使用 ZSET 可以更精确地清理超时用户。
    • 通过定时任务定期清理无效数据,避免实时清理的性能开销。

注意事项

  • 用户标识需唯一(如 Session ID 或用户 ID)。
  • 超时时间根据业务需求调整。
  • 高并发场景下可考虑分布式锁或 Lua 脚本保证原子性。

网站公告

今日签到

点亮在社区的每一天
去签到