* ----------------------------------------------------------------------------
* Copyright (c) 2013-2014, xSky <guozhw at gmail dot com>
* All rights reserved.
* Distributed under GPL license.
* ----------------------------------------------------------------------------
*/
#ifndef _XREDIS_CLIENT_H_
#define _XREDIS_CLIENT_H_
#include "hiredis.h"
#include <stdint.h>
#include <string>
#include <vector>
#include <set>
#include <map>
#include <algorithm>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "util.h"
using namespace std;
#define MAX_ERR_STR_LEN 128
typedef std::string KEY;
typedef std::string VALUE;
typedef std::vector<KEY> KEYS;
typedef std::vector<VALUE> VALUES;
typedef std::vector<string> VDATA;
typedef std::set<string> SETDATA;
typedef struct _REDIS_NODE_
{
unsigned int dbindex;
const char *host;
unsigned int port;
const char *passwd;
unsigned int poolsize;
unsigned int timeout;
}RedisNode;
class xRedisClient;
typedef struct _DATA_ITEM_
{
int type;
string str;
}DataItem;
typedef std::vector<DataItem> ReplyData;
typedef ReplyData ArrayReply;
typedef std::map<string, double> ZSETDATA;
typedef enum _BIT_OP_
{
AND = 0,
OR = 1,
XOR = 2,
NOT = 3
}BITOP;
typedef enum _LIST_MODEL_
{
BEFORE = 0,
AFTER = 1
}LMODEL;
#define PING_REDIS_SEC 5
class xRedisClient
{
public:
xRedisClient(string host, const unsigned int port, string pass, const unsigned int timeout )
{
mHost = host;
mPort = port;
mPass = pass;
mTimeout = timeout;
mLastTime = 0;
}
~xRedisClient()
{
if(mCtx)
{
redisFree(mCtx);
}
if(mStrerr)
{
delete(mStrerr);
}
}
public:
// connection
/* ECHO */
/* PING */
/* QUIT */
/* SELECT */
// Commands operating on string values
/* DECR */ bool decr(const string& key, int& result);
/* DECRBY */ bool decrby(const string& key, const int by, int& result);
/* GET */ bool get(const string& key, string& value);
/* GETBIT */ bool getbit(const string& key, const int& offset, int& bit);
/* GETRANGE */ bool getrange(const string& key, const int start, const int end, string& out);
/* GETSET */ bool getset(const string& key, const string& newValue, string& oldValue);
/* INCR */ bool incr(const string& key, int& result);
/* INCRBY */ bool incrby(const string& key, const int by, int& result);
/* INCRBYFLOAT */
/* MGET */ bool mget(const KEYS & keys, ReplyData& vDdata);
/* MSET */ bool mset(const VDATA& data);
/* MSETNX */
/* SET */ bool set(const string& key, const string& value);
/* SETBIT */ bool setbit(const string& key, const int offset, const int64_t newbitValue, int64_t oldbitValue);
/* SETEX */ bool setex(const string& key, const int seconds, const string& value);
/* SETNX */ bool setnx(const string& key, const string& value);
/* SETRANGE */ bool setrange(const string& key, const int offset, const string& value, int& length);
/* STRLEN */ // bool strlen(const string& key, int& length);
/* DEL */ bool del(const string& key);
bool del(const KEYS & vkey, int64_t& count);
/* DUMP */
/* EXISTS */ bool exists( const string& key);
/* RANDOMKEY */ bool randomkey( KEY& key);
/* HDEL */ bool hdel(const string& key, const string& filed, int64_t& num);
bool hdel(const string& key, const KEYS& vfiled, int64_t& num);
/* HEXISTS */ bool hexist(const string& key, const string& filed);
/* HGET */ bool hget(const string& key, const string& filed, string& value);
/* HGETALL */ bool hgetall(const string& key, ArrayReply& array);
/* HKEYS */ bool hkeys(const string& key, KEYS& keys);
/* HLEN */ bool hlen(const string& key, int64_t& count);
/* HMGET */ bool hmget(const string& key, const KEYS& filed, ArrayReply& array);
/* HMSET */ bool hmset(const string& key, const VDATA& vData);
/* HSCAN */
/* HSET */ bool hset(const string& key, const string& filed, const string& value, int64_t& retval);
/* HSETNX */ bool hsetnx(const string& key, const string& filed, const string& value);
/* HVALS */ bool hvals(const string& key, VALUES& values);
/* LLEN */ bool llen(const string& key, int64_t& len);
/* LPOP */ bool lpop(const string& key, string& value);
/* LPUSH */ bool lpush(const string& key, const VALUES& vValue, int64_t& length);
/* LPUSHX */ bool lpushx(const string& key, const string& value, int64_t& length);
/* LRANGE */ bool lrange(const string& key, const int64_t start, const int64_t end, ArrayReply& array);
/* LTRIM */ bool ltrim(const string& key, const int start, const int end);
/* RPOP */ bool rpop(const string& key, string& value);
/* RPOPLPUSH */ bool rpoplpush(const string& key_src, const string& key_dest, string& value);
/* RPUSH */ bool rpush(const string& key, const VALUES& vValue, int64_t& length);
/* RPUSHX */ bool rpushx(const string& key, const string& value, int64_t& length);
/* SADD */ bool sadd(const KEY& key, const VALUES& vValue, int64_t& count);
/* SMOVE */ bool smove(const KEY& srckey, const KEY& deskey, const VALUE& member);
/* SPOP */ bool spop(const KEY& key, VALUE& member);
/* SRANDMEMBER */ bool srandmember(const KEY& key, VALUES& vmember, int num=0);
/* ZADD */ bool zadd(const KEY& deskey, const VALUES& vValues, int64_t& count);
private:
void addparam(VDATA& vDes, const VDATA& vSrc)
{
for (VDATA::const_iterator iter=vSrc.begin(); iter!=vSrc.end();++iter)
{
vDes.push_back(*iter);
}
}
bool command_bool(const char* cmd, ...);
bool command_status(const char* cmd, ...);
bool command_integer(int64_t &intval, const char* cmd, ...);
bool command_string(string &data, const char* cmd, ...);
bool command_list(VALUES &vValue, const char* cmd, ...);
bool command_array(ArrayReply& array, const char* cmd, ...);
bool commandargv_bool(const VDATA& vData);
bool commandargv_status(const VDATA& vData);
bool commandargv_array(const VDATA& vDataIn, ArrayReply& array);
bool commandargv_array(const VDATA& vDataIn, VALUES& array);
bool commandargv_integer(const VDATA& vDataIn, int64_t& retval);
public:
char *GetErrInfo()
{
return this->mStrerr;
}
bool SetErrInfo(const char *info)
{
if (NULL==info)
{
return false;
}
if (NULL == this->mStrerr)
{
this->mStrerr = new char[MAX_ERR_STR_LEN];
}
if (NULL!=this->mStrerr)
{
memset(this->mStrerr,0, MAX_ERR_STR_LEN);
strncpy(this->mStrerr, info, MAX_ERR_STR_LEN);
return true;
}
return false;
}
bool CheckReply(const redisReply *reply)
{
if(NULL==reply)
{
return false;
}
switch(reply->type)
{
case REDIS_REPLY_STRING:
{
return true;
}
case REDIS_REPLY_ARRAY:
{
return (strcasecmp(reply->str,"OK") == 0)?true:false;
}
case REDIS_REPLY_INTEGER:
{
return true;
}
case REDIS_REPLY_NIL:
{
return false;
}
case REDIS_REPLY_STATUS:
{
return (strcasecmp(reply->str,"OK") == 0)?true:false;
}
case REDIS_REPLY_ERROR:
{
return false;
}
default:
{
return false;
}
}
return false;
}
void FreeReply(const redisReply *reply)
{
if (NULL!=reply)
{
freeReplyObject((void*)reply);
}
}
bool RedisConnect()
{
bool bRet = false;
struct timeval timeoutVal;
timeoutVal.tv_sec = mTimeout;
timeoutVal.tv_usec = 0;
mCtx = redisConnectWithTimeout(mHost.c_str(), mPort, timeoutVal);
if (NULL == mCtx || mCtx->err)
{
if (NULL!=mCtx)
{
redisFree(mCtx);
} else
{
}
} else
{
if (0 == mPass.size())
{
bRet = true;
} else
{
redisReply *reply = static_cast<redisReply *>(redisCommand(mCtx,"AUTH %s", mPass.c_str()));
if((NULL==reply)||(strcasecmp(reply->str,"OK") != 0))
{
bRet = false;
}
freeReplyObject(reply);
}
}
return bRet;
}
bool Ping() const
{
redisReply *reply = static_cast<redisReply *>(redisCommand(mCtx,"PING"));
bool bRet = (NULL!=reply);
freeReplyObject(reply);
return bRet;
}
//计算频率
bool checkRedisNetwork()
{
time_t tick = time(NULL);
if(tick >= mLastTime + PING_REDIS_SEC)
{
if(Ping() == false)
{
DEBUG_LOG("检测redis断开");
this->RedisConnect();
}
mLastTime = tick;
}
}
redisContext *getCtx() const
{
return mCtx;
}
redisContext *mCtx;
private:
char *mStrerr;
string mHost; // redis host
unsigned int mPort; // redis sever port
string mPass; // redis server password
unsigned int mTimeout; // connect timeout second
time_t mLastTime; //
};
#endif
/*
* ----------------------------------------------------------------------------
* Copyright (c) 2013-2014, xSky <guozhw at gmail dot com>
* All rights reserved.
* Distributed under GPL license.
* ----------------------------------------------------------------------------
*/
#include "redis_client.h"
#include <sstream>
#include <stdlib.h>
bool xRedisClient::command_bool( const char *cmd, ...)
{
bool bRet = false;
va_list args;
va_start(args, cmd);
redisReply *reply = static_cast<redisReply *>(redisvCommand(mCtx, cmd, args));
va_end(args);
if (CheckReply(reply))
{
bRet = (reply->integer==1)?true:false;
} else
{
SetErrInfo(reply->str);
}
FreeReply(reply);
return bRet;
}
bool xRedisClient::command_status( const char* cmd, ...)
{
bool bRet = false;
va_list args;
va_start(args, cmd);
redisReply *reply = static_cast<redisReply *>(redisvCommand(mCtx, cmd, args));
va_end(args);
if (CheckReply(reply))
{
bRet = true;
} else
{
SetErrInfo(reply->str);
}
FreeReply(reply);
return bRet;
}
bool xRedisClient::command_integer( int64_t &retval, const char* cmd, ...)
{
bool bRet = false;
if(this->mCtx == NULL)
{
return bRet;
}
va_list args;
va_start(args, cmd);
redisReply *reply = static_cast<redisReply *>(redisvCommand(this->mCtx, cmd, args));
va_end(args);
if (CheckReply(reply))
{
retval = reply->integer;
bRet = true;
} else
{
SetErrInfo(reply->str);
}
FreeReply(reply);
return bRet;
}
bool xRedisClient::command_string( string &data, const char* cmd, ...)
{
bool bRet = false;
if(this->mCtx == NULL)
{
return bRet;
}
va_list args;
va_start(args, cmd);
redisReply *reply = static_cast<redisReply *>(redisvCommand(this->mCtx, cmd, args));
va_end(args);
if (CheckReply(reply))
{
data = reply->str;
bRet = true;
} else
{
SetErrInfo(reply->str);
}
FreeReply(reply);
return bRet;
}
bool xRedisClient::command_list( VALUES &vValue, const char* cmd, ...)
{
bool bRet = false;
va_list args;
va_start(args, cmd);
redisReply *reply = static_cast<redisReply *>(redisvCommand(this->mCtx, cmd, args));
va_end(args);
if (CheckReply(reply))
{
for (unsigned int i =0; i<reply->elements; i++)
{
vValue.push_back(reply->element[i]->str);
}
bRet = true;
} else
{
SetErrInfo(reply->str);
}
FreeReply(reply);
return bRet;
}
bool xRedisClient::command_array( ArrayReply& array, const char* cmd, ...){
bool bRet = false;
va_list args;
va_start(args, cmd);
redisReply *reply = static_cast<redisReply *>(redisvCommand(this->mCtx, cmd, args));
va_end(args);
if (CheckReply(reply))
{
for (unsigned int i =0; i<reply->elements; i++)
{
DataItem item;
item.type = reply->element[i]->type;
item.str = reply->element[i]->str;
array.push_back(item);
}
bRet = true;
} else
{
SetErrInfo(reply->str);
}
FreeReply(reply);
return bRet;
}
bool xRedisClient::commandargv_bool( const VDATA& vData) {
bool bRet = false;
vector<const char *> argv( vData.size() );
vector<size_t> argvlen( vData.size() );
unsigned int j = 0;
for ( vector<string>::const_iterator i = vData.begin(); i != vData.end(); ++i, ++j )
{
argv[j] = i->c_str(), argvlen[j] = i->size();
}
redisReply *reply = static_cast<redisReply *>(redisCommandArgv(this->mCtx, argv.size(), &(argv[0]), &(argvlen[0])));
if (CheckReply(reply))
{
bRet = (reply->integer==1)?true:false;
}
FreeReply(reply);
return bRet;
}
bool xRedisClient::commandargv_status( const VDATA& vData)
{
bool bRet = false;
vector<const char *> argv( vData.size() );
vector<size_t> argvlen( vData.size() );
unsigned int j = 0;
for ( vector<string>::const_iterator i = vData.begin(); i != vData.end(); ++i, ++j )
{
argv[j] = i->c_str(), argvlen[j] = i->size();
}
redisReply *reply = static_cast<redisReply *>(redisCommandArgv(this->mCtx, argv.size(), &(argv[0]), &(argvlen[0])));
if (CheckReply(reply))
{
bRet = true;
}
FreeReply(reply);
return bRet;
}
bool xRedisClient::commandargv_array( const VDATA& vDataIn, ArrayReply& array)
{
bool bRet = false;
vector<const char*> argv( vDataIn.size() );
vector<size_t> argvlen( vDataIn.size() );
unsigned int j = 0;
for ( vector<string>::const_iterator i = vDataIn.begin(); i != vDataIn.end(); ++i, ++j )
{
argv[j] = i->c_str(), argvlen[j] = i->size();
}
redisReply *reply = static_cast<redisReply *>(redisCommandArgv(this->mCtx, argv.size(), &(argv[0]), &(argvlen[0])));
if (CheckReply(reply))
{
for (unsigned int i =0; i<reply->elements; i++)
{
DataItem item;
item.type = reply->element[i]->type;
item.str = reply->element[i]->str;
array.push_back(item);
}
bRet = true;
} else
{
SetErrInfo(reply->str);
}
FreeReply(reply);
return bRet;
}
bool xRedisClient::commandargv_array( const VDATA& vDataIn, VALUES& array)
{
bool bRet = false;
vector<const char*> argv( vDataIn.size() );
vector<size_t> argvlen( vDataIn.size() );
unsigned int j = 0;
for ( vector<string>::const_iterator i = vDataIn.begin(); i != vDataIn.end(); ++i, ++j )
{
argv[j] = i->c_str(), argvlen[j] = i->size();
}
redisReply *reply = static_cast<redisReply *>(redisCommandArgv(this->mCtx, argv.size(), &(argv[0]), &(argvlen[0])));
if (CheckReply(reply))
{
for (unsigned int i =0; i<reply->elements; i++)
{
array.push_back(reply->element[i]->str);
}
bRet = true;
} else
{
SetErrInfo(reply->str);
}
FreeReply(reply);
return bRet;
}
bool xRedisClient::commandargv_integer( const VDATA& vDataIn, int64_t& retval)
{
bool bRet = false;
vector<const char*> argv( vDataIn.size() );
vector<size_t> argvlen( vDataIn.size() );
unsigned int j = 0;
for ( vector<string>::const_iterator iter = vDataIn.begin(); iter != vDataIn.end(); ++iter, ++j )
{
argv[j] = iter->c_str(), argvlen[j] = iter->size();
}
redisReply *reply = static_cast<redisReply *>(redisCommandArgv(this->mCtx, argv.size(), &(argv[0]), &(argvlen[0])));
if (CheckReply(reply))
{
retval = reply->integer;
bRet = true;
} else
{
SetErrInfo(reply->str);
}
FreeReply(reply);
return bRet;
}
bool xRedisClient::incr( const string& key, int& result)
{
return command_integer((int64_t&)result, "INCR %s", key.c_str());
}
bool xRedisClient::set( const string& key, const string& value)
{
VDATA vCmdData;
vCmdData.push_back("SET");
vCmdData.push_back(key);
vCmdData.push_back(value);
return commandargv_status( vCmdData);
}
bool xRedisClient::del(const string& key)
{
return command_bool( "DEL %s", key.c_str());
}
bool xRedisClient::hdel(const string& key, const string& filed, int64_t& count)
{
return command_integer( count, "HDEL %s %s %s", key.c_str(), filed.c_str());
}
bool xRedisClient::hdel(const string& key, const KEYS& vfiled, int64_t& count)
{
VDATA vCmdData;
vCmdData.push_back("HDEL");
vCmdData.push_back(key);
addparam(vCmdData, vfiled);
return commandargv_integer(vCmdData, count);
}
bool xRedisClient::hexist(const string& key, const string& filed)
{
return command_bool("HEXIST %s %s %s", key.c_str(), filed.c_str());
}
bool xRedisClient::hget(const string& key, const string& filed, string& value)
{
return command_string(value, "HGET %s %s %s", key.c_str(), filed.c_str());
}
bool xRedisClient::hgetall(const string& key, ArrayReply& array)
{
return command_array( array, "HGETALL %s", key.c_str());
}
bool xRedisClient::hkeys(const string& key, KEYS& keys)
{
return command_list(keys, "HKEYS %s", key.c_str());
}
bool xRedisClient::hlen(const string& key, int64_t& count)
{
return command_integer(count, "HLEN %s", key.c_str());
}
bool xRedisClient::hmget(const string& key, const KEYS& filed, ArrayReply& array)
{
VDATA vCmdData;
vCmdData.push_back("HMGET");
vCmdData.push_back(key);
addparam(vCmdData, filed);
return commandargv_array(vCmdData, array);
}
bool xRedisClient::hmset(const string& key, const VDATA& vData)
{
VDATA vCmdData;
vCmdData.push_back("HMGET");
vCmdData.push_back(key);
addparam(vCmdData, vData);
return commandargv_bool(vCmdData);
}
bool xRedisClient::hset(const string& key, const string& filed, const string& value, int64_t& retval)
{
return command_integer( retval, "HSET %s %s %s", key.c_str(), filed.c_str(), value.c_str());
}
bool xRedisClient::hvals(const string& key, VALUES& values)
{
return command_list( values, "HVALS %s", key.c_str());
}
bool xRedisClient::exists(const string& key)
{
return command_bool( "EXISTS %s", key.c_str());
}
bool xRedisClient::randomkey( KEY& key)
{
return command_string( key, "RANDOMKEY");
}
bool xRedisClient::llen(const string& key, int64_t& retval)
{
return command_integer(retval, "LLEN %s", key.c_str());
}
bool xRedisClient::lpop(const string& key, string& value)
{
return command_string(value, "LPOP %s", key.c_str());
}
bool xRedisClient::lpush(const string& key, const VALUES& vValue, int64_t& length)
{
VDATA vCmdData;
vCmdData.push_back("lpush");
vCmdData.push_back(key);
addparam(vCmdData, vValue);
return commandargv_integer(vCmdData, length);
}
bool xRedisClient::lrange( const string& key, const int64_t start, const int64_t end, ArrayReply& array)
{
return command_array(array, "LRANGE %s", key.c_str(), start, end);
}
bool xRedisClient::ltrim(const string& key, const int start, const int end)
{
return command_status( "ltrim %s %d %d", key.c_str(), start, end);
}
bool xRedisClient::rpop(const string& key, string& value)
{
return command_string(value, "LPOP %s", key.c_str());
}
bool xRedisClient::rpoplpush(const string& key_src, const string& key_dest, string& value)
{
return command_string(value, "RPOPLPUSH %s %s", key_src.c_str(), key_dest.c_str());
}
bool xRedisClient::rpush(const string& key, const VALUES& vValue, int64_t& length)
{
VDATA vCmdData;
vCmdData.push_back("rpush");
vCmdData.push_back(key);
addparam(vCmdData, vValue);
return commandargv_integer( vCmdData, length);
}
bool xRedisClient::rpushx( const string& key, const string& value, int64_t& length)
{
return command_integer(length, "RPUSHX %s %s", key.c_str(), value.c_str());
}
//sets
bool xRedisClient::sadd( const string& key, const VALUES& vValue, int64_t& count)
{
VDATA vCmdData;
vCmdData.push_back("SADD");
vCmdData.push_back(key);
addparam(vCmdData, vValue);
return commandargv_integer(vCmdData, count);
}
bool xRedisClient::smove(const KEY& srckey, const KEY& deskey, const VALUE& member)
{
return command_bool( "SMOVE %s", srckey.c_str(), deskey.c_str(), member.c_str());
}
bool xRedisClient::spop(const KEY& key, VALUE& member)
{
return command_string(member, "SPOP %s", key.c_str());
}
bool xRedisClient::srandmember(const KEY& key, VALUES& members, int count)
{
if (0==count)
{
return command_list( members, "SRANDMEMBER %s", key.c_str());
}
return command_list( members, "SRANDMEMBER %s %d", key.c_str(), count);
}
bool xRedisClient::zadd( const KEY& key, const VALUES& vValues, int64_t& count)
{
VDATA vCmdData;
vCmdData.push_back("ZADD");
vCmdData.push_back(key);
addparam(vCmdData, vValues);
return commandargv_integer( vCmdData, count);
}
bool xRedisClient::setbit(const string& key, const int offset, const int64_t newbitValue, int64_t oldbitValue)
{
return command_integer(oldbitValue, "SETBIT %s %d %lld", key.c_str(), offset, newbitValue);
}
bool xRedisClient::get(const string& key, string& value)
{
return command_string(value, "GET %s", key.c_str());
}
bool xRedisClient::getbit( const string& key, const int& offset, int& bit )
{
return command_integer((int64_t&)bit, "GETBIT %s %d", key.c_str(), offset);
}
bool xRedisClient::getrange(const string& key, const int start, const int end, string& out)
{
return command_string(out, "GETRANGE %s %d %d", key.c_str(), start, end);
}
bool xRedisClient::getset(const string& key, const string& newValue, string& oldValue)
{
return command_string(oldValue, "GETSET %s %s", key.c_str(), newValue.c_str());
}
bool xRedisClient::setrange(const string& key, const int offset, const string& value, int& length)
{
return command_integer((int64_t&)length, "setrange %s %d %s", key. c_str(), offset, value.c_str());
}
bool xRedisClient::incrby(const string& key, const int by, int& result)
{
return command_integer((int64_t&)result, "INCR %s %d", key.c_str(), by);
}
bool xRedisClient::decr( const string& key, int& result)
{
return command_integer((int64_t&)result,"decr %s", key.c_str());
}
bool xRedisClient::decrby( const string& key, const int by, int& result)
{
return command_integer((int64_t&)result, "decrby %s %d", key.c_str(), by);
}