Skip to content

redis 使用示例 ​

以下片段假设已按 RedisPlugin 初始化 启动插件,并导入 nexus.io.redis.Redis 和 nexus.io.redis.RedisDb。除独立给出导入的示例外,省略重复 import;资源在应用关闭时统一释放。

使用示例 ​

示例 1:设置和获取字符串值 ​

java
public class StringValueExample {

    public static void main(String[] args) {
        // 获取 RedisDb 实例
        RedisDb redisCache = Redis.use();

        // 设置键值对
        redisCache.set("myKey", "myValue");

        // 获取值
        String value = redisCache.get("myKey");
        System.out.println("The value of 'myKey' is: " + value);

        // 设置带有过期时间的键值对
        redisCache.setex("expiringKey", 60, "expiringValue"); // 60秒后过期

        // 获取值
        String expiringValue = redisCache.get("expiringKey");
        System.out.println("The value of 'expiringKey' is: " + expiringValue);
    }
}

示例 2:操作整数值 ​

java
public class IntegerValueExample {

    public static void main(String[] args) {
        // 获取 RedisDb 实例
        RedisDb redisCache = Redis.use();

        // 设置整数值
        redisCache.setInt("intKey", 100);

        // 获取整数值
        Integer intValue = redisCache.getInt("intKey");
        System.out.println("The value of 'intKey' is: " + intValue);

        // 增加整数值
        redisCache.incrBy("intKey", 50);
        intValue = redisCache.getInt("intKey");
        System.out.println("The value of 'intKey' after increment is: " + intValue);

        // 减少整数值
        redisCache.decrBy("intKey", 30);
        intValue = redisCache.getInt("intKey");
        System.out.println("The value of 'intKey' after decrement is: " + intValue);
    }
}

示例 3:操作哈希表 ​

java
public class HashValueExample {

    public static void main(String[] args) {
        // 获取 RedisDb 实例
        RedisDb redisCache = Redis.use();

        // 设置哈希表字段
        redisCache.hset("user:1001", "name", "John Doe");
        redisCache.hset("user:1001", "age", 30);

        // 获取哈希表字段的值
        String name = redisCache.hget("user:1001", "name");
        Integer age = redisCache.hget("user:1001", "age");

        System.out.println("User 1001 Name: " + name);
        System.out.println("User 1001 Age: " + age);

        // 获取整个哈希表
        Map<String, Object> user = redisCache.hgetAll("user:1001");
        System.out.println("User 1001 Details: " + user);
    }
}

示例 4:将对象存入哈希表 ​

java
import java.util.ArrayList;
import java.util.List;

import nexus.io.jfinal.plugins.model.User;
import nexus.io.redis.Redis;
import nexus.io.redis.RedisPlugin;

public class HashDemoWithUser {

  public static void main(String[] args) {
    // 用于缓存bbs模块的redis服务
    RedisPlugin bbsRedis = new RedisPlugin("main", "192.168.3.9", 6379, 3000, "123456");
    bbsRedis.start();

    List<User> list = new ArrayList<>();
    list.add(new User("Tong Li", "0000000"));
    list.add(new User("Tong Li1", "0000000"));

    // set list
    Redis.use().hsetList("user", "001", list);
    // get list
    List<User> hgetList = Redis.use().hgetList("user", "001", User.class);
    System.out.println(hgetList.size());
    bbsRedis.stop();
  }
}

示例 5:操作列表 ​

java
public class ListValueExample {

    public static void main(String[] args) {
        // 获取 RedisDb 实例
        RedisDb redisCache = Redis.use();

        // 将元素压入列表头部
        redisCache.lpush("myList", "element1", "element2", "element3");

        // 从列表尾部弹出元素
        String element = redisCache.rpop("myList");
        System.out.println("Popped element from myList: " + element);

        // 获取列表中的所有元素
        List<String> elements = redisCache.lrange("myList", 0, -1);
        System.out.println("All elements in myList: " + elements);
    }
}

示例 6:检查键是否存在 ​

java
public class KeyExistenceExample {

    public static void main(String[] args) {
        // 获取 RedisDb 实例
        RedisDb redisCache = Redis.use();

        // 设置一个键值对
        redisCache.set("someKey", "someValue");

        // 检查键是否存在
        boolean exists = redisCache.hasKey("someKey");
        System.out.println("Does 'someKey' exist? " + exists);

        // 删除键
        redisCache.del("someKey");

        // 再次检查键是否存在
        exists = redisCache.hasKey("someKey");
        System.out.println("Does 'someKey' exist after deletion? " + exists);
    }
}

示例 7:发布和订阅消息 ​

java
public class PubSubExample {

    public static void main(String[] args) {
        // 获取 RedisDb 实例
        RedisDb redisCache = Redis.use();

        // 订阅一个频道
        new Thread(() -> {
            redisCache.subscribe(new JedisPubSub() {
                @Override
                public void onMessage(String channel, String message) {
                    System.out.println("Received message: " + message + " from channel: " + channel);
                }
            }, "myChannel");
        }).start();

        // 发布消息到频道
        redisCache.publish("myChannel", "Hello, Redis!");
    }
}

