Redis Connector API 使用指南

本文档介绍如何在你的 Halo 插件中使用 Redis Connector 提供的 API。

添加依赖

1. 配置 Gradle

在你的插件 build.gradle 中添加:

dependencyResolutionManagement {
		repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
		repositories {
			mavenCentral()
			maven { url 'https://jitpack.io' }
		}
	}
	dependencies {
	        implementation 'com.github.acanyo:plugin-redis-connector:版本号'
	}

2. 声明插件依赖

plugin.yaml 中声明依赖:

spec:
  pluginDependencies:
    redis-connector: ">=1.0.0"

使用方式

方式一:静态 API(推荐)

使用 Redis 静态类,最简洁的调用方式:

import com.xhhao.redisconnector.api.Redis;

// 检查是否可用
if (Redis.isAvailable()) {
    // 执行 Redis 操作
}

方式二:注入 RedisClient

如果需要更灵活的控制,可以注入 RedisClient 接口:

import com.xhhao.redisconnector.api.RedisClient;

@Component
@RequiredArgsConstructor
public class MyService {
    
    private final RedisClient redisClient;
    
    public Mono<String> getData(String key) {
        if (!redisClient.isAvailable()) {
            return Mono.empty();
        }
        return redisClient.get(key);
    }
}

方式三:获取原始 JedisPool

当封装的 API 无法满足需求时,可以直接获取 JedisPool 进行操作:

import redis.clients.jedis.Jedis;

import redis.clients.jedis.JedisPool;

import redis.clients.jedis.Pipeline;

import redis.clients.jedis.Transaction;

import com.xhhao.redisconnector.api.Redis;

// 获取连接池

JedisPool pool = Redis.getJedisPool();

if (pool == null) {

    // Redis 未初始化

    return;

}

// ========== Pipeline 批量操作 ==========

try (Jedis jedis = pool.getResource()) {

    Pipeline pipeline = jedis.pipelined();

    for (int i = 0; i < 1000; i++) {

        pipeline.set("key:" + i, "value:" + i);

    }

    pipeline.sync(); // 一次性发送所有命令

}

// ========== 事务操作 ==========

try (Jedis jedis = pool.getResource()) {

    Transaction tx = jedis.multi();

    tx.set("account:1:balance", "100");

    tx.set("account:2:balance", "200");

    tx.incr("transaction:count");

    tx.exec(); // 原子执行

}

// ========== Lua 脚本 ==========

try (Jedis jedis = pool.getResource()) {

    // 原子性的「获取并删除」操作

    String script = "local val = redis.call('get', KEYS[1]); " +

                    "redis.call('del', KEYS[1]); " +

                    "return val";

    Object result = jedis.eval(script, 1, "mykey");

}

// ========== 发布订阅 ==========

try (Jedis jedis = pool.getResource()) {

    // 发布消息

    jedis.publish("channel:notifications", "Hello World");

}

// ========== List 操作 ==========

try (Jedis jedis = pool.getResource()) {

    jedis.lpush("queue:tasks", "task1", "task2", "task3");

    String task = jedis.rpop("queue:tasks"); // 从右侧弹出

    List<String> tasks = jedis.lrange("queue:tasks", 0, -1);

}

// ========== 分布式锁 ==========

try (Jedis jedis = pool.getResource()) {

    String lockKey = "lock:order:123";

    String lockValue = UUID.randomUUID().toString();

    

    // 获取锁(NX=不存在才设置,EX=过期时间秒)

    String result = jedis.set(lockKey, lockValue, SetParams.setParams().nx().ex(30));

    if ("OK".equals(result)) {

        try {

            // 执行业务逻辑

        } finally {

            // 释放锁(Lua 脚本保证原子性)

            String unlockScript = "if redis.call('get', KEYS[1]) == ARGV[1] then " +

                                  "return redis.call('del', KEYS[1]) else return 0 end";

            jedis.eval(unlockScript, 1, lockKey, lockValue);

        }

    }

}

// ========== Scan 遍历 ==========

try (Jedis jedis = pool.getResource()) {

    String cursor = "0";

    ScanParams params = new ScanParams().match("user:*").count(100);

    do {

        ScanResult<String> scanResult = jedis.scan(cursor, params);

        List<String> keys = scanResult.getResult();

        // 处理 keys

        cursor = scanResult.getCursor();

    } while (!"0".equals(cursor));

}
INFO

⚠️ 注意事项

必须使用 try-with-resources 确保 Jedis 实例被正确归还到连接池

在响应式环境中执行阻塞操作时,建议包装在 Mono.fromCallable(...).subscribeOn(Schedulers.boundedElastic()) 中

API 详解

字符串操作

// 设置值
Redis.set("key", "value").subscribe();

// 获取值
Redis.get("key").subscribe(value -> {
    System.out.println("Value: " + value);
});

// 设置值并指定过期时间(秒)
Redis.setEx("session:abc123", "user_data", 3600).subscribe();

// 删除键
Redis.del("key").subscribe(count -> {
    System.out.println("Deleted: " + count);
});

计数器

// 自增 1
Redis.incr("page:views").subscribe(newValue -> {
    System.out.println("Views: " + newValue);
});

// 自增指定值
Redis.incrBy("counter", 10).subscribe();

