redis-sdk:仓颉原生Redis客户端,支持RESP2/3协议与集群模式

仓颉语言实现的Redis客户端SDK。接口设计兼容jedis接口语义,支持RESP2和RESP3协议,支持发布订阅模式,支持哨兵模式和集群模式。当前master分支适配仓颉1.0.0 LTS版本,分支Branch_cj0.53.18适配仓颉0.53.18 Beta版本。

分支5Tags3
文件最后提交记录最后更新时间
1 年前
1 年前
9 个月前
9 个月前
9 个月前
1 年前
2 年前
1 年前
9 个月前

仓颉Redis语言客户端

1 简介

这是一款原生为仓颉设计的Redis客户端。已针对仓颉1.0.0 LTS版本进行适配,依赖于Hyperion TCP框架,由北京宝兰德软件股份有限公司开发完成。API设计参照了以下项目:

用户手册请参阅:用户手册

功能特点

  1. 支持RESP2和RESP3协议
  2. 接口设计兼容jedis接口语义
  3. 提供丰富的管理命令支持
  4. 支持单连接多线程模式
  5. 支持发布订阅模式
  6. 支持哨兵模式和集群模式
  7. 完善的单元测试覆盖
  8. 架构简洁,便于扩展

支持的版本

  • Redis 7.x:目前已针对Redis 7.0.0——Redis 7.2.4版本完成兼容性测试
  • Redis 6.x:目前已针对Redis 6.0.0——Redis 6.2.14版本完成兼容性测试 其他版本的兼容性测试正在逐步完善中,敬请期待

仓颉SDK版本与分支对应关系

仓颉SDK版本 分支
1.0.0 LTS master
0.60.5 Branch_cj0.60.5
0.53.18 Beta Branch_cj0.53.18
0.45.2和0.51.4 Branch_cj0.51.4

工程目录结构

工程目录结构说明

|---samples  Redis客户端使用示例目录
|---src      Redis客户端源码目录  
|---test     Redis客户端单元测试目录
|     |---UT  通用命令的单元测试目录
      |---HLT 需要准备特殊环境的命令的单元测试
|---module.json
|---README.md

已实现的命令

BitMap数据类型操作命令

BITCOUNT, BITFIELD, BITFIELD_RO, BITOP, GETBIT, SETBIT, BITPOS

地理空间坐标

GEOADD, GEODIST, GEOHASH, GEOENCODE, GEODECODE, GEOPOS, GEORADIUS, GEORADIUS_RO, GEORADIUSBYMEMBER, GEORADIUSBYMEMBER_RO, GEOSEARCH, GEOSEARCHSTORE

Key命令

COPY, DEL, DUMP, EXISTS, EXPIRE, EXPIREAT, EXPIRETIME, MOVE, KEYS, MIGRATE, MEMORYUSAGE, OBJECTENCODING, OBJECTFREQ, OBJECTIDLETIME, OBJECTREFCOUNT, PERSIST, PEXPIRE, PEXPIREAT, PEXPIRETIME, PTTL, RANDOMKEY, RENAME, RENAMENX, RESTORE, SCAN, SORT, SORT_RO, TOUCH, TTL, TYPE, UNLINK

Hash数据类型操作命令

HDEL, HEXISTS, HGET, HGETALL, HINCRBY, HINCRBYFLOAT, HKEYS, HLEN, HMGET, HMSET, HRANDFIELD, HSCAN, HSET, HSETNX, HSTRLEN, HVALS

List数据类型操作命令

BLMOVE, BLMPOP, BLPOP, BRPOP, BRPOPLPUSH, LINDEX, LINSERT, LLEN, LMOVE, LMPOP, LPOP, LPOS, LPUSH, LPUSHX, LRANGE, LREM, LSET, LTRIM, RPOP, RPOPLPUSH, RPUSH, RPUSHX

Set数据类型操作命令

SADD, SCARD, SDIFF, SDIFFSTORE, SINTER, SINTERCARD, SINTERSTORE, SISMEMBER, SMEMBERS, SMOVE, SPOP, SRANDMEMBER, SREM, SSCAN, SUNION, SUNIONSTORE

Stream数据类型操作命令

XACK, XADD, XAUTOCLAIM, XCLAIM, XDEL, XGROUP CREATE, XGROUP CREATECONSUMER, XGROUP DELCONSUMER, XGROUP DESTROY, XGROUP SETID, XINFO CONSUMERS, XINFO GROUPS, XINFO STREAM, XLEN, XPENDING, XRANGE, XREVRANGE, XREAD, XREADGROUP, XTRIM

String数据类型操作命令

APPEND, DECR, DECRBY, GET, GETDEL, GETEX, GETRANGE, GETSET, INCR, INCRBY, INCRBYFLOAT, MGET, MSET, MSETNX, PSETEX, SET, SETEX, SETNX, SETRANGE, STRLEN, SUBSTR

