Hiredis是Redis数据库一个轻量的C语言客户端库。它只是简单的提供了对redis操作语句支持的接口,并没有实现具体的操作语句的功能,因此可以很容易的使用该库和redis数据库进行交互。本文主要翻译自官方说明文档。
1. Hiredis编译
Hiredis是用C写的Redis客户端,对Redis协议进行了简单的封装。除了支持发送命令和接收应答外,Hiredis还提供了独立于I/O的数据流解析操作,用于解析应答数据。在Windows平台上使用Hiredis,一般需要将源码编译生成库文件然后进行调用。在Visual Studio上编译Hiredis项目的时候会出现无法生成lib的问题,其Github库的issues#687提供了一种解决方案,测试可行。
同步连接:服务器与第一个请求建立连接并通信以后,第二个请求会被阻塞。
异步连接:服务器可以同时响应多个请求
Hiredis提供了同步、异步以及回复解析三种API。
2. 同步API
常用的同步API的函数有:
1 | redisContext *redisConnect(const char *ip, int port); |
2.1 连接
Hiredis通过redisConnect
创建一个redisContext
来实现与Redis进行连接,context中包含了连接的信息。redisContext
中包含有一个整形的err
变量和一个字符类型的errstr
变量,当创建连接失败,err
为非零值,errstr
为错误的表述。当使用redisConnect
创建连接后,应该检查err
参数以判断连接是否成功。
1 | redisContext* context = redisConnect("127.0.0.1",6379) |
NOTE:redisContext
是线程不安全的。
线程安全:多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。
线程不安全:不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。
2.2 发送命令
有多种方法可以向Redis发送命令,这里介绍redisCommand
,其采用的是类似于printf
的格式。
1 | redisReply* reply; |
NOTE:Windows下redisCommand
返回的reply
与用redisReply
声明的的类型会不匹配,需要进行类型转换,reply = static_cast<redisReply*>(redisCommand(context, "GET key"));
。
2.3 处理应答
当redisCommand
执行命令成功以后,会返回一个redisReply
类型的返回值reply。当发生错误的时候,reply为NULL
且context中的err
的值会被改变。NOTE:一旦发生了错误,context就不能被重用,需要重新建立连接。
reply对象中有一个type
属性来标识不同的错误类型:
REDIS_REPLY_STATUS
:返回执行结果的状态,reply->str
获取状态的描述信息,reply->len
得到信息的长度。REDIS_REPLY_ERROR
:返回错误,通过reply->str
获取错误的描述信息。REDIS_REPLY_INTEGER
:返回整形标识,通过reply->integer
获得类型为long long
的整型值。REDIS_REPLY_NIL
:返回NIL
对象,说明不存在要访问的数据。REDIS_REPLY_STRING
:返回字符串标识,reply->str
获取返回的字符串的值,reply->len
得到字符串的长度。REDIS_REPLY_ARRAY
:返回包含多个reply的数据集,通过reply->elements
获取reply的个数,每一个reply可以通过reply->element[..index..]
索引得到。
NOTE:在执行完命令之后,必须要通过freeReplyObject()
函数将reply对象释放掉。对于数组或者嵌套数组中的sub-reply,不需要进行嵌套释放。
2.4 清理连接
断开连接并释放context:
1 | void redisFree(redisContext* context); |
redisFree
函数会关闭网络套接字并且释放所有在创建连接时分配的资源。
2.5 发送多个命令参数
redisCommandArgv
函数可以用于传输多个命令参数,其函数原型为:
1 | void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen); |
argc
表示参数的个数,argv
是存储命令字符串的数组,argvlen
为数组中每个元素的长度。为了方便可以将argvlen
设置为NULL
,函数将会使用strlen()
函数来判断每个参数的长度。为了保证参数的二进制安全,还是应该提供argvlen
的值。
2.6 管线
如果使用redisCommand
函数发送多次命令,需要每次发送后等待结果返回才能进行下一次发送。Redis的管线机制允许客户端一次性向服务端发送多个命令,Redis在接收到这些命令后按顺序进行处理,然后将请求的处理结果一次性返回给客户端。管线可以减少客户端与服务端之间的网络通信次数来提升Redis客户端发送多个命令时的性能。
当redisCommand
函数被调用,Hiredis先将命令格式化,被格式化的命令放入context的输出缓冲区(命令缓冲区),然后发送到Redis执行,并将结果返回到输入缓冲区(结果缓冲区)。Hiredis提供了redisAppendCommand()
函数来实现管线的命令发送方案。输出缓冲区是动态的,可以容纳任意数量的命令。
1 | void redisAppendCommand(redisContext *c, const char *format, ...); |
redisAppendCommand()
函数被执行后,命令缓存到context的输出缓冲区,并不会立刻发送到Redis执行。当redisGetReply()
被调用时,才会将输出缓冲区的命令一次性发送到Redis,并返回第一条命令的应答结果。redisGetReply()
的返回值为REDIS_ERR
或REDIS_OK
。redisGetReply()
的执行方式有两种:
- 输入缓冲区非空:
- 从出入缓冲区中解析一个reply并返回
- 如果没有reply可以被解析,执行2
- 输入缓冲区为空:
- 将整个输出缓冲区写入socket
- 从socket中读取命令直到有一个reply可以被解析
以下为一个简单的管线使用例程:
1 | redisReply *reply; |
redisGetReply()
也可以用来实现一个阻塞的订阅:
1 | reply = redisCommand(context,"SUBSCRIBE foo"); |
NOTE:调用redisAppendCommand()
函数的次数需要与调用redisGetReply()
的次数要一致,否则会出现获取的Redis处理结果跟预期不一致的情况。
2.7 错误处理
当函数调用不成功时,将会返回NULL
或REDIS_ERR
,context中的err
字段会设置为以下常量之一:
REDIS_ERR_IO
:I/O
错误,发生在创建连接时(尝试写入或者读取socket)。通过包含errno.h
可以获取详细的错误信息。REDIS_ERR_EOF
:服务端关闭了连接,导致读取为空。REDIS_ERR_PROTOCOL
:服务端解析协议时出错。REDIS_ERR_OTHER
:其他错误类型,仅在无法解析连接目标主机名时使用。
3. 异步API
// TODO
4. 回复解析API
Hiredis提供了回复解析API,可以轻松的与高级语言绑定。回复解析API函数有:
1 | redisReader *redisReaderCreate(void); |
//TODO
5. SSL/TLS支持
SSL(Secure Socket Layer,完全套接字层):用以保障在Internet上数据传输之安全,利用数据加密(Encryption)技术,可确保数据在网络上之传输过程中不会被截取。SSL协议位于TCP/IP协议与各种应用层协议之间,为数据通讯提供安全支持。
SSL协议可分为两层: SSL记录协议(SSL Record Protocol):它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。 SSL握手协议(SSL Handshake Protocol):它建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。
TLS(Transport Layer Security,传输层安全协议),用于两个应用程序之间提供保密性和数据完整性。
TLS协议由两层组成:TLS记录协议(TLS Record)和TLS握手协议(TLS Handshake)。较低的层为TLS 记录协议,位于某个可靠的传输协议(例如TCP)上面。