redis 使用示例
以下片段假设已按 RedisPlugin 初始化 启动插件,并导入 nexus.io.redis.Redis 和 nexus.io.redis.RedisDb。除独立给出导入的示例外,省略重复 import;资源在应用关闭时统一释放。
使用示例
示例 1:设置和获取字符串值
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:操作整数值
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:操作哈希表
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:将对象存入哈希表
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:操作列表
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:检查键是否存在
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:发布和订阅消息
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 脚本执行操作
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:使用扫描命令查找匹配的键
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:获取随机元素(集合操作)
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:为键设置过期时间
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 进行分布式锁
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("业务逻辑执行成功。");
}
}
代码说明
获取
RedisDb实例:- 通过
Redis.use()获取RedisDb实例。
- 通过
定义锁相关参数:
lockName: 锁的名称,通常与业务逻辑相关联。lockExpireTime: 锁的过期时间,设置为 120 秒。lockTimeout: 尝试获取锁的超时时间,设置为 5 秒。
尝试获取分布式锁:
- 调用
redisCache.lock(lockName, lockExpireTime, lockTimeout)尝试获取锁。 - 如果成功获取到锁,返回
lockId;否则返回null。
- 调用
执行业务逻辑:
- 在成功获取锁后,执行需要保护的业务逻辑。
释放锁:
- 在业务逻辑执行完毕后,调用
redisCache.unlock(lockName, lockId)释放锁。
- 在业务逻辑执行完毕后,调用
注意事项
- 分布式锁通常用于确保在分布式环境下,多个进程不会同时执行同一段代码,从而避免资源竞争导致的数据不一致问题。
- 在高并发场景下,设置合理的锁过期时间和获取锁的超时时间非常重要。
示例 13:使用 RedisDb 的 withLock 方法进行分布式锁
以下是一个使用 RedisDb 的 withLock 方法进行分布式锁的示例。withLock 方法会自动处理锁的获取和释放逻辑,用户只需专注于需要加锁的业务逻辑。
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("业务逻辑执行成功。");
}
}
代码说明
获取
RedisDb实例:- 通过
Redis.use()获取RedisDb实例。
- 通过
定义锁相关参数:
lockName: 锁的名称,用于标识此锁。lockExpireTime: 锁的过期时间,设置为 120 秒。lockTimeout: 尝试获取锁的超时时间,设置为 5 秒。
使用
withLock方法加锁执行业务逻辑:- 调用
redisCache.withLock(lockName, lockExpireTime, lockTimeout, () -> { ... }),并在 Lambda 表达式中编写需要保护的业务逻辑。 - 如果成功获取锁并执行业务逻辑,则返回
true;否则返回false。
- 调用
锁的自动释放:
withLock方法会在业务逻辑执行完毕后自动释放锁,因此不需要手动调用unlock。
使用 withLock 方法的优点
- 简洁易用:
withLock方法封装了锁的获取、业务逻辑的执行和锁的释放,简化了代码。 - 自动释放锁:业务逻辑结束或抛出异常后会在 finally 中尝试释放锁;连接故障、租期和释放原子性的限制见本页末尾。
防止重复提交
先执行 SISMEMBER、再执行 SADD 是两条独立命令,并发请求可能同时通过检查。给整个集合设置 TTL 也不能实现每个设备独立的去重窗口。
以下方法使用同一条 SET 命令完成“键不存在才写入”和“120 秒后过期”。用户身份应来自服务端认证结果,设备标识应先校验;示例只展示请求占位逻辑。
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:
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 示例应保留必要的参数校验、鉴权和键命名空间,避免操作其他业务数据。
