处理命令回复
使用 hiredis
处理命令回复。
redisCommand()
和 redisCommandArgv()
函数在发出命令时返回指向 redisReply
对象的指针(详见发出命令)。此类型支持 RESP2 和 RESP3 协议中定义的所有回复格式,因此其内容在不同调用之间差异很大。
一个简单的例子是 SET
命令返回的状态回复。下面的代码展示了如何从 redisReply
对象中获取它。
redisReply *reply = redisCommand(c, "SET greeting Hello");
// Check and free the reply.
if (reply != NULL) {
printf("Reply: %s\n", reply->str);
freeReplyObject(reply);
reply = NULL;
}
null 回复表示错误,因此应始终进行检查。如果发生错误,则 redisContext
对象的整型 err
字段将包含非零错误码,其 errstr
字段将包含错误的文本描述。
对于 SET
命令,成功调用将简单地返回一个“OK”字符串,您可以使用 reply->str
字段访问它。示例中的代码将其打印到控制台,但您应检查特定值以确保命令正确执行。
redisCommand()
调用为回复分配内存,因此在使用完回复后应始终使用 freeReplyObject()
释放它。如果想重复使用回复变量,最好在释放后将其设置为 NULL
,以避免之后意外使用陈旧的指针。
回复格式
Redis 的 RESP
协议支持多种不同的命令回复格式。
您可以在命令参考页面的末尾的 RESP2/RESP3 回复部分找到命令的回复格式(例如,INCRBY
页面显示该命令返回一个整数结果)。您也可以使用回复对象的 type
字段来确定格式。此字段包含针对每种类型的不同整数值。hiredis.h
头文件为所有这些整数值定义了常量(例如 REDIS_REPLY_STRING
)。
redisReply
结构体有多个字段用于包含不同类型的回复,根据 type
字段的值设置不同的字段。下表显示了类型常量、相应的回复类型以及可用于访问回复值的字段。
常量 | 类型 | redisReply 的相关字段 |
RESP 协议 |
---|---|---|---|
REDIS_REPLY_STATUS |
简单字符串 | reply->str :字符串值 (char* )reply->len :字符串长度 (size_t ) |
2, 3 |
REDIS_REPLY_ERROR |
简单错误 | reply->str :字符串值 (char* )reply->len :字符串长度 (size_t ) |
2, 3 |
REDIS_REPLY_INTEGER |
整型 | reply->integer :整型值 (long long ) |
2, 3 |
REDIS_REPLY_NIL |
Null | 无数据 | 2, 3 |
REDIS_REPLY_STRING |
批量字符串 | reply->str :字符串值 (char* )reply->len :字符串长度 (size_t ) |
2, 3 |
REDIS_REPLY_ARRAY |
数组 | reply->elements :元素数量 (size_t )reply->element :数组元素 (redisReply ) |
2, 3 |
REDIS_REPLY_DOUBLE |
双精度浮点数 | reply->str :作为字符串的双精度浮点数值 (char* )reply->len :字符串长度 (size_t ) |
3 |
REDIS_REPLY_BOOL |
布尔值 | reply->integer :布尔值,0 或 1 (long long ) |
3 |
REDIS_REPLY_MAP |
Map | reply->elements :元素数量 (size_t )reply->element :数组元素 (redisReply ) |
3 |
REDIS_REPLY_SET |
Set | reply->elements :元素数量 (size_t )reply->element :数组元素 (redisReply ) |
3 |
REDIS_REPLY_PUSH |
Push | reply->elements :元素数量 (size_t )reply->element :数组元素 (redisReply ) |
3 |
REDIS_REPLY_BIGNUM |
大整数 | reply->str :作为字符串的数值 (char* )reply->len :字符串长度 (size_t ) |
3 |
REDIS_REPLY_VERB |
原样字符串 | reply->str :字符串值 (char* )reply->len :字符串长度 (size_t )reply->vtype :内容类型 (char[3] ) |
3 |
回复格式处理示例
以下部分将更详细地解释如何处理特定的回复类型。
整型
REDIS_REPLY_INTEGER
和 REDIS_REPLY_BOOL
回复类型都将值包含在 reply->integer
中。但是,REDIS_REPLY_BOOL
很少使用。即使命令本质上返回一个布尔值,回复通常也会报告为整型。
// Add some values to a set.
redisReply *reply = redisCommand(c, "SADD items bread milk peas");
if (reply->type == REDIS_REPLY_INTEGER) {
// Report status.
printf("Integer reply\n");
printf("Number added: %lld\n", reply->integer);
// >>> Number added: 3
}
freeReplyObject(reply);
reply = NULL;
reply = redisCommand(c, "SISMEMBER items bread");
// This also gives an integer reply but you should interpret
// it as a boolean value.
if (reply->type == REDIS_REPLY_INTEGER) {
// Respond to boolean integer value.
printf("Integer reply\n");
if (reply->integer == 0) {
printf("Items set has no member 'bread'\n");
} else {
printf("'Bread' is a member of items set\n");
}
// >>> 'Bread' is a member of items set
}
freeReplyObject(reply);
reply = NULL;
字符串
REDIS_REPLY_STATUS
、REDIS_REPLY_ERROR
、REDIS_REPLY_STRING
、REDIS_REPLY_DOUBLE
、REDIS_REPLY_BIGNUM
和 REDIS_REPLY_VERB
都以字符串形式返回,主要区别在于如何解释它们。对于所有这些类型,字符串值都在 reply->str
中返回,字符串长度在 reply->len
中。下面的示例展示了一些可能性。
// Set a numeric value in a string.
reply = redisCommand(c, "SET number 1.5");
// This gives a status reply.
if (reply->type == REDIS_REPLY_STATUS) {
// Report status.
printf("Status reply\n");
printf("Reply: %s\n", reply->str); // >>> Reply: OK
}
freeReplyObject(reply);
reply = NULL;
// Attempt to interpret the key as a hash.
reply = redisCommand(c, "HGET number field1");
// This gives an error reply.
if (reply->type == REDIS_REPLY_ERROR) {
// Report the error.
printf("Error reply\n");
printf("Reply: %s\n", reply->str);
// >>> Reply: WRONGTYPE Operation against a key holding the wrong kind of value
}
freeReplyObject(reply);
reply = NULL;
reply = redisCommand(c, "GET number");
// This gives a simple string reply.
if (reply->type == REDIS_REPLY_STRING) {
// Display the string.
printf("Simple string reply\n");
printf("Reply: %s\n", reply->str); // >>> Reply: 1.5
}
freeReplyObject(reply);
reply = NULL;
reply = redisCommand(c, "ZADD prices 1.75 bread 5.99 beer");
// This gives an integer reply.
if (reply->type == REDIS_REPLY_INTEGER) {
// Display the integer.
printf("Integer reply\n");
printf("Number added: %lld\n", reply->integer);
// >>> Number added: 2
}
freeReplyObject(reply);
reply = NULL;
reply = redisCommand(c, "ZSCORE prices bread");
// This gives a string reply with RESP2 and a double reply
// with RESP3, but you handle it the same way in either case.
if (reply->type == REDIS_REPLY_STRING) {
printf("String reply\n");
char *endptr; // Not used.
double price = strtod(reply->str, &endptr);
double discounted = price * 0.75;
printf("Discounted price: %.2f\n", discounted);
// >>> Discounted price: 1.31
}
freeReplyObject(reply);
reply = NULL;
数组和 Map
数组(回复类型 REDIS_REPLY_ARRAY
)和 Map(回复类型 REDIS_REPLY_MAP
)由同时检索多个值的命令返回。对于这两种类型,回复中的元素数量包含在 reply->elements
中,指向数组本身的指针是 reply->element
。数组中的每个项都是 redisReply
类型。数组元素通常是简单类型,而不是数组或 Map。
下面的示例展示了如何从 列表中 获取项。
reply = redisCommand(c, "RPUSH things thing0 thing1 thing2 thing3");
printf("Added %lld items\n", reply->integer);
// >>> Added 4 items
freeReplyObject(reply);
reply = NULL;
reply = redisCommand(c, "LRANGE things 0 -1");
for (int i = 0; i < reply->elements; ++i) {
if (reply->element[i]->type == REDIS_REPLY_STRING) {
printf("List item %d: %s\n", i, reply->element[i]->str);
}
}
// >>> List item 0: thing0
// >>> List item 1: thing1
// >>> List item 2: thing2
// >>> List item 3: thing3
Map 本质上与数组相同,但额外保证项将以键值对的形式列出。下面的示例展示了如何使用 HGETALL
从 哈希 中获取所有字段。
const char *hashCommand[] = {
"HSET", "details",
"name", "Mr Benn",
"address", "52 Festive Road",
"hobbies", "Cosplay"
};
reply = redisCommandArgv(c, 8, hashCommand, NULL);
printf("Added %lld fields\n", reply->integer);
// >>> Added 3 fields
freeReplyObject(reply);
reply = NULL;
reply = redisCommand(c, "HGETALL details");
// This gives an array reply with RESP2 and a map reply with
// RESP3, but you handle it the same way in either case.
if (reply->type == REDIS_REPLY_ARRAY) {
for (int i = 0; i < reply->elements; i += 2) {
char *key = reply->element[i]->str;
char *value = reply->element[i + 1]->str;
printf("Key: %s, value: %s\n", key, value);
}
// >>> Key: name, value: Mr Benn
// >>> Key: address, value: 52 Festive Road
// >>> Key: hobbies, value: Cosplay
}
错误处理
当命令成功执行时,上下文对象的 err
字段将被设置为零。如果命令失败,它将返回 NULL
或 REDIS_ERR
,具体取决于您使用的函数和命令。发生这种情况时,context->err
将包含错误码。
REDIS_ERR_IO
:在创建连接或尝试写入或读取数据时发生 I/O 错误。无论何时context->err
包含REDIS_ERR_IO
,您都可以使用标准库文件errno.h
的功能来查找有关错误的更多信息。REDIS_ERR_EOF
:服务器关闭了连接,导致读取为空。REDIS_ERR_PROTOCOL
:解析 RESP 协议时发生错误。REDIS_ERR_OTHER
:任何其他错误。目前仅在无法解析连接主机名时使用。
上下文对象还有一个 errstr
字段,其中包含描述性错误消息。