Redis是一个NoSQL数据库,本文将实现一个用c++ API接口连接的例子,来实现对Redis数据库的写入和读出功能,具体将从Redis的安装,Redis的c++接口hiredis安装,代码演示三部分组成。
由于源中已有Redis的相关组件,这里就不进行源码编译而直接使用apt-get (ubuntu环境)下载和安装
1.redis的安装和配置
sudo apt-cache search redis //根据结果列表找到需要安装的软件包:redis-server
sudo apt-get install redis-server //安装软件
2.配置文件
whereis resids //查看redis位置: /etc/redis
cd /etc/redis //进入文件夹
/etc/redis$ ls -l //显示文件,其中redis.conf为配置文件
总用量 60
-rw-r----- 1 redis redis 41623 12月 19 2015 redis.conf
drwxr-xr-x 2 root root 4096 9月 21 10:47 redis-server.post-down.d
drwxr-xr-x 2 root root 4096 9月 21 10:47 redis-server.post-up.d
drwxr-xr-x 2 root root 4096 9月 21 10:47 redis-server.pre-down.d
drwxr-xr-x 2 root root 4096 9月 21 10:47 redis-server.pre-up.d
3.启动
服务端:redis-server (使用默认端口) (--port 6599 加端口)
客户端:redis-cli (连接之前测试启动指令 redis-cli ping 返回PONG启动成功)
4.关闭:redis-cli (-p 6380 可指定端口)shutdown
实际上hiredis是一个c的接口,同样使用apt-get安装hiredis,GitHub上有他的完整工程项目,点此转到。
sudo apt-cache search hiredis // 查看发现c语言开发库为libhiredis-dev
libhiredis-dbg - minimalistic C client library for Redis (debug)
libhiredis-dev - minimalistic C client library for Redis (development files)
libhiredis0.13 - minimalistic C client library for Redis
python-hiredis - redis protocol reader for Python 2.X using hiredis
sudo apt-get install libhiredis-dev //选择并安装
hiredis库目录的位置为默认的 /usr/lib/x86_64-linux-gnu/下,头文件在 /usr/include/hiredis 下,hiredis头文件中定义了Redis的连接的方式redisConnect()等方法,连接信息存储在上下文redisContext的结构体对象中,通过redisCommand()等方法进行具体的数据库存取指令操作并返回相关信息在redisReply的结构体对象中,不要忘了freeReplyObject(void *reply)释放redisReply连接响应对象,redisFree()函数释放redisContext上下文对象,具体的定义和方法请看以下代码。
#ifndef __HIREDIS_H
#define __HIREDIS_H
#include "read.h"
#include /* for va_list */
#include /* for struct timeval */
#include /* uintXX_t, etc */
#include "sds.h" /* for sds */
#define HIREDIS_MAJOR 0
#define HIREDIS_MINOR 13
#define HIREDIS_PATCH 3
#define HIREDIS_SONAME 0.13
#ifdef __cplusplus
extern "C" {
#endif
/* This is the reply object returned by redisCommand()
执行redis数据库指令操作的响应信息封装在redisReply的结构体中
*/
typedef struct redisReply {
int type; /* REDIS_REPLY_* */
long long integer; /* The integer when type is REDIS_REPLY_INTEGER */
int len; /* Length of string 存储字符串长度 */
char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING 错误信息和返回的string类型*/
size_t elements; /* number of elements, for REDIS_REPLY_ARRAY 如果为数组存储数组长度*/
struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY 存储数组元素向量*/
} redisReply;
redisReader *redisReaderCreate(void);
/* Function to free the reply objects hiredis returns by default.
释放响应对象
*/
void freeReplyObject(void *reply);
/* Functions to format a command according to the protocol.
数据库操作相关语句
*/
int redisvFormatCommand(char **target, const char *format, va_list ap);
int redisFormatCommand(char **target, const char *format, ...);
int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen);
int redisFormatSdsCommandArgv(sds *target, int argc, const char ** argv, const size_t *argvlen);
void redisFreeCommand(char *cmd);
void redisFreeSdsCommand(sds cmd);
enum redisConnectionType {
REDIS_CONN_TCP,
REDIS_CONN_UNIX,
};
/* Context for a connection to Redis
建立上下文连接对象的结构体
*/
typedef struct redisContext {
int err; /* Error flags, 0 when there is no error错误标志,0表示没有错误 */
char errstr[128]; /* String representation of error when applicable 错误声明 */
int fd;
int flags;
char *obuf; /* Write buffer */
redisReader *reader; /* Protocol reader */
enum redisConnectionType connection_type;
struct timeval *timeout; //设置连接等待时间
struct {
char *host;
char *source_addr;
int port;
} tcp;
struct {
char *path;
} unix_sock;
} redisContext;
//建立上下文连接
redisContext *redisConnect(const char *ip, int port);
redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv);
redisContext *redisConnectNonBlock(const char *ip, int port);
redisContext *redisConnectBindNonBlock(const char *ip, int port,
const char *source_addr);
redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port,
const char *source_addr);
redisContext *redisConnectUnix(const char *path);
redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv);
redisContext *redisConnectUnixNonBlock(const char *path);
redisContext *redisConnectFd(int fd);
/**
* Reconnect the given context using the saved information.
*
* This re-uses the exact same connect options as in the initial connection.
* host, ip (or path), timeout and bind address are reused,
* flags are used unmodified from the existing context.
*
* Returns REDIS_OK on successfull connect or REDIS_ERR otherwise.
*/
int redisReconnect(redisContext *c);
int redisSetTimeout(redisContext *c, const struct timeval tv);
int redisEnableKeepAlive(redisContext *c);
void redisFree(redisContext *c);
int redisFreeKeepFd(redisContext *c);
int redisBufferRead(redisContext *c);
int redisBufferWrite(redisContext *c, int *done);
/* In a blocking context, this function first checks if there are unconsumed
* replies to return and returns one if so. Otherwise, it flushes the output
* buffer to the socket and reads until it has a reply. In a non-blocking
* context, it will return unconsumed replies until there are no more. */
int redisGetReply(redisContext *c, void **reply);
int redisGetReplyFromReader(redisContext *c, void **reply);
/* Write a formatted command to the output buffer. Use these functions in blocking mode
* to get a pipeline of commands. */
int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len);
/* Write a command to the output buffer. Use these functions in blocking mode
* to get a pipeline of commands. */
int redisvAppendCommand(redisContext *c, const char *format, va_list ap);
int redisAppendCommand(redisContext *c, const char *format, ...);
int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
/* Issue a command to Redis. In a blocking context, it is identical to calling
* redisAppendCommand, followed by redisGetReply. The function will return
* NULL if there was an error in performing the request, otherwise it will
* return the reply. In a non-blocking context, it is identical to calling
* only redisAppendCommand and will always return NULL. */
void *redisvCommand(redisContext *c, const char *format, va_list ap);
void *redisCommand(redisContext *c, const char *format, ...);
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
#ifdef __cplusplus
}
#endif
#endif
在read.h文件中有redisReply结构体宏的定义,int type的类型:
#ifndef __HIREDIS_READ_H
#define __HIREDIS_READ_H
#define REDIS_ERR -1
#define REDIS_OK 0
#define REDIS_ERR_IO 1 /* Error in read or write */
#define REDIS_ERR_EOF 3 /* End of file */
#define REDIS_ERR_PROTOCOL 4 /* Protocol error */
#define REDIS_ERR_OOM 5 /* Out of memory */
#define REDIS_ERR_OTHER 2 /* Everything else... */
#define REDIS_REPLY_STRING 1 //存放在char *str
#define REDIS_REPLY_ARRAY 2
#define REDIS_REPLY_INTEGER 3 //integer存储为数据条数
#define REDIS_REPLY_NIL 4
#define REDIS_REPLY_STATUS 5 //成功状态码为:"OK" 存放在char *str
#define REDIS_REPLY_ERROR 6 //存放在char *str
如hiredis.c文件中看freeReplyObject函数对以上响应状态的处理:
/* Free a reply object
redisReply的类型有:REDIS_REPLY_INTEGER:
REDIS_REPLY_ERROR:
REDIS_REPLY_STATUS:
REDIS_REPLY_STRING:这三个都是返回字符串
*/
void freeReplyObject(void *reply) {
redisReply *r = reply;
size_t j;
if (r == NULL)
return;
switch(r->type) {
case REDIS_REPLY_INTEGER:
break; /* Nothing to free */
case REDIS_REPLY_ARRAY:
if (r->element != NULL) {
for (j = 0; j < r->elements; j++)
freeReplyObject(r->element[j]);
free(r->element);
}
break;
case REDIS_REPLY_ERROR:
case REDIS_REPLY_STATUS:
case REDIS_REPLY_STRING:
free(r->str);
break;
}
free(r);
}
代码结构如图所示:
具体代码如下:配置文件
#ifndef REDISCONFIG_H
#define REDISCONFIG_H
#include
class RedisConfig
{
public:
RedisConfig();
std::string getRedisIP();//获取ip
int getRedisPort();//获取端口号
};
#endif
#include "RedisConfig.h"
#include
RedisConfig::RedisConfig()
{
}
std::string RedisConfig::getRedisIP()
{
return "127.0.0.1";//设置为本机ip
}
int RedisConfig::getRedisPort()
{
return 6379;
}
具体处理代码:包含了string类型和list类型的存取代码,根据redisReply响应的信息来进行信息的处理,对于其他类型对响应状态的判断可以通过命令行操作来确定,如图1所示。
#ifndef _H_REDIS_TOOLS_
#define _H_REDIS_TOOLS_
#include
#include
#include
#include
using namespace std;
class RedisTool
{
public:
RedisTool();
~RedisTool();
int setString(string key, string value);
string getString(string key);
int setList(string key,vector value);
vector getList(string key);
private:
void init();
redisContext *m_redis;
RedisConfig m_config;
};
#endif
#include
#include
#include
#include
#include
RedisTool::RedisTool()
{
m_redis = NULL;
init();
}
RedisTool::~RedisTool()
{
if(m_redis != NULL)
{
redisFree(m_redis);//析构函数释放资源
cout << "~RedisTool :: free redis connection " << endl;
}
}
void RedisTool::init()
{
struct timeval timeout = { 1, 500000 }; // 1.5 seconds 设置连接等待时间
char ip[255];
strcpy(ip, m_config.getRedisIP().c_str());
cout << "init : ip = " << ip << endl;
m_redis = redisConnectWithTimeout(ip, m_config.getRedisPort(), timeout);//建立连接
if (m_redis->err) {
printf("RedisTool : Connection error: %s\n", m_redis->errstr);
}
else
{
cout << "init redis tool success " << endl;
//REDIS_REPLY响应的类型type
cout << "#define REDIS_REPLY_STRING 1"<< endl;
cout << "#define REDIS_REPLY_ARRAY 2"<< endl;
cout << "#define REDIS_REPLY_INTEGER 3"<< endl;
cout << "#define REDIS_REPLY_NIL 4"<< endl;
cout << "#define REDIS_REPLY_STATUS 5"<< endl;
cout << "#define REDIS_REPLY_ERROR 6"<< endl;
}
}
//向数据库写入string类型数据
int RedisTool::setString(string key, string value)
{
if(m_redis == NULL || m_redis->err)//int err; /* Error flags, 错误标识,0表示无错误 */
{
cout << "Redis init Error !!!" << endl;
init();
return -1;
}
redisReply *reply;
reply = (redisReply *)redisCommand(m_redis,"SET %s %s", key.c_str(), value.c_str());//执行写入命令
cout<<"set string type = "<type<str = NULL " << endl;
//pthread_spin_unlock(&m_redis_flock);
return -1;
}
else if(strcmp(reply->str, "OK") == 0)//根据不同的响应类型进行判断获取成功与否
{
result = 1;
}
else
{
result = -1;
cout << "set string fail :" << reply->str << endl;
}
freeReplyObject(reply);//释放响应信息
return result;
}
//从数据库读出string类型数据
string RedisTool::getString(string key)
{
if(m_redis == NULL || m_redis->err)
{
cout << "Redis init Error !!!" << endl;
init();
return NULL;
}
redisReply *reply;
reply = (redisReply *)redisCommand(m_redis,"GET %s", key.c_str());
cout<<"get string type = "<type<len <= 0)
{
freeReplyObject(reply);
return NULL;
}
else
{
stringstream ss;
ss << reply->str;
freeReplyObject(reply);
return ss.str();
}
}
//向数据库写入vector(list)类型数据
int RedisTool::setList(string key,vector value)
{
if(m_redis == NULL || m_redis->err)
{
cout << "Redis init Error !!!" << endl;
init();
return -1;
}
redisReply *reply;
int valueSize = value.size();
int result = 0;
for(int i=0; iinteger;
if(reply == NULL)
{
redisFree(m_redis);
m_redis = NULL;
result = -1;
cout << "set list fail : reply->str = NULL " << endl;
//pthread_spin_unlock(&m_redis_flock);
return -1;
}
else if(reply->integer == old++)
{
result = 1;
cout<<"rpush list ok"<integer = " << reply->integer << endl;
return -1;
}
}
freeReplyObject(reply);
cout<<"set List success"< RedisTool::getList(string key)
{
if(m_redis == NULL || m_redis->err)
{
cout << "Redis init Error !!!" << endl;
init();
return vector{};//返回空的向量
}
redisReply *reply;
reply = (redisReply*)redisCommand(m_redis,"LLEN %s", key.c_str());
int valueSize = reply->integer;
cout<<"List size is :"<integer<
main函数:
/*
* Author:wamgbaojia
* 2018.10.10
* email:[email protected]
*
* */
#include
#include
#include
using namespace std;
int main()
{
RedisTool redis;
//测试 string
redis.setString("wangbaojia","test1");
string result = redis.getString("wangbaojia");
cout<<"result="< vec ={1,2,3,4};
redis.setList("bao",vec);
vector vecResult = redis.getList("bao");
for(int i=0;i
运行结果:
点击下载