示例 8:使用 Redis 脚本执行操作 ​

java
public class ScriptExecutionExample {

    public static void main(String[] args) {
        // 获取 RedisDb 实例
        RedisDb redisCache = Redis.use();

        // Lua 脚本示例:获取一个键的值并设置一个新值
        String script = "local value = redis.call('get', KEYS[1]); " +
                        "redis.call('set', KEYS[2], ARGV[1]); " +
                        "return value;";

        // 执行 Lua 脚本
        Object result = redisCache.eval(script, 2, "key1", "key2", "newValue");
        System.out.println("Result of script execution: " + result);
    }
}

示例 9:使用扫描命令查找匹配的键 ​

java
public class ScanKeysExample {

    public static void main(String[] args) {
        // 获取 RedisDb 实例
        RedisDb redisCache = Redis.use();

        // 扫描匹配的键
        redisCache.scan(0, "user:*", 10, keys -> {
            System.out.println("Found keys: " + keys);
            return true; // 返回 true 继续扫描,返回 false 停止扫描
        });
    }
}

示例 10:获取随机元素(集合操作) ​

java
public class SetOperationsExample {

    public static void main(String[] args) {
        // 获取 RedisDb 实例
        RedisDb redisCache = Redis.use();

        // 添加元素到集合
        redisCache.sadd("mySet", "element1", "element2", "element3");

        // 获取集合中的随机元素
        String randomElement = redisCache.srandmember("mySet");
        System.out.println("Random element from mySet: " + randomElement);

        // 获取集合中的所有元素
        Set<String> elements = redisCache.smembers("mySet");
        System.out.println("All elements in mySet: " + elements);
    }
}

示例 11:为键设置过期时间 ​

java
public class ExpireKeyExample {

    public static void main(String[] args) {
        // 获取 RedisDb 实例
        RedisDb redisCache = Redis.use();

        // 设置键值对
        redisCache.set("tempKey", "tempValue");

        // 设置键的过期时间为 30 秒
        redisCache.expire("tempKey", 30);

        // 检查剩余时间
        Long ttl = redisCache.ttl("tempKey");
        System.out.println("TTL of 'tempKey': " + ttl + " seconds");
    }
}

示例 12 :使用 RedisDb.lock 进行分布式锁 ​

java
public class DistributedLockExample {

    public static void main(String[] args) {
        // 获取 RedisDb 实例
        RedisDb redisCache = Redis.use();

        // 定义锁的名称
        String lockName = "distributed_lock";

        // 锁的超时时间,单位:秒
        int lockExpireTime = 120; // 锁的过期时间为 120 秒

        // 获取锁的等待时间,单位:秒
        int lockTimeout = 5; // 如果在 5 秒内没有获取到锁,则放弃

        // 尝试获取锁
        String lockId = redisCache.lock(lockName, lockExpireTime, lockTimeout);

        if (lockId != null) {
            try {
                // 成功获取到锁,执行需要保护的业务逻辑
                System.out.println("成功获取锁,正在执行业务逻辑...");

                // 业务逻辑代码
                performBusinessLogic();

            } finally {
                // 释放锁
                redisCache.unlock(lockName, lockId);
                System.out.println("锁已释放。");
            }
        } else {
            // 未能获取到锁
            System.out.println("未能获取锁,可能另一个进程持有该锁。");
        }
    }

    private static void performBusinessLogic() {
        // 模拟业务逻辑的执行
        try {
            Thread.sleep(3000); // 假设业务逻辑需要 3 秒钟才能执行完毕
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println("业务逻辑执行成功。");
    }
}

代码说明 ​

  1. 获取 RedisDb 实例:

    • 通过 Redis.use() 获取 RedisDb 实例。
  2. 定义锁相关参数:

    • lockName: 锁的名称,通常与业务逻辑相关联。
    • lockExpireTime: 锁的过期时间,设置为 120 秒。
    • lockTimeout: 尝试获取锁的超时时间,设置为 5 秒。
  3. 尝试获取分布式锁:

    • 调用 redisCache.lock(lockName, lockExpireTime, lockTimeout) 尝试获取锁。
    • 如果成功获取到锁,返回 lockId;否则返回 null。
  4. 执行业务逻辑:

    • 在成功获取锁后,执行需要保护的业务逻辑。
  5. 释放锁:

    • 在业务逻辑执行完毕后,调用 redisCache.unlock(lockName, lockId) 释放锁。

注意事项 ​

  • 分布式锁通常用于确保在分布式环境下,多个进程不会同时执行同一段代码,从而避免资源竞争导致的数据不一致问题。
  • 在高并发场景下,设置合理的锁过期时间和获取锁的超时时间非常重要。

示例 13:使用 RedisDb 的 withLock 方法进行分布式锁 ​

以下是一个使用 RedisDb 的 withLock 方法进行分布式锁的示例。withLock 方法会自动处理锁的获取和释放逻辑,用户只需专注于需要加锁的业务逻辑。

java
public class DistributedLockWithLockMethodExample {

