在菜鳥教程自學了redis,總想著像Mysql一樣,在C/C++中進行對接。於是查詢了一些資料,最後找到了hiredis。然而直接用它的話,難免有點不方便。於是,對其進行封裝。
hiredis直接去git上克隆,地址:https://github.com/redis/hiredis。
下載好之後,由於其自帶Makefile,只要make一下就編譯出靜態庫與動態庫了,接著把頭文件和靜/動態庫放在相應的文件夾裡就可以了。注意如果使用動態庫,而且是放在/usr/local/lib/裡,得執行ldconfig命令,來更新一下配置,或者得配置一下動態庫路徑。
安裝好了就是如何使用的事了。
學習hiredis主要是參考這兩個鏈接:
http://blog.csdn.net/gqtcgq/article/details/51344232
http://blog.csdn.net/achelloworld/article/details/41598389?utm_source=tuicool&utm_medium=referral
一共就五個函數。
1、redisContext* redisConnect(const char *ip, int port)
2、redisContext* redisConnectWithTimeout(const char *ip, int port, timeval tv)
3、void redisFree(redisContext *c)
4、void *redisCommand(redisContext *c, const char *format...)
5、void freeReplyObject(void *reply)
和Mysql一樣,要對接,第一件事就是用IP和端口號建立連接什麼的。redis的端口號一般是6379,IP直接用127.0.0.1就可以了。既然要用到IP和端口號,又是可能會變的東西,為了不使想要改變它們的時候得直接修改代碼,我寫了個配置文件:
redisConf.json
1 { 2 "IP" : "127.0.0.1" , 3 "PORT" : 6379 4 }
相應地,有提取配置信息的類
redisConf.h
1 #ifndef __REDISCONF_H__ 2 #define __REDISCONF_H__ 3 #include <string> 4 namespace ccx{ 5 using std::string; 6 class RedisConf 7 { 8 public: 9 RedisConf(); 10 void getConf(); 11 string getIP(); 12 int getPort(); 13 private: 14 string _ip; 15 int _port; 16 }; 17 } 18 #endif
redisconf.cc
1 #include "redisConf.h" 2 #include <stdlib.h> 3 #include <json/json.h> 4 #include <string> 5 #include <iostream> 6 #include <fstream> 7 8 namespace ccx{ 9 10 using std::ifstream; 11 using std::cout; 12 using std::endl; 13 14 RedisConf::RedisConf() 15 { 16 getConf(); 17 } 18 19 void RedisConf::getConf() 20 { 21 ifstream ifs; 22 ifs.open("redisConf.json"); 23 if(!ifs.good()) 24 { 25 cout << "open RedisConf.json error" << endl; 26 exit(EXIT_FAILURE); 27 } 28 29 Json::Value root; 30 Json::Reader reader; 31 if(!reader.parse(ifs, root, false)) 32 { 33 cout << "RedisConf json reader error" << endl; 34 exit(EXIT_FAILURE); 35 } 36 37 _ip = root["IP"].asString(); 38 _port = root["PORT"].asInt(); 39 ifs.close(); 40 } 41 42 string RedisConf::getIP() 43 { 44 return _ip; 45 } 46 47 int RedisConf::getPort() 48 { 49 return _port; 50 } 51 52 }
然後是目前的redis類:
redis.h
1 #ifndef __REDIS_H__ 2 #define __REDIS_H__ 3 4 #include "redisConf.h" 5 6 #include <hiredis/hiredis.h> 7 8 9 namespace ccx{ 10 11 class Redis 12 { 13 public: 14 Redis(); 15 public: 16 void Connect(); 17 void disConnect(); 18 public: 19 void setString(const string & key, const string & value); 20 void setString(const string & key, const int & value); 21 void setString(const string & key, const float & value); 22 private: 23 void setString(const string & data); 24 public: 25 void getString(const string & key, string & value); 26 void getString(const string & key, int & value); 27 void getString(const string & key, float & value); 28 private: 29 void getString(const string & key); 30 private: 31 void freeReply(); 32 bool isError(); 33 private: 34 RedisConf _conf; 35 redisContext * _context; 36 redisReply * _reply; 37 }; 38 } 39 40 #endif
下面結合寫好的代碼說說前面的五個函數。
函數1是用來連接redis的,具體如下:
1 void Redis::Connect() 2 { 3 _context = ::redisConnect(_conf.getIP().c_str(), _conf.getPort()); 4 cout << _conf.getIP() << "-" << _conf.getPort() << endl; 5 if(_context && _context->err) 6 { 7 cout << "connect redis error" << endl; 8 exit(EXIT_FAILURE); 9 } 10 cout << "redis Connect success" << endl; 11 }
函數2是在1的基礎上,添加了一個超時功能。
函數3是在不使用redis了,要斷開連接時使用的:
1 void Redis::disConnect() 2 { 3 ::redisFree(_context); 4 cout << "redis disConnect success" << endl; 5 }
函數4稍微復雜一些,有點像C中的printf:
1 printf("%d%s%d",d1,s1,d2); 2 printf("hello,world");
可以這樣用:
1 char * command = "SET name lili"; 2 reply = (redisReply*)::redisCommand(context, command); 3 char * s1 = "name"; 4 char * s2 = "lili"; 5 reply = (redisReply*)::redisCommand(context, "SET %s %s", s1, s2); 6 reply = (redisReply*)::redisCommand(context, "SET name lili");
7 ...
第一個參數context是函數1或者2的返回值,告訴它要與哪裡的redis進行交互。reply指向命令結果的存儲位置。
函數5是用來清理函數4 的返回結果的:
1 void Redis::freeReply() 2 { 3 if(_reply) 4 { 5 ::freeReplyObject(_reply); 6 _reply = NULL; 7 } 8 }
第6行是因為對這個函數不熟,就干脆清完之後給它賦值NULL。
由於redis的string中存的可能是字符串、整形、浮點數,於是各自重載了三個版本的get與set方法,並重用一些函數,以減少代碼量。
對於set,直接用一個宏替換:
1 #define SETSTRING(key, value) \ 2 stringstream ss;\ 3 ss << "SET " << key << " " << value;\ 4 string s;\ 5 getline(ss, s);\ 6 setString(s);
1 void Redis::setString(const string & key, const string & value) 2 { 3 SETSTRING(key, value); 4 } 5 void Redis::setString(const string & key, const int & value) 6 { 7 SETSTRING(key, value); 8 } 9 void Redis::setString(const string & key, const float & value) 10 { 11 SETSTRING(key, value); 12 }
使用C++中的stringstream,會比用“%d”、“%s”、“%f”來區分類型少些代碼。兩種方法的結果是相同的。
它們共用的setString方法:
1 void Redis::setString(const string & data) 2 { 3 freeReply(); 4 _reply = (redisReply*)::redisCommand(_context, data.c_str()); 5 if(!isError()) 6 { 7 if (!(_reply->type == REDIS_REPLY_STATUS && strcasecmp(_reply->str,"OK") == 0)) 8 { 9 cout << "Failed to execute SET(string)" << endl; 10 } 11 } 12 }
這裡的isError是用來判斷是否連接異常的:
1 bool Redis::isError() 2 { 3 if(NULL == _reply) 4 { 5 freeReply(); 6 disConnect(); 7 Connect(); 8 return true; 9 } 10 return false; 11 }
如果連接異常,得斷開重連。
在redis命令行裡,如果set成功,會提示“OK”。於是,這裡先判斷了一下命令結果的數據類型,如果是字符串,再判斷它是不是“OK”,以此來判斷set是否成功。
對於get,我試了各種方法,都無法直接從命令結果中提取出數字,暫時還沒找到原因。但是數字卻可以以字符串格式得到。於是,使用了atoi來處理:
1 void Redis::getString(const string & key) 2 { 3 freeReply(); 4 _reply = (redisReply*)::redisCommand(_context, "GET %s", key.c_str()); 5 } 6 7 void Redis::getString(const string & key, string & value) 8 { 9 getString(key); 10 if(!isError() && _reply->type == REDIS_REPLY_STRING) 11 { 12 value = _reply->str; 13 } 14 } 15 16 void Redis::getString(const string & key, int & value) 17 { 18 getString(key); 19 if(!isError() && _reply->type == REDIS_REPLY_STRING) 20 { 21 value = ::atoi(_reply->str); 22 } 23 } 24 25 void Redis::getString(const string & key, float & value) 26 { 27 getString(key); 28 if(!isError() && _reply->type == REDIS_REPLY_STRING) 29 { 30 value = ::atof(_reply->str); 31 } 32 }
redis.cc
1 #include "redis.h" 2 3 #include <string.h> 4 #include <stdlib.h> 5 6 #include <sstream> 7 #include <iostream> 8 9 namespace ccx{ 10 11 using std::cout; 12 using std::endl; 13 using std::stringstream; 14 15 #define SETSTRING(key, value) \ 16 stringstream ss;\ 17 ss << "SET " << key << " " << value;\ 18 string s;\ 19 getline(ss, s);\ 20 setString(s); 21 22 Redis::Redis() 23 : _conf() 24 { 25 } 26 27 void Redis::Connect() 28 { 29 _context = ::redisConnect(_conf.getIP().c_str(), _conf.getPort()); 30 cout << _conf.getIP() << "-" << _conf.getPort() << endl; 31 if(_context && _context->err) 32 { 33 cout << "connect redis error" << endl; 34 exit(EXIT_FAILURE); 35 } 36 cout << "redis Connect success" << endl; 37 } 38 39 void Redis::disConnect() 40 { 41 ::redisFree(_context); 42 cout << "redis disConnect success" << endl; 43 } 44 45 void Redis::setString(const string & data) 46 { 47 freeReply(); 48 _reply = (redisReply*)::redisCommand(_context, data.c_str()); 49 if(!isError()) 50 { 51 if (!(_reply->type == REDIS_REPLY_STATUS && strcasecmp(_reply->str,"OK") == 0)) 52 { 53 cout << "Failed to execute SET(string)" << endl; 54 } 55 } 56 } 57 58 void Redis::setString(const string & key, const string & value) 59 { 60 SETSTRING(key, value); 61 } 62 63 void Redis::setString(const string & key, const int & value) 64 { 65 SETSTRING(key, value); 66 } 67 68 void Redis::setString(const string & key, const float & value) 69 { 70 SETSTRING(key, value); 71 } 72 73 void Redis::getString(const string & key) 74 { 75 freeReply(); 76 _reply = (redisReply*)::redisCommand(_context, "GET %s", key.c_str()); 77 } 78 79 void Redis::getString(const string & key, string & value) 80 { 81 getString(key); 82 if(!isError() && _reply->type == REDIS_REPLY_STRING) 83 { 84 value = _reply->str; 85 } 86 } 87 88 void Redis::getString(const string & key, int & value) 89 { 90 getString(key); 91 if(!isError() && _reply->type == REDIS_REPLY_STRING) 92 { 93 value = ::atoi(_reply->str); 94 } 95 } 96 97 void Redis::getString(const string & key, float & value) 98 { 99 getString(key); 100 if(!isError() && _reply->type == REDIS_REPLY_STRING) 101 { 102 value = ::atof(_reply->str); 103 } 104 } 105 106 void Redis::freeReply() 107 { 108 if(_reply) 109 { 110 ::freeReplyObject(_reply); 111 _reply = NULL; 112 } 113 } 114 115 bool Redis::isError() 116 { 117 if(NULL == _reply) 118 { 119 freeReply(); 120 disConnect(); 121 Connect(); 122 return true; 123 } 124 return false; 125 } 126 127 }View Code
test.cc
1 #include "redis.h" 2 3 #include <string> 4 #include <iostream> 5 6 using std::cout; 7 using std::endl; 8 using std::string; 9 10 int main() 11 { 12 ccx::Redis redis; 13 redis.Connect(); 14 redis.setString("name", "lii"); 15 16 string s; 17 redis.getString("name", s); 18 cout << s << endl; 19 20 redis.setString("age", "30"); 21 redis.getString("age", s); 22 cout << s << endl; 23 24 int i; 25 redis.getString("age", i); 26 cout << i << endl; 27 28 redis.disConnect(); 29 }View Code
測試結果如下:
127.0.0.1-6379 redis Connect success lii 30 30 redis disConnect success