ZSet数据类型操作命令

BZMPOP, BZPOPMAX, ZADD, ZCARD, ZCOUNT, ZDIFF, ZDIFFSTORE, ZINCRBY, ZINTER, ZINTERCARD, ZINTERSTORE, ZLEXCOUNT, ZMSCORE, ZMPOP, ZPOPMIN, ZPOPMAX, ZRANDMEMBER, ZRANGE, ZRANGEBYSCORE, ZRANGESTORE, ZRANK, ZREM, ZREMRANGEBYRANK, ZREMRANGEBYSCORE, ZREVRANGE, ZREVRANGEBYLEX, ZREVRANGEBYSCORE, ZREVRANK, ZSCAN, ZSCORE, ZUNION, ZUNIONSTORE, ZREMRANGEBYLEX, ZRANGEBYLEX

HyperLogLog命令

PFADD, PFCOUNT, PFMERGE

脚本和函数命令

EVAL, EVAL_RO, EVALSHA, EVALSHA_RO, SCRIPT EXISTS, SCRIPT FLUSH, SCRIPT KILL, SCRIPT LOAD

FCALL, FCALL_RO, FUNCTION DELETE, FUNCTION DUMP, FUNCTION FLUSH, FUNCTION KILL, FUNCTION LIST, FUNCTION LOAD, FUNCTION RESTORE, FUNCTION STATS

连接管理命令

AUTH, ECHO, PING, RESET

数据库命令

DBSIZE, FLUSHDB, FLUSHALL, SELECT, SWAPDB

访问控制列表命令

ACL CAT, ACL DELUSER, ACL DRYRUN, ACL GENPASS, ACL GETUSER, ACL LIST, ACL LOAD, ACL LOG, ACL SAVE, ACL SETUSER, ACL USERS, ACL WHOAMI

客户端命令

CLIENT GETNAME, CLIENT ID, CLIENT INFO, CLIENT KILL, CLIENT LIST, CLIENT NO-EVICT, CLIENT NO-TOUCH, CLIENT PAUSE, CLIENT SETINFO, CLIENT SETNAME, CLIENT UNBLOCK, CLIENT UNPAUSE

配置命令

CONFIG GET, CONFIG RESETSTAT, CONFIG REWRITE, CONFIG SET

服务管理命令

BGREWRITEAOF, BGSAVE, INFO, LASTSAVE, LATENCYDOCTOR, LOLWUT, REPLICAOF, SAVE, SHUTDOWN, SLAVEOF

其他

MEMORY DOCTOR, MEMORY PURGE, MEMORY STATS, ROLE, WAIT, WAITAOF

架构图标

2 架构

2.1 Redis命令处理模块的架构

Redis命令处理的架构图如下: 架构图

RedisCommand类

Redis命令的实现类 包含以下成员: commandType: Redis命令的名称 commandArgs: Redis命令的参数列表 response: Redis命令的响应消息,类型为RedisMessage exception: 执行Redis命令发生的异常

CommandArg类

封装Redis命令的基本类型参数,包括Array、String、Integer、Double

CommandArgs类

Redis命令的参数列表,包含CommandArg的列表

CompositeArgs类

封装Redis命令参数列表中存在关联关系的多个参数 调用buildArgs(CommandArgs)方法将多个存在关联的参数添加到CommandArgs的参数列表中

RedisMessage类

封装Redis命令的响应消息 RESP2协议对应的RedisMessage: SIMPLE_STRINGS: StringRedisMessage SIMPLE_ERRORS: ErrorRedisMessage INTEGERS: IntegerRedisMessage BULK_STRINGS: BulkStringRedisMessage ARRAYS: ArraysRedisMessage

RESP3协议对应的RedisMessage: NULLS: NullArraysRedisMessage BOOLEANS: BooleanRedisMessage DOUBLES: DoubleRedisMessage BIG_NUMBERS: BigNumberRedisMessage BULK_ERRORS: BulkErrorRedisMessage VERBATIM_STRINGS: VerbatimRedisMessage MAPS: MapRedisMessage SETS: SetRedisMessage PUSHES: PushesRedisMessage

ResponseBuilder类

将RedisMessage转换为命令执行的结果

基本数据类型的RepsonseBuilder实现: StringResponseBuilder IntegerResponseBuilder DoubleResponseBuilder BoolResponseBuilder NillableStringResponseBuilder

集合类型的RepsonseBuilder实现: ListResponseBuilder SetOfResponseBuilder PrimitiveMapResponseBuilder<String, T> ListOfAnyResponseBuilder MapOfAnyResponseBuilder

ParameterizedRedisCommand类