Hash 操作

适用于存储对象属性:

// 设置字段
Redis.hset("user:1001", "name", "张三").subscribe();
Redis.hset("user:1001", "email", "zhangsan@example.com").subscribe();

// 获取单个字段
Redis.hget("user:1001", "name").subscribe(name -> {
    System.out.println("Name: " + name);
});

// 获取所有字段
Redis.hgetAll("user:1001").subscribe(user -> {
    user.forEach((field, value) -> {
        System.out.println(field + ": " + value);
    });
});

Set 操作

适用于标签、去重等场景:

// 添加成员
Redis.sadd("post:1:tags", "java", "redis", "halo").subscribe();

// 获取所有成员
Redis.smembers("post:1:tags").subscribe(tags -> {
    tags.forEach(System.out::println);
});

// 判断是否是成员
Redis.sismember("post:1:tags", "java").subscribe(isMember -> {
    System.out.println("Is member: " + isMember);
});

Sorted Set 操作

适用于排行榜、优先级队列:

// 添加成员(带分数)
Redis.zadd("leaderboard", 1000, "player1").subscribe();
Redis.zadd("leaderboard", 2000, "player2").subscribe();
Redis.zadd("leaderboard", 1500, "player3").subscribe();

// 获取排行榜(按分数降序)
Redis.zrevrange("leaderboard", 0, 9).subscribe(top10 -> {
    System.out.println("Top 10:");
    top10.forEach(System.out::println);
});

// 增加分数
Redis.zincrby("leaderboard", 500, "player1").subscribe(newScore -> {
    System.out.println("New score: " + newScore);
});

通用操作

// 判断键是否存在
Redis.exists("key").subscribe(exists -> {
    System.out.println("Exists: " + exists);
});

// 设置过期时间(秒)
Redis.expire("key", 3600).subscribe();

// 获取剩余过期时间
Redis.ttl("key").subscribe(ttl -> {
    if (ttl == -1) {
        System.out.println("永不过期");
    } else if (ttl == -2) {
        System.out.println("键不存在");
    } else {
        System.out.println("剩余: " + ttl + " 秒");
    }
});

API 方法列表

方法

说明

返回值

isAvailable()

检查 Redis 是否可用

boolean

set(key, value)

设置字符串值

Mono<String>

setEx(key, value, seconds)

设置值并指定过期时间

Mono<String>

get(key)

获取字符串值

Mono<String>

del(key)

删除键

Mono<Long>

incr(key)

自增 1

Mono<Long>

incrBy(key, increment)

自增指定值

Mono<Long>

hset(key, field, value)

设置 Hash 字段

Mono<Long>

hget(key, field)

获取 Hash 字段

Mono<String>

hgetAll(key)

获取 Hash 所有字段

Mono<Map<String, String>>

sadd(key, members...)

添加 Set 成员

Mono<Long>

smembers(key)

获取 Set 所有成员

Mono<Set<String>>

sismember(key, member)

判断是否是 Set 成员

Mono<Boolean>

zadd(key, score, member)

添加 Sorted Set 成员

Mono<Long>

zrevrange(key, start, stop)

获取 Sorted Set(降序)

Mono<List<String>>

zincrby(key, increment, member)

增加 Sorted Set 分数

Mono<Double>

exists(key)

判断键是否存在

Mono<Boolean>

expire(key, seconds)

设置过期时间

Mono<Long>

ttl(key)

获取剩余过期时间

Mono<Long>

实际应用示例

文章浏览量统计

@Component
@RequiredArgsConstructor
public class PostViewService {
    
    public Mono<Long> incrementView(String postName) {
        if (!Redis.isAvailable()) {
            return Mono.just(-1L);
        }
        return Redis.incr("post:views:" + postName);
    }
    
    public Mono<String> getViewCount(String postName) {
        if (!Redis.isAvailable()) {
            return Mono.just("0");
        }
        return Redis.get("post:views:" + postName)
            .defaultIfEmpty("0");
    }
}

用户会话缓存

@Component
public class SessionService {
    
    private static final long SESSION_TTL = 7200; // 2小时
    
    public Mono<String> createSession(String userId, String sessionData) {
        String sessionId = UUID.randomUUID().toString();
        return Redis.setEx("session:" + sessionId, sessionData, SESSION_TTL)
            .thenReturn(sessionId);
    }
    
    public Mono<String> getSession(String sessionId) {
        return Redis.get("session:" + sessionId);
    }
    
    public Mono<Long> destroySession(String sessionId) {
        return Redis.del("session:" + sessionId);
    }
}

热门文章排行榜

@Component
public class HotPostService {
    
    public Mono<Long> recordView(String postName) {
        return Redis.zincrby("hot:posts:daily", 1, postName)
            .thenReturn(1L);
    }
    
    public Mono<List<String>> getHotPosts(int limit) {
        return Redis.zrevrange("hot:posts:daily", 0, limit - 1);
    }
}

注意事项

  1. 检查可用性:调用前建议先检查 Redis.isAvailable()

  2. 响应式编程:所有方法返回 Mono,需要 subscribe() 或在响应式链中使用

  3. 错误处理:操作失败时会返回默认值,不会抛出异常

  4. 键命名规范:建议使用 模块:类型:标识 的格式,如 post:views:123