    public static void main(String[] args) {
        // 获取 RedisDb 实例
        RedisDb redisCache = Redis.use();

        // 定义锁的名称
        String lockName = "distributed_lock_with_withLock";

        // 锁的超时时间,单位:秒
        int lockExpireTime = 120; // 锁的过期时间为 120 秒

        // 获取锁的等待时间,单位:秒
        int lockTimeout = 5; // 如果在 5 秒内没有获取到锁,则放弃

        // 使用 withLock 方法执行加锁的业务逻辑
        boolean success = redisCache.withLock(lockName, lockExpireTime, lockTimeout, () -> {
            // 业务逻辑代码,这段代码在获取锁后执行
            System.out.println("成功获取锁,正在执行业务逻辑...");
            performBusinessLogic();
        });

        if (success) {
            System.out.println("业务逻辑执行完毕,锁已释放。");
        } else {
            System.out.println("未能获取锁,可能另一个进程持有该锁。");
        }
    }

    private static void performBusinessLogic() {
        // 模拟业务逻辑的执行
        try {
            Thread.sleep(3000); // 假设业务逻辑需要 3 秒钟才能执行完毕
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println("业务逻辑执行成功。");
    }
}

代码说明 ​

  1. 获取 RedisDb 实例:

    • 通过 Redis.use() 获取 RedisDb 实例。
  2. 定义锁相关参数:

    • lockName: 锁的名称,用于标识此锁。
    • lockExpireTime: 锁的过期时间,设置为 120 秒。
    • lockTimeout: 尝试获取锁的超时时间,设置为 5 秒。
  3. 使用 withLock 方法加锁执行业务逻辑:

    • 调用 redisCache.withLock(lockName, lockExpireTime, lockTimeout, () -> { ... }),并在 Lambda 表达式中编写需要保护的业务逻辑。
    • 如果成功获取锁并执行业务逻辑,则返回 true;否则返回 false。
  4. 锁的自动释放:

    • withLock 方法会在业务逻辑执行完毕后自动释放锁,因此不需要手动调用 unlock。

使用 withLock 方法的优点 ​

  • 简洁易用:withLock 方法封装了锁的获取、业务逻辑的执行和锁的释放,简化了代码。
  • 自动释放锁:业务逻辑结束或抛出异常后会在 finally 中尝试释放锁;连接故障、租期和释放原子性的限制见本页末尾。

防止重复提交 ​

先执行 SISMEMBER、再执行 SADD 是两条独立命令,并发请求可能同时通过检查。给整个集合设置 TTL 也不能实现每个设备独立的去重窗口。

以下方法使用同一条 SET 命令完成“键不存在才写入”和“120 秒后过期”。用户身份应来自服务端认证结果,设备标识应先校验;示例只展示请求占位逻辑。

java
import nexus.io.redis.Redis;
import redis.clients.jedis.params.SetParams;

public class SubmissionGuard {
  public boolean trySubmit(String userId, String equipmentId) {
    String key = "equipment:submitted:" + userId.length() + ":" + userId + ":" + equipmentId;
    return Redis.call(jedis -> "OK".equals(
        jedis.set(key, "1", new SetParams().nx().ex(120L))));
  }
}

返回 false 表示窗口内已提交;Redis 连接异常会向上抛出,应由调用方明确处理。该示例不会因为业务执行失败自动删除占位,也不能替代数据库唯一约束或完整的业务幂等状态记录。

分布式锁的实现边界 ​

本页 lock/withLock 示例用于解释 API。java-db 的 unlock 使用独立的 GET 和 DEL,比较令牌与删除之间存在竞争窗口;withLock 的 finally 会调用 unlock,但不会消除这一问题,也没有自动续租。因此,这些示例不能保证超时或故障情况下的互斥正确性。

需要令牌校验与删除原子执行时,可通过 Redis.call 执行 Lua,且必须同时评估锁租期、业务幂等与故障恢复。参见 Redis 官方分布式锁说明。

静态字符串 API:写入、读取与删除 ​

插件初始化后,可直接调用 nexus.io.redis.Redis:

java
String key = "demo:message";
String status = Redis.setStr(key, 60, "你好 Redis");
String value = Redis.getStr(key);
long deleted = Redis.del(key);
  • Redis.setStr(key, value) 写入不过期的字符串;Redis.setStr(key, seconds, value) 写入并原子设置有效期,单位为秒。
  • Redis.getStr(key) 读取原生字符串,键不存在或过期返回 null。
  • Redis.del(String key) 删除原生字符串键,存在并删除返回 1,不存在返回 0;重复删除返回 0。键编码与 setStr/getStr 一致,连接由现有连接池管理。
  • 这些静态方法使用默认缓存,不额外创建 RedisPlugin 或连接池。

单纯演示这些 API 的 Handler 可以直接调用上述方法并返回 RespBodyVo,不必为三次调用额外创建 Service。实际业务仍按业务职责组织 Service。HTTP 示例应保留必要的参数校验、鉴权和键命名空间,避免操作其他业务数据。

MIT Licensed