RedisCommand的子类,支持将Redis命令的响应构建为类型T,包含成员RepsonseBuilder用于处理响应 注意: 需要返回响应的命令,可以使用RedisCommand类, 也可以使用ParameterizedRedisCommand类; 不需要返回响应的命令,只能使用RedisCommand类

2.2 Redis编解码模块的架构

Redis编解码处理的架构图如下: 编解码架构图

MessageCompletedHandler接口

解码二进制报文时,判断二进制报文是否包含完整的消息报文

ByteToRedisMessageDecoder类

RESP协议解码器实现,将RESP协议的二进制报文转换为RedisMessage 该类实现了MessageCompletedHandler接口

RedisCommandToByteEncoder类

RESP协议编码器实现,将RedisCommand编码为RESP协议的二进制报文

RedisCodec类

实现了Hyperion TCP框架的IOFilter接口,处理Redis命令和Redis响应的编解码

RedisCommandHandler类

实现了Hyperion TCP框架的IOFilter接口,处理RedisMessage和RedisCommand的关联

RedisSubscriberHandler类

实现了Hyperion TCP框架的IOFilter接口,处理RedisMessage和Redis订阅者的关联 在未切换成订阅模式之前,处理方式和RedisCommandHandler类一致(处理认证和握手相关命令) 切换成订阅模式之后,只能执行以下命令: SUBSCRIBE channel [channel ...] PSUBSCRIBE pattern [pattern ...] UNSUBSCRIBE channel [channel ...] PUNSUBSCRIBE pattern [pattern ...] UNSUBSCRIBE PUNSUBSCRIBE PING PING message

RedisClientConfig

记录Redis客户端的参数配置

RedisClientBuilder类

用于创建RedisClient对象,RedisClient可以执行Redis命令

RedisClient类

执行Redis命令,并返回命令执行的结果

RedisSubscriberBuilder类

用于创建RedisSubscriber对象,RedisSubscriber可以订阅消息

RedisSubscriber类

订阅Redis频道的消息

2.3 编写Redis命令的实现

对于一个特定的数据类型,例如String类型,需要提供以下2个接口: StringCommands接口:定义String类型的操作命令对应的接口 StringCommansBuidldler接口:构建Sting类型相关命令对应的ParameterizedRedisCommand对象 需要实现以上2个接口: StringCommandsImpl类:提供String类型的操作命令对应的实现 StringCommansBuidlerImpl类:构建Sting类型相关命令对应的ParameterizedRedisCommand对象

RedisClient类需要实现StringCommands接口,并将String类型的相关方法委托给StringCommandsImpl类 命令实现图

String类型,支持超时参数的SET命令按如下方式构建(参考StringCommansBuidlerImpl类):

    public func set(key: String, value: String, params: SetParams): ParameterizedRedisCommand<?String> {
        let commandArgs = CommandArgs().add(StringArg(key)).add(StringArg(value))
        let command = ParameterizedRedisCommand<?String>(
            CommandType.SET,
            commandArgs,
            params,
            ResponseBuilderFactory.nillableStringBuilder
        )
        return command
    }

String 类型,支持超时参数的 SET 命令实现方式如下:

    public func set(key: String, value: String, params: SetParams): ?String {
        let redisCommand = RedisCommandBuilder.set(key, value, params)
        return executeCommand<String>(redisCommand)
    }

