基于Redis对用户数量进行统计——计数器系统的模拟实现

本文介绍了基于Redis实现用户数量统计的项目,涵盖了Redis的使用、类设计以及文件监听技术。项目中封装了Redis操作,如字符串、哈希的增删查改,并通过json文件管理计数器和动作,实现数据的动态更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、项目背景

Remote Dictionary Server (Redis),即远程字典服务,是使用ANSI C语言编写,支持网络、可基于内存亦可持久化的日志型的,Key-Value型的数据库,并提供多种语言的API。
它通常被称为数据结构服务器,因为值value可以是string、hash、list、set、sorted set等类型。应用场景:缓存、任务队列、排行榜、网站访问统计、数据过期处理、分布式集群架构中session分离。
本项目为非关系数据库课程的作业之一,利用了Redis对用户数量进行统计,在此基础上实现了周期性统计。
开发工具:IntelliJ IDEA

二、项目设计

2.1 类描述

2.1.1 JedisInstance.java

Jedis对象实例, 对外暴露了一个获取User对象的静态方法。

public class JedisInstance {
   
   
    //私有化构造函数
    private JedisInstance(){
   
    }

    //定义一个静态枚举类
    static enum SingletonEnum{
   
   
        //创建一个枚举对象,该对象天生为单例
        INSTANCE;
        private JedisPool jedisPool;
        //私有化枚举的构造函数
        private SingletonEnum(){
   
   
            JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxTotal(30);
            config.setMaxIdle(10);

            jedisPool = new JedisPool(config, "127.0.0.1", 6379);
        }
        public JedisPool getInstnce(){
   
   
            return jedisPool;
        }
    }
 
    //对外暴露一个获取User对象的静态方法
    public static JedisPool getInstance(){
   
   
        return SingletonEnum.INSTANCE.getInstnce();
    }
}

2.1.2 RedisUtil.java

Redis常用操作封装类,封装了value类型为string、hash、set、list、map的一些操作。该项目使用了简单的封装操作。
String:添加元素set,获取元素get,追加append,incr,decr,incrBy,decrBy
Hash:添加元素hset,获取元素hget,hincrBy
(项目中只用到了get、incrBy、decrBy、hincrBy、hget)
Set:添加元素sadd、删除元素srem、获得key对应的value总数scard、获得key对应的所有value smembers、判断set是否存在sismember、随机获取数据srandmember
List:添加元素lpush、获取list lrange、删除任意类型的key del
Map:设置map hmset、获取map的key个数hlen、获取map中所有的key hkeys、获取map中所有的value hvals、获取map中指定key的value hmget、获取map所有的key和value hgetAll、删除指定key的map。

public class RedisUtil {
   
   

    //获取连接
    private final Jedis jedis = JedisInstance.getInstance().getResource();

    //==================================string===================================

    //string的value在遇到incr, decr操作时会转成数值型进行计算
    //数值加1
    public void incr(String key){
   
   
        jedis.incr(key);
    }

    //数值减1
    public void decr(String key)
    {
   
   
        jedis.decr(key);
    }

    //数值加num
    public void incrBy(String key, long num){
   
   
        jedis.incrBy(key, num);
    }

    //数值减num
    public void decrBy(String key, long num)
    {
   
   
        jedis.decrBy(key, num);
    }

    //为string添加元素
    public void set(String key, String value) {
   
   
        jedis.set(key,value);
    }

    //获取string
    public String get(String key) {
   
   
        return jedis.get(key);
    }

    //追加string
    public void append(String key, String value) {
   
   
        jedis.append(key,value);
    }

    //==================================hash===================================

    //hash数值加num
    public void hincrBy(String key, String field, long num)
    {
   
   
        jedis.hincrBy(key, field, num);
    }

    //为hash添加元素
    public void hset(String key, String field, String value) {
   
   
        jedis.hset(key,field,value);
    }

    //获取hash元素
    public String hget(String key, String field) {
   
   
        return jedis.hget(key,field);
    }
    
### 使用 `RedisTemplate` 实现分布式计数器 为了实现一个可靠的分布式计数器,可以利用 Redis 的原子操作特性以及 Spring Data Redis 提供的 `RedisTemplate` 来完成。下面是一个具体的例子展示如何通过 Java 和 `RedisTemplate` 创建并管理分布式的每日访问次数计算器。 #### 配置 `RedisTemplate` 首先,在应用程序上下文中配置好 `RedisTemplate` 并指定合适的序列化方式: ```java @Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); Jackson2JsonRedisSerializer<Object> jacksonSer = new Jackson2JsonRedisSerializer<>(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jacksonSer.setObjectMapper(om); template.setValueSerializer(jacksonSer); template.afterPropertiesSet(); return template; } } ``` 此部分设置确保了键值存储时能够正确处理对象类型的序列化与反序列化[^1]。 #### 记录日志中的每次请求 每当有新的页面浏览发生时,拦截器会捕获该事件并将对应的 ID 发送到后台服务进行处理。这里定义了一个简单的自定义拦截器用于增加特定资源(比如文章ID)下的点击量: ```java @Configuration public class CustomInterceptor implements HandlerInterceptor { @Autowired private RedisCount redisCount; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String id = request.getParameter("id"); if (id != null && !id.isEmpty()) { redisCount.incr(id); } return true; } } ``` 这段代码展示了当接收到带有参数 `id` 的 HTTP 请求时,调用 `redisCount.incr()` 方法来进行增量更新[^4]。 #### 更新 Redis 中的数据 接下来是核心逻辑——实际执行增减操作的地方。这可以通过封装成独立的服务类来简化业务层的操作: ```java @Service public class RedisServiceImpl implements RedisService { @Autowired private RedisTemplate<String, Long> redisTemplate; /** * 对给定key做INCRBY操作,默认步长为1. */ @Override public void incr(String key) { ValueOperations<String, Long> opsForValue = redisTemplate.opsForValue(); opsForValue.increment(key, 1L); } /** * 设置过期时间以便于第二天自动清除旧数据。 */ @Override public void expireAtMidnight(String key) { ZonedDateTime now = ZonedDateTime.now(ZoneId.systemDefault()); ZonedDateTime tomorrowMidnight = now.plusDays(1).withHour(0).withMinute(0).withSecond(0).withNano(0); Duration durationUntilTomorrow = Duration.between(now, tomorrowMidnight); long secondsToExpire = durationUntilTomorrow.getSeconds(); redisTemplate.expire(key, secondsToExpire, TimeUnit.SECONDS); } } ``` 在此基础上,还可以进一步优化以适应更复杂的需求场景,例如支持批量写入、读取历史统计数据等功能[^3]。 #### 测试案例 最后编写单元测试验证功能是否正常工作: ```java @SpringBootTest(classes = Application.class) class RedisCounterTest { @Autowired private RedisService redisService; @Test void testIncrement() throws InterruptedException { final int COUNTS = 5; for(int i=0;i<COUNTS;i++){ redisService.incr("test_key"); } assertEquals(COUNTS, ((Number) redisTemplate.opsForValue().get("test_key")).longValue()); //模拟一天过去之后重新计算 Thread.sleep(TimeUnit.DAYS.toMillis(1)); assertNull(redisTemplate.opsForValue().get("test_key")); } } ``` 上述实现了基本的功能模块,并提供了相应的接口便于后续扩展和维护。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值