调用String类型,支持设置超时参数的SET命令:

    let client = RedisClientBuilder.builder()
       .host(servHost)
       .port(servPort)
       .password(servPass)
       .build()
    // 500秒后超时
    let result = client.set(myKey, myValue, 
       SetParams().exAt(DateTime.now().addSeconds(500).toUnixTimeStamp().toSeconds())

3 使用指南

3.1 通过源代码方式集成Redis客户端依赖

在使用仓颉版本0.60.5及以上时,若需引入Redis客户端依赖,必须设置环境变量CANGJIE_STDX_PATH,以明确指定stdx扩展包的存放路径。

以下为Windows X64平台设置环境变量的示例:

 SET CANGJIE_STDX_PATH=D:\cangjie-stdx-windows-x64-0.60.5.1\windows_x86_64_llvm\dynamic\stdx
 SET PATH=%CANGJIE_STDX_PATH%;%PATH%

Linux X64平台环境下环境变量设置示例:

 export CANGJIE_STDX_PATH= /opt/cangjie-stdx-linux-x64-0.60.5.1/linux_x86_64_llvm/dynamic/stdx
 export LD_LIBRARY_PATH=$CANGJIE_STDX_PATH:$LD_LIBRARY_PATH

仓颉1.0.0 LTS版本:在项目配置文件 cjpm.toml 中添加 dependencies 以引入 redis_sdk 依赖项:

[dependencies]
  redis_sdk = {git = "https://gitcode.com/Cangjie-TPC/redis-sdk.git", branch = "master", version = "3.0.0"}

仓颉0.53.18 Beta版:在项目的 cjpm.toml 文件中新增 dependencies 项以引入 redis_sdk 依赖。

[dependencies]
  redis_sdk = {git = "https://gitcode.com/Cangjie-TPC/redis-sdk.git", branch = "Branch_cj0.53.18", version = "2.0.0"}

仓颉版本0.45.2与0.51.4,请参照Branch_cj0.51.4分支中的说明。

更新依赖时,执行cjpm update命令将自动下载redis_sdk项目的依赖至~/.cjpm目录下: $> cjpm update

3.2 编译Redis仓颉语言客户端并导入静态库依赖

编译Redis仓颉语言客户端,请参阅:用户手册

您可以选择引入预编译的静态库依赖,或者通过源码方式引入依赖,任选其一。具体操作请参考"1.1 通过源码方式引入Redis客户端依赖"。

对于仓颉0.53.4及以上版本,您需要首先确认目标平台对应的target-name

例如,在Windows X64平台上执行cjc -v命令,将返回以下信息:

$cjc -v
Cangjie Compiler: 0.53.4 (cjnative)
Target: x86_64-w64-mingw32

例如在Linux X64平台上执行cjc -v命令,返回结果如下:

$cjc -v
Cangjie Compiler: 0.53.4 (cjnative)
Target: x86_64-unknown-linux-gnu

在仓颉 0.53.4 版本中,需要在项目的 cjpm.toml 文件中添加与平台对应的二进制依赖项。以 Linux X64 平台为例:

[target.x86_64-unknown-linux-gnu.bin-dependencies.package-option]
  "hyperion.buffer" = "${path_to_redis_sdk}/build/release/hyperion/hyperion.buffer.cjo"
  "hyperion.logadapter" = "${path_to_redis_sdk}/build/release/hyperion/hyperion.logadapter.cjo"
  "hyperion.objectpool" = "${path_to_redis_sdk}/build/release/hyperion/hyperion.objectpool.cjo"
  "hyperion.threadpool" = "${path_to_redis_sdk}/build/release/hyperion/hyperion.threadpool.cjo"
  "hyperion.transport" = "${path_to_redis_sdk}/build/release/hyperion/hyperion.transport.cjo"
  "redis_sdk.client.api" = "${path_to_redis_sdk}/build/release/redis_sdk/redis_sdk.client.api.cjo"
  "redis_sdk.client.commands" = "${path_to_redis_sdk}/build/release/redis_sdk/redis_sdk.client.commands.cjo"
  "redis_sdk.client.commands.impl" = "${path_to_redis_sdk}/build/release/redis_sdk/redis_sdk.client.commands.impl.cjo"
  "redis_sdk.client" = "${path_to_redis_sdk}/build/release/redis_sdk/redis_sdk.client.cjo"

请参考以下版本:仓颉0.45.2与0.51.4。具体说明,请查阅Branch_cj0.51.4分支。

3.3 创建RedisClient以执行Redis指令集

import redis_sdk.client.api.*
import redis_sdk.client.commands.*
import redis_sdk.client.*

public func getRedisClient(): RedisClient {
    let redisClient = RedisClientBuilder.builder()
        .host("127.0.0.1")
        .port(6379)
        .readTimeout(Duration.second * 60)
        .writeTimeout(Duration.second * 30)
        .receiveBufferSize(32768)
        .sendBufferSize(32768)
        .build()
    return redisClient
}

3.4 利用RedisClient进行Redis命令的操作执行

import redis_sdk.client.api.*
import redis_sdk.client.commands.*
import redis_sdk.client.*

main() {
    // 获取Redis客户端
    let client = getRedisClient()
    // 执行 SET testKey1 testValue1 XX命令
    let result = client.set("testKey1", "testValue1", SetParams().xx())
    if (let Some(result) <- result) {
        println(result)
    } else {
        println("nil")
    }

    // 执行 GET testKey1命令
    var getResult = client.get("testKey1")
    if (let Some(getResult) <- getResult) {
        println(getResult)
    } else {
        println("nil")
    }
}

4 参与贡献

redis-sdk项目由北京宝兰德软件股份有限公司的中间件团队负责实现与维护。如需技术支持或意见反馈,请通过Issue提出。

本项目遵循Apache License 2.0协议,我们欢迎您提交PR或以任何形式参与贡献力量。

本项目的commiter为:@hapinwater

项目介绍

仓颉语言实现的Redis客户端SDK。接口设计兼容jedis接口语义,支持RESP2和RESP3协议,支持发布订阅模式,支持哨兵模式和集群模式。当前master分支适配仓颉1.0.0 LTS版本,分支Branch_cj0.53.18适配仓颉0.53.18 Beta版本。

定制我的领域