当前位置: 首页 > news >正文

Redis的C客户端(hiredis库)使用

文章目录

  • 1、Ubuntu安装redis服务端
  • 2、hiredis库的安装
  • 3、同步API接口的使用
    • 3.1、连接redis数据库redisConnect
    • 3.2、发送需要执行的命令redisCommand
    • 3.3、redisCommandArgv函数
    • 3.4、redisAppendCommand*函数支持管道命令
    • 3.5、释放资源
    • 3.6、同步连接代码
  • 3.7、异步连接
  • 4、redis连接池实现

Hiredis是一个Redis的C客户端库函数,基本实现了Redis的协议的最小集。这里对hiredis的api作基本的介绍以及应用,主要参考hiredis的README文件以及相关源码。

1、Ubuntu安装redis服务端

Redis全称为Remote Dictionary Server(远程数据服务),是一款开源的基于内存的键值对存储系统,其主要被用作高性能缓存服务器使用,当然也可以作为消息中间件和Session共享等。Redis独特的键值对模型使之支持丰富的数据结构类型,即它的值可以是字符串、哈希、列表、集合、有序集合,而不像Memcached要求的键和值都是字符串。同时由于Redis是基于内存的方式,免去了磁盘I/O速度的影响,因此其读写性能极高。

1、在Ubuntu中打开终端,输入下列命令,下载Redis安装包:

wget http://download.redis.io/releases/redis-4.0.9.tar.gz

2、对安装包进行解压,并将其移动放到usr/local⽬录下
命令如下:
解压:

tar xzf redis-4.0.9.tar.gz
移动到
sudo mv ./redis-4.0.9 /usr/local/redis/

3、进入redis⽬录,编译生成
命令:

cd /usr/local/redis/
sudo make测试
sudo make test

4、安装,将redis的命令安装到/usr/local/bin/⽬录

sudo make install

5、安装完成后,我们进入目录/usr/local/bin中查看

cd /usr/local/bin
ls -all

6、配置⽂件,移动到/etc/⽬录下
配置⽂件⽬录为/usr/local/redis/redis.conf

sudo cp /usr/local/redis/redis.conf /etc/redis/

7、Redis的配置信息在/etc/redis/redis.conf下

sudo vi /etc/redis/redis.conf绑定ip:如果需要远程访问,可将此⾏注释,或绑定⼀个真实ip
bind 127.0.0.1端⼝,默认为6379
port 6379是否以守护进程运⾏
如果以守护进程运⾏,则不会在命令⾏阻塞,类似于服务
如果以⾮守护进程运⾏,则当前终端被阻塞
设置为yes表示守护进程,设置为no表示⾮守护进程
推荐设置为yes
daemonize yes数据⽂件
dbfilename dump.rdb数据⽂件存储路径
dir /var/lib/redis⽇志⽂件
logfile "/var/log/redis/redis-server.log"数据库,默认有16个
database 16主从复制,类似于双机备份。
slaveof

连接服务端

./redis-cli -h 127.0.0.1 -p 6379

2、hiredis库的安装

官网:https://redislabs.com/lp/hiredis/
发行版本:https://github.com/redis/hiredis/releases
目前最新的版本:https://codeload.github.com/redis/hiredis/tar.gz/v0.14.0

1、解压:tar -zxvf hiredis-0.14.0.tar.gz
2、编译:make
3、安装:make install

也可以直接将文件编译到自己的工程代码。

3、同步API接口的使用

我们的项目中使用的hireds接口都是同步的API,所谓同步意思就是使用阻塞的方式向redis server下发消息。
接口的主要部分为下面三个部分,下面分别介绍。

/**连接数据库*/
redisContext *redisConnect(const char *ip, int port);
/**发送命令请求*/
void *redisCommand(redisContext *c, const char *format, ...);
void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
void redisAppendCommand(redisContext *c, const char *format, ...);
void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
/*释放资源*/
void freeReplyObject(void *reply);
void redisFree(redisContext *c);

3.1、连接redis数据库redisConnect

redisContext *redisConnect(const char *ip, int port);

参数说明
● port:为redis数据监听的端口号,redis默认监听的端口号为6379
● ip:为redis数据库的IP地址,可以是远程的,也可以是本地的127.0.0.1
返回值
返回值是一个指向redisContext对象,可以不用了解这个对象的具体组成部分,只需要知道怎么使用就可以了。下面是其定义。

typedef struct redisContext {int err; /* Error flags, 0 when there is no error */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;

3.2、发送需要执行的命令redisCommand

void *redisCommand(redisContext *c, const char *format, ...);

参数说明
这个函数是一个带有不定参数的。可以按着format格式给出对应的参数,这就和printf函数类似。
c 是一个reidsConnect函数返回的一个对象。
返回值
返回值是一个void类型的指针,实际为指向一个redisReply类型的指针。

/* This is the reply object returned by redisCommand() */
typedef struct redisReply {/*命令执行结果的返回类型*/int type; /* REDIS_REPLY_* *//*存储执行结果返回为整数*/long long integer; /* The integer when type is REDIS_REPLY_INTEGER *//*字符串值的长度*/size_t len; /* Length of string *//*存储命令执行结果返回是字符串*/char *str; /* Used for both REDIS_REPLY_ERROR and REDIS_REPLY_STRING *//*返回结果是数组的大小*/size_t elements; /* number of elements, for REDIS_REPLY_ARRAY *//*存储执行结果返回是数组*/struct redisReply **element; /* elements vector for REDIS_REPLY_ARRAY */
} redisReply;返回结果的类型reply->type,reply 为redisReply* 类型。
● REDIS_REPLY_STRING == 1:返回值是字符串,字符串储存在redis->str当中,字符串长度为redis->len。
● REDIS_REPLY_ARRAY == 2:返回值是数组,数组大小存在redis->elements里面,数组值存储在redis->element[i]里面。数组里面存储的是指向redisReply的指针,数组里面的返回值可以通过redis->element[i]->str来访问,数组的结果里全是type==REDIS_REPLY_STRING的redisReply对象指针。
● REDIS_REPLY_INTEGER == 3:返回值为整数 long long。
● REDIS_REPLY_NIL==4:返回值为空表示执行结果为空。
● REDIS_REPLY_STATUS ==5:返回命令执行的状态,比如set foo bar 返回的状态为OK,存储在str当中 reply->str == "OK"。
● REDIS_REPLY_ERROR ==6 :命令执行错误,错误信息存放在 reply->str当中。

3.3、redisCommandArgv函数

void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);

参数说明
argvlen这个数组存储了命令参数中,每一个参数的长度,包含命令本身,比如 set foo bar 则argvlen ={3,3,3},如果argvlen为空,那么这个函数内部会自动调用strlen函数对每个参数进行求长度。
argv 存放每个命令参数的指针,argv={“set”,“foo”,“bar”}
argc 存放命令参数的个数上面的例子中argc=3
c 为redisContext对象。
为每一个参数指定长度,可以是二进制安全的函数。函数会按着长度来决定字符串的终止,而不是’\0’.

char hkey[] = "123456";
char hset[] = "hset";
char key[] = "testkey";
char hvalue[] = "3210";
int argc = 4;
char *argv[] = {hset,key,hkey,hvalue};
size_t argvlen[] = {4,6,4,3};
redisCommandArgv(context,argc,argv,argvlen);

hgetall testkey
会得到321并不会得到和hvalue一样的值"3210",因为在hset命令中指定了长度,只会读取前面的三个字符。

3.4、redisAppendCommand*函数支持管道命令

void redisAppendCommand(redisContext *c, const char *format, ...);
void redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen);
int redisGetReply(redisContext *context,redisReply** reply);

参数说明:
redisAppendCommand函数和redisCommand函数参数一致,format可以指定特定参数的类型。
c 为redisContext对象
redisAppendCommandArgv函数和redisCommandArgv函数类似,参数含义也相同。
redisGetReply函数用来获得执行的结果的一条返回,并存储在reply所指的对象当中。成功返回REDIS_OK,否则返回REIDS_ERR。多条命令的一次性返回结果都存放在redisContext里面。
所不同的是,这个两个命令的结果。这两个函数是把多个命令存放在缓冲区内,然后一起发送给redis服务器,一次执行。可以通过redisGetReply函数从
redisContext中取出返回的结果。
使用例子:

redisReply *reply;
/*添加命令set */
redisAppendCommand(context,"SET foo bar");
/*添加命令get */
redisAppendCommand(context,"GET foo");
/*获取set命令结果*/
redisGetReply(context,&reply); // reply for SET
freeReplyObject(reply);
/*获取get命令结果*/
redisGetReply(context,&reply); // reply for GET
freeReplyObject(reply);

3.5、释放资源

void freeReplyObject(void *reply);
void redisFree(redisContext *c);

参数说明
freeReplyObject函数中reply 实际为指向redisReply结构体的指针,可能是redisCommand的返回值,后续可以看到以也能是管道命令执行结果的返回值。
redisFree函数中c实际为指向redisContext对象,这个函数会清理连接资源并释放连接。

3.6、同步连接代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "hiredis/hiredis.h"int main(int argc,char *argv[])
{unsigned int j,isunix = 0;redisContext *c;redisReply *reply;const char *hostname = "127.0.0.1";int port = 6379;struct timeval timeout = {1,500000};    // 1.5 secondsc = redisConnectWithTimeout(hostname,port,timeout);if(c == NULL || c->err){if(c){printf("Connection error:%s\n",c->errstr);redisFree(c);} else {printf("Connection error:can't allocate redis context\n");}exit(1);}int num = 1000;for(int i=0;i<num;i++){// INCR counter 是 Redis 的一个命令,它用于将名为 "counter" 的键的值递增1。如果该键不存在,它将被创建并初始化为0reply = redisCommand(c,"INCR counter");printf("INCR counter:%lld\n",reply->integer);freeReplyObject(reply);}redisFree(c);return 0;
}

如果执行的时候报这个错误
在这里插入图片描述
解决办法:
确保你的/etc/ld.so.conf里面有 /usr/local/lib 这一行
没有的话vim编辑在尾行加上
然后 sudo ldconfig

2、 ldconfig介绍
ldconfig是一个动态链接库管理命令,其目的为了让动态链接库为系统所共享。
当进程需要链接相应的库文件时候,会默认搜寻/lilb和/usr/lib,以及配置文件/etc/ld.so.conf内所列的目录下的库文件。若找不到的话,就会出现如上图的错误。
ldconfig通常在系统启动时运行,而当用户安装了一个新的动态链接库时,就需要手工运行这个命令。
ldconfig需要注意的地方:

/lib和/usr/lib里面加东西,是不用修改/etc/ld.so.conf文件的,但是添加完后需要调用下ldconfig,不然添加的library会找不到。如果添加的library不在/lib和/usr/lib里面的话,就一定要修改/etc/ld.so.conf文件,往该文件追加library所在的路径,然后也需要重新调用下ldconfig命令。比如在安装MySQL的时候,其库文件/usr/local/mysql/lib,就需要追加到/etc/ld.so.conf文件中。命令如下:echo "/usr/local/mysql/lib" >> /etc/ld.so.confldconfig -v | grep mysql最后注意:
如果添加的library不在/lib或/usr/lib下,但是却没有权限操作写/etc/ld.so.conf文件的话,这时就需要往export里写一个全局变量LD_LIBRARY_PATH,就可以了。

3.7、异步连接

#include "hiredis/hiredis.h"
#include "hiredis/async.h"
#include "reactor.h"
#include "adapter.h"
#include <time.h>static reactor_t *R;
static int cnt, before, num;int current_tick() {int t = 0;struct timespec ti;clock_gettime(CLOCK_MONOTONIC, &ti);t = (int)ti.tv_sec * 1000;t += ti.tv_nsec / 1000000;return t;
}void getCallback(redisAsyncContext *c, void *r, void *privdata) {redisReply *reply = r;if (reply == NULL) return;printf("argv[%s]: %lld\n", (char*)privdata, reply->integer);/* Disconnect after receiving the reply to GET */cnt++;if (cnt == num) {int used = current_tick()-before;printf("after %d exec redis command, used %d ms\n", num, used);redisAsyncDisconnect(c);}
}void connectCallback(const redisAsyncContext *c, int status) {if (status != REDIS_OK) {printf("Error: %s\n", c->errstr);stop_eventloop(R);return;}printf("Connected...\n");
}void disconnectCallback(const redisAsyncContext *c, int status) {if (status != REDIS_OK) {printf("Error: %s\n", c->errstr);stop_eventloop(R);return;}printf("Disconnected...\n");stop_eventloop(R);
}int main(int argc, char **argv) {redisAsyncContext *c = redisAsyncConnect("127.0.0.1", 6379);if (c->err) {/* Let *c leak for now... */printf("Error: %s\n", c->errstr);return 1;}R = create_reactor();redisAttach(R, c);redisAsyncSetConnectCallback(c, connectCallback);redisAsyncSetDisconnectCallback(c, disconnectCallback);before = current_tick();num = (argc > 1) ? atoi(argv[1]) : 1000;for (int i = 0; i < num; i++) {redisAsyncCommand(c, getCallback, "count", "INCR counter");}eventloop(R);release_reactor(R);return 0;
}// gcc main.c -o main -L./hiredis -lhiredis

4、redis连接池实现

1、Thread.h头文件

#ifndef __THREAD_H__
#define __THREAD_H__
#include <stdint.h>
#include <pthread.h>class CThreadNotify
{
public:CThreadNotify(){pthread_mutexattr_init(&m_mutexattr);pthread_mutexattr_settype(&m_mutexattr, PTHREAD_MUTEX_RECURSIVE);pthread_mutex_init(&m_mutex, &m_mutexattr);pthread_cond_init(&m_cond, NULL);}~CThreadNotify(){pthread_mutexattr_destroy(&m_mutexattr);pthread_mutex_destroy(&m_mutex);pthread_cond_destroy(&m_cond);}void Lock(){pthread_mutex_lock(&m_mutex);}void Unlock(){pthread_mutex_unlock(&m_mutex);}void Wait(){pthread_cond_wait(&m_cond, &m_mutex);}// 返回0则正常,其他值为异常int WaitTime(int ms){//获取时间struct timespec outtime;clock_gettime(CLOCK_MONOTONIC, &outtime);//ms为毫秒,换算成秒outtime.tv_sec += ms / 1000;//在outtime的基础上,增加ms毫秒//outtime.tv_nsec为纳秒,1微秒=1000纳秒//tv_nsec此值再加上剩余的毫秒数 ms%1000,有可能超过1秒。需要特殊处理uint64_t us = outtime.tv_nsec / 1000 + 1000 * (ms % 1000); //微秒//us的值有可能超过1秒,outtime.tv_sec += us / 1000000;us = us % 1000000;outtime.tv_nsec = us * 1000; //换算成纳秒return pthread_cond_timedwait(&m_cond, &m_mutex, &outtime);}void Signal(){pthread_cond_signal(&m_cond);}private:pthread_mutex_t m_mutex;pthread_mutexattr_t m_mutexattr;pthread_cond_t m_cond;
};#endif

2、头文件CachePool.h

/** @Author: your name* @Date: 2019-12-07 10:54:57* @LastEditTime : 2020-01-10 16:35:13* @LastEditors  : Please set LastEditors* @Description: In User Settings Edit* @FilePath: \src\cache_pool\CachePool.h*/
#ifndef CACHEPOOL_H_
#define CACHEPOOL_H_#include <iostream>
#include <vector>
#include <map>
#include <list>#include "Thread.h"#include "hiredis.h"using std::string;
using std::list;
using std::map; 
using std::vector; class CachePool;class CacheConn {
public:CacheConn(const char* server_ip, int server_port, int db_index, const char* password, const char *pool_name ="");CacheConn(CachePool* pCachePool);	virtual ~CacheConn();int Init();void DeInit();const char* GetPoolName();// 通用操作// 判断一个key是否存在bool isExists(string &key);// 删除某个keylong del(string &key);// ------------------- 字符串相关 -------------------string get(string key);string set(string key, string& value);string setex(string key, int timeout, string value);// string mset(string key, map);//批量获取bool mget(const vector<string>& keys, map<string, string>& ret_value);//原子加减1long incr(string key);long decr(string key);// ---------------- 哈希相关 ------------------------long hdel(string key, string field);string hget(string key, string field);bool hgetAll(string key, map<string, string>& ret_value);long hset(string key, string field, string value);long hincrBy(string key, string field, long value);long incrBy(string key, long value);string hmset(string key, map<string, string>& hash);bool hmget(string key, list<string>& fields, list<string>& ret_value);// ------------ 链表相关 ------------long lpush(string key, string value);long rpush(string key, string value);long llen(string key);bool lrange(string key, long start, long end, list<string>& ret_value);bool flushdb();private:CachePool* 		m_pCachePool;redisContext* 	m_pContext;uint64_t		m_last_connect_time;uint16_t 		m_server_port;string 			m_server_ip;string          m_password;uint16_t        m_db_index;string 			m_pool_name;
};class CachePool {
public:// db_index和mysql不同的地方 CachePool(const char* pool_name, const char* server_ip, int server_port, int db_index, const char *password, int max_conn_cnt);virtual ~CachePool();int Init();// 获取空闲的连接资源CacheConn* GetCacheConn();// Pool回收连接资源void RelCacheConn(CacheConn* pCacheConn);const char* GetPoolName() { return m_pool_name.c_str(); }const char* GetServerIP() { return m_server_ip.c_str(); }const char* GetPassword() { return m_password.c_str(); }int GetServerPort() { return m_server_port; }int GetDBIndex() { return m_db_index; }
private:string 		m_pool_name;string		m_server_ip;string 		m_password;int			m_server_port;int			m_db_index;	// mysql 数据库名字, redis db indexint			m_cur_conn_cnt;int 		m_max_conn_cnt;list<CacheConn*>	m_free_list;CThreadNotify		m_free_notify;
};#endif /* CACHEPOOL_H_ */

3、CachePool.cpp文件

#include "CachePool.h"#include <stdlib.h>
#include <string.h>
#include "Thread.h"#define log_error printf
#define log_info printf#define MIN_CACHE_CONN_CNT 2
#define MAX_CACHE_CONN_FAIL_NUM 10CacheConn::CacheConn(const char *server_ip, int server_port, int db_index, const char *password,const char *pool_name)
{m_server_ip = server_ip;m_server_port = server_port;m_db_index = db_index;m_password = password;m_pool_name = pool_name;m_pContext = NULL;m_last_connect_time = 0;
}CacheConn::CacheConn(CachePool *pCachePool)
{m_pCachePool = pCachePool;if (pCachePool){m_server_ip = pCachePool->GetServerIP();m_server_port = pCachePool->GetServerPort();m_db_index = pCachePool->GetDBIndex();m_password = pCachePool->GetPassword();m_pool_name = pCachePool->GetPoolName();}else{log_error("pCachePool is NULL\n");}m_pContext = NULL;m_last_connect_time = 0;
}CacheConn::~CacheConn()
{if (m_pContext){redisFree(m_pContext);m_pContext = NULL;}
}/** redis初始化连接和重连操作,类似mysql_ping()*/
int CacheConn::Init()
{if (m_pContext)	// 非空,连接是正常的{return 0;}// 1s 尝试重连一次uint64_t cur_time = (uint64_t)time(NULL);if (cur_time < m_last_connect_time + 1) 		// 重连尝试 间隔1秒 {printf("cur_time:%lu, m_last_connect_time:%lu\n", cur_time, m_last_connect_time);return 1;}// printf("m_last_connect_time = cur_time\n");m_last_connect_time = cur_time;// 1000ms超时struct timeval timeout = {0, 1000000};// 建立连接后使用 redisContext 来保存连接状态。// redisContext 在每次操作后会修改其中的 err 和  errstr 字段来表示发生的错误码(大于0)和对应的描述。m_pContext = redisConnectWithTimeout(m_server_ip.c_str(), m_server_port, timeout);if (!m_pContext || m_pContext->err){if (m_pContext){log_error("redisConnect failed: %s\n", m_pContext->errstr);redisFree(m_pContext);m_pContext = NULL;}else{log_error("redisConnect failed\n");}return 1;}redisReply *reply;// 验证if (!m_password.empty()){reply = (redisReply *)redisCommand(m_pContext, "AUTH %s", m_password.c_str());if (!reply || reply->type == REDIS_REPLY_ERROR){log_error("Authentication failure:%p\n", reply);if (reply)freeReplyObject(reply);return -1;}else{// log_info("Authentication success\n");}freeReplyObject(reply);}reply = (redisReply *)redisCommand(m_pContext, "SELECT %d", 0);if (reply && (reply->type == REDIS_REPLY_STATUS) && (strncmp(reply->str, "OK", 2) == 0)){freeReplyObject(reply);return 0;}else{if (reply)log_error("select cache db failed:%s\n", reply->str);return 2;}
}void CacheConn::DeInit()
{if (m_pContext){redisFree(m_pContext);m_pContext = NULL;}
}const char *CacheConn::GetPoolName()
{return m_pool_name.c_str();
}string CacheConn::get(string key)
{string value;if (Init()){return value;}redisReply *reply = (redisReply *)redisCommand(m_pContext, "GET %s", key.c_str());if (!reply){log_error("redisCommand failed:%s\n", m_pContext->errstr);redisFree(m_pContext);m_pContext = NULL;return value;}if (reply->type == REDIS_REPLY_STRING){value.append(reply->str, reply->len);}freeReplyObject(reply);return value;
}string CacheConn::set(string key, string &value)
{string ret_value;if (Init()){return ret_value;}// 返回的结果存放在redisReplyredisReply *reply = (redisReply *)redisCommand(m_pContext, "SET %s %s", key.c_str(), value.c_str());if (!reply){log_error("redisCommand failed:%s\n", m_pContext->errstr);redisFree(m_pContext);m_pContext = NULL;return ret_value;}ret_value.append(reply->str, reply->len);freeReplyObject(reply); // 释放资源return ret_value;
}string CacheConn::setex(string key, int timeout, string value)
{string ret_value;if (Init()){return ret_value;}redisReply *reply = (redisReply *)redisCommand(m_pContext, "SETEX %s %d %s", key.c_str(), timeout, value.c_str());if (!reply){log_error("redisCommand failed:%s\n", m_pContext->errstr);redisFree(m_pContext);m_pContext = NULL;return ret_value;}ret_value.append(reply->str, reply->len);freeReplyObject(reply);return ret_value;
}bool CacheConn::mget(const vector<string> &keys, map<string, string> &ret_value)
{if (Init()){return false;}if (keys.empty()){return false;}string strKey;bool bFirst = true;for (vector<string>::const_iterator it = keys.begin(); it != keys.end(); ++it){if (bFirst){bFirst = false;strKey = *it;}else{strKey += " " + *it;}}if (strKey.empty()){return false;}strKey = "MGET " + strKey;redisReply *reply = (redisReply *)redisCommand(m_pContext, strKey.c_str());if (!reply){log_info("redisCommand failed:%s\n", m_pContext->errstr);redisFree(m_pContext);m_pContext = NULL;return false;}if (reply->type == REDIS_REPLY_ARRAY){for (size_t i = 0; i < reply->elements; ++i){redisReply *child_reply = reply->element[i];if (child_reply->type == REDIS_REPLY_STRING){ret_value[keys[i]] = child_reply->str;}}}freeReplyObject(reply);return true;
}bool CacheConn::isExists(string &key)
{if (Init()){return false;}redisReply *reply = (redisReply *)redisCommand(m_pContext, "EXISTS %s", key.c_str());if (!reply){log_error("redisCommand failed:%s\n", m_pContext->errstr);redisFree(m_pContext);m_pContext = NULL;return false;}long ret_value = reply->integer;freeReplyObject(reply);if (0 == ret_value){return false;}else{return true;}
}long CacheConn::del(string &key)
{if (Init()){return 0;}redisReply *reply = (redisReply *)redisCommand(m_pContext, "DEL %s", key.c_str());if (!reply){log_error("redisCommand failed:%s\n", m_pContext->errstr);redisFree(m_pContext);m_pContext = NULL;return 0;}long ret_value = reply->integer;freeReplyObject(reply);return ret_value;
}long CacheConn::hdel(string key, string field)
{if (Init()){return 0;}redisReply *reply = (redisReply *)redisCommand(m_pContext, "HDEL %s %s", key.c_str(), field.c_str());if (!reply){log_error("redisCommand failed:%s\n", m_pContext->errstr);redisFree(m_pContext);m_pContext = NULL;return 0;}long ret_value = reply->integer;freeReplyObject(reply);return ret_value;
}string CacheConn::hget(string key, string field)
{string ret_value;if (Init()){return ret_value;}redisReply *reply = (redisReply *)redisCommand(m_pContext, "HGET %s %s", key.c_str(), field.c_str());if (!reply){log_error("redisCommand failed:%s\n", m_pContext->errstr);redisFree(m_pContext);m_pContext = NULL;return ret_value;}if (reply->type == REDIS_REPLY_STRING){ret_value.append(reply->str, reply->len);}freeReplyObject(reply);return ret_value;
}bool CacheConn::hgetAll(string key, map<string, string> &ret_value)
{if (Init()){return false;}redisReply *reply = (redisReply *)redisCommand(m_pContext, "HGETALL %s", key.c_str());if (!reply){log_error("redisCommand failed:%s\n", m_pContext->errstr);redisFree(m_pContext);m_pContext = NULL;return false;}if ((reply->type == REDIS_REPLY_ARRAY) && (reply->elements % 2 == 0)){for (size_t i = 0; i < reply->elements; i += 2){redisReply *field_reply = reply->element[i];redisReply *value_reply = reply->element[i + 1];string field(field_reply->str, field_reply->len);string value(value_reply->str, value_reply->len);ret_value.insert(make_pair(field, value));}}freeReplyObject(reply);return true;
}long CacheConn::hset(string key, string field, string value)
{if (Init()){return -1;}redisReply *reply = (redisReply *)redisCommand(m_pContext, "HSET %s %s %s", key.c_str(), field.c_str(), value.c_str());if (!reply){log_error("redisCommand failed:%s\n", m_pContext->errstr);redisFree(m_pContext);m_pContext = NULL;return -1;}long ret_value = reply->integer;freeReplyObject(reply);return ret_value;
}long CacheConn::hincrBy(string key, string field, long value)
{if (Init()){return -1;}redisReply *reply = (redisReply *)redisCommand(m_pContext, "HINCRBY %s %s %ld", key.c_str(), field.c_str(), value);if (!reply){log_error("redisCommand failed:%s\n", m_pContext->errstr);redisFree(m_pContext);m_pContext = NULL;return -1;}long ret_value = reply->integer;freeReplyObject(reply);return ret_value;
}long CacheConn::incrBy(string key, long value)
{if (Init()){return -1;}redisReply *reply = (redisReply *)redisCommand(m_pContext, "INCRBY %s %ld", key.c_str(), value);if (!reply){log_error("redis Command failed:%s\n", m_pContext->errstr);redisFree(m_pContext);m_pContext = NULL;return -1;}long ret_value = reply->integer;freeReplyObject(reply);return ret_value;
}string CacheConn::hmset(string key, map<string, string> &hash)
{string ret_value;if (Init()){return ret_value;}int argc = hash.size() * 2 + 2;const char **argv = new const char *[argc];if (!argv){return ret_value;}argv[0] = "HMSET";argv[1] = key.c_str();int i = 2;for (map<string, string>::iterator it = hash.begin(); it != hash.end(); it++){argv[i++] = it->first.c_str();argv[i++] = it->second.c_str();}redisReply *reply = (redisReply *)redisCommandArgv(m_pContext, argc, argv, NULL);if (!reply){log_error("redisCommand failed:%s\n", m_pContext->errstr);delete[] argv;redisFree(m_pContext);m_pContext = NULL;return ret_value;}ret_value.append(reply->str, reply->len);delete[] argv;freeReplyObject(reply);return ret_value;
}bool CacheConn::hmget(string key, list<string> &fields, list<string> &ret_value)
{if (Init()){return false;}int argc = fields.size() + 2;const char **argv = new const char *[argc];if (!argv){return false;}argv[0] = "HMGET";argv[1] = key.c_str();int i = 2;for (list<string>::iterator it = fields.begin(); it != fields.end(); it++){argv[i++] = it->c_str();}redisReply *reply = (redisReply *)redisCommandArgv(m_pContext, argc, (const char **)argv, NULL);if (!reply){log_error("redisCommand failed:%s\n", m_pContext->errstr);delete[] argv;redisFree(m_pContext);m_pContext = NULL;return false;}if (reply->type == REDIS_REPLY_ARRAY){for (size_t i = 0; i < reply->elements; i++){redisReply *value_reply = reply->element[i];string value(value_reply->str, value_reply->len);ret_value.push_back(value);}}delete[] argv;freeReplyObject(reply);return true;
}long CacheConn::incr(string key)
{if (Init()){return -1;}redisReply *reply = (redisReply *)redisCommand(m_pContext, "INCR %s", key.c_str());if (!reply){log_error("redis Command failed:%s\n", m_pContext->errstr);redisFree(m_pContext);m_pContext = NULL;return -1;}long ret_value = reply->integer;freeReplyObject(reply);return ret_value;
}long CacheConn::decr(string key)
{if (Init()){return -1;}redisReply *reply = (redisReply *)redisCommand(m_pContext, "DECR %s", key.c_str());if (!reply){log_error("redis Command failed:%s\n", m_pContext->errstr);redisFree(m_pContext);m_pContext = NULL;return -1;}long ret_value = reply->integer;freeReplyObject(reply);return ret_value;
}long CacheConn::lpush(string key, string value)
{if (Init()){return -1;}redisReply *reply = (redisReply *)redisCommand(m_pContext, "LPUSH %s %s", key.c_str(), value.c_str());if (!reply){log_error("redisCommand failed:%s\n", m_pContext->errstr);redisFree(m_pContext);m_pContext = NULL;return -1;}long ret_value = reply->integer;freeReplyObject(reply);return ret_value;
}long CacheConn::rpush(string key, string value)
{if (Init()){return -1;}redisReply *reply = (redisReply *)redisCommand(m_pContext, "RPUSH %s %s", key.c_str(), value.c_str());if (!reply){log_error("redisCommand failed:%s\n", m_pContext->errstr);redisFree(m_pContext);m_pContext = NULL;return -1;}long ret_value = reply->integer;freeReplyObject(reply);return ret_value;
}long CacheConn::llen(string key)
{if (Init()){return -1;}redisReply *reply = (redisReply *)redisCommand(m_pContext, "LLEN %s", key.c_str());if (!reply){log_error("redisCommand failed:%s\n", m_pContext->errstr);redisFree(m_pContext);m_pContext = NULL;return -1;}long ret_value = reply->integer;freeReplyObject(reply);return ret_value;
}bool CacheConn::lrange(string key, long start, long end, list<string> &ret_value)
{if (Init()){return false;}redisReply *reply = (redisReply *)redisCommand(m_pContext, "LRANGE %s %d %d", key.c_str(), start, end);if (!reply){log_error("redisCommand failed:%s\n", m_pContext->errstr);redisFree(m_pContext);m_pContext = NULL;return false;}if (reply->type == REDIS_REPLY_ARRAY){for (size_t i = 0; i < reply->elements; i++){redisReply *value_reply = reply->element[i];string value(value_reply->str, value_reply->len);ret_value.push_back(value);}}freeReplyObject(reply);return true;
}bool CacheConn::flushdb()
{bool ret = false;if (Init()){return false;}redisReply *reply = (redisReply *)redisCommand(m_pContext, "FLUSHDB");if (!reply){log_error("redisCommand failed:%s\n", m_pContext->errstr);redisFree(m_pContext);m_pContext = NULL;return false;}if (reply->type == REDIS_REPLY_STRING && strncmp(reply->str, "OK", 2) == 0){ret = true;}freeReplyObject(reply);return ret;
}
///
CachePool::CachePool(const char *pool_name, const char *server_ip, int server_port, int db_index,const char *password, int max_conn_cnt)
{m_pool_name = pool_name;m_server_ip = server_ip;m_server_port = server_port;m_db_index = db_index;m_password = password;m_max_conn_cnt = max_conn_cnt;m_cur_conn_cnt = MIN_CACHE_CONN_CNT;
}CachePool::~CachePool()
{m_free_notify.Lock();for (list<CacheConn *>::iterator it = m_free_list.begin(); it != m_free_list.end(); it++){CacheConn *pConn = *it;delete pConn;}m_free_list.clear();m_cur_conn_cnt = 0;m_free_notify.Unlock();
}int CachePool::Init()
{for (int i = 0; i < m_cur_conn_cnt; i++){CacheConn *pConn = new CacheConn(m_server_ip.c_str(), m_server_port,m_db_index, m_password.c_str(), m_pool_name.c_str());if (pConn->Init()){delete pConn;return 1;}m_free_list.push_back(pConn);}log_info("cache pool: %s, list size: %lu\n", m_pool_name.c_str(), m_free_list.size());return 0;
}CacheConn *CachePool::GetCacheConn()
{m_free_notify.Lock();while (m_free_list.empty()){if (m_cur_conn_cnt >= m_max_conn_cnt){m_free_notify.Wait();}else{CacheConn *p_cache_conn = new CacheConn(m_server_ip.c_str(), m_server_port,m_db_index, m_password.c_str(), m_pool_name.c_str());int ret = p_cache_conn->Init();if (ret){log_error("Init CacheConn failed\n");delete p_cache_conn;m_free_notify.Unlock();return NULL;}else{m_free_list.push_back(p_cache_conn);m_cur_conn_cnt++;log_info("new cache connection: %s, conn_cnt: %d\n", m_pool_name.c_str(), m_cur_conn_cnt);}}}CacheConn *pConn = m_free_list.front();m_free_list.pop_front();m_free_notify.Unlock();return pConn;
}void CachePool::RelCacheConn(CacheConn *p_cache_conn)
{m_free_notify.Lock();list<CacheConn *>::iterator it = m_free_list.begin();for (; it != m_free_list.end(); it++){if (*it == p_cache_conn){break;}}if (it == m_free_list.end()){m_free_list.push_back(p_cache_conn);}m_free_notify.Signal();m_free_notify.Unlock();
}

相关文章:

Redis的C客户端(hiredis库)使用

文章目录 1、Ubuntu安装redis服务端2、hiredis库的安装3、同步API接口的使用3.1、连接redis数据库redisConnect3.2、发送需要执行的命令redisCommand3.3、redisCommandArgv函数3.4、redisAppendCommand*函数支持管道命令3.5、释放资源3.6、同步连接代码 3.7、异步连接4、redis连…...

光引擎、光模块、光器件之间的关系和区别

最近小编有收到一些用户问“光引擎、光模块、光器件之间的关系和区别&#xff1f;”&#xff0c;众所周知光通信技术一直在不断演进&#xff0c;为满足不断增长的数据传输需求提供了强大的解决方案。而光通信系统中&#xff0c;光引擎、光模块和光器件是关键的组成部分&#xf…...

【办公-excel】两个时间相减 (二) - 带毫秒的时间进行相减操作

一、使用内部函数 1.1 效果展示 TEXT(((RIGHT(TEXT(B2,"yyyy-mm-dd hh:mm:ss.000"),LEN(TEXT(B2,"yyyy-mm-dd hh:mm:ss.000"))-FIND(".",TEXT(B2,"yyyy-mm-dd hh:mm:ss.000")))-RIGHT(TEXT(A2,"yyyy-mm-dd hh:mm:ss.000"),…...

二次封装View Design的table组件,实现宽度自适应,内容在一行展示

由于table组件本身并不支持宽度自适应&#xff0c;但实际项目需要&#xff0c;而且多处有用到table组件&#xff0c;所以尝试着自己来二次封装一下组件 想法 刚开始的想法很简单&#xff0c;就是获取每一列中数据和标题在表格中的长度&#xff0c;然后将当中最大的长度作为该列…...

Node.js代码漏洞扫描工具介绍——npm audit

npm audit 运行安全检查 主要作用&#xff1a;检查命令将项目中配置的依赖项的描述提交到默认注册中心&#xff0c;并要求报告已知漏洞。如果发现任何漏洞&#xff0c;则将计算影响和适当的补救措施。如果 fix 提供了参数&#xff0c;则将对包树应用补救措施。 具体参考&#x…...

node.js知识系列(3)-每天了解一点

目录 1. Express.js 中的中间件2. 处理路由和请求3. RESTful 路由4. 身份验证和授权5. 视图引擎6. 错误处理中间件7. 文件上传处理8. Cookie 和 Session 管理9. 路由参数和查询参数10. 处理跨域请求&#xff08;CORS&#xff09; &#x1f44d; 点赞&#xff0c;你的认可是我创…...

Zabbix监控系统 自定义监控项、自动发现与自动注册

Zabbix监控系统 自定义监控项、自动发现与自动注册 一、自定义监控内容部署实例二、zabbix 自动发现与自动注册部署实例2.1 部署zabbix自动发现 一、自定义监控内容部署实例 案列&#xff1a;自定义监控客户端服务器登录的人数 需求&#xff1a;限制登录人数不超过 3 个&#…...

Python信号之分享

在了解了Linux的信号基础之后&#xff0c;Python标准库中的signal包就很容易学习和理解。signal包负责在Python程序内部处理信号&#xff0c;典型的操作包括预设信号处理函数&#xff0c;暂停并等待信号&#xff0c;以及定时发出SIGALRM等。要注意&#xff0c;signal包主要是针…...

环信web、uniapp、微信小程序SDK报错详解---登录篇

项目场景&#xff1a; 记录对接环信sdk时遇到的一系列问题&#xff0c;总结一下避免大家再次踩坑。这里主要针对于web、uniapp、微信小程序在对接环信sdk时遇到的问题。主要针对报错400、404、401、40 (一) 登录用户报400 原因分析&#xff1a; 从console控制台输出及networ…...

DAZ To UMA⭐五.模型在Blender中的配置教程

文章目录 🟥 创建符合UMA的材质球属性1️⃣ 合并材质球🎁 选择材质球🎁 合并材质球🎁 删除多余材质球2️⃣ 将身体按材质球拆分🎁 进入身体编辑模式🎁 全选身体🎁 按材质分割身体🎁 重命名不同部位3️⃣ 将其余部位进行拆分🟧 更正选择缩放🟩 更新骨骼结构…...

网络安全工具汇总

网络安全工具汇总 1. 前言1.1. 工具提供 2. 漏洞库3. 杂项3.1. topology-scanner3.2. MDUT3.3. 404 4. 插件工具4.1. 浏览器插件4.1.1. Heimdallr4.1.2. HackTools4.1.3. SwitchyOmega4.1.4. fofa_view4.1.5. mitaka 4.2. CS插件4.2.1. taowu-cobalt_strike4.2.2. OLa4.2.3. Z1…...

day-65 代码随想录算法训练营(19)图论 part 04

463.岛屿的周长 分析&#xff1a; 1.陆地的旁边是海面&#xff0c;存在周长2.陆地在边界上&#xff0c;存在周长 思路一&#xff1a;深度优先遍历 1.通过记录访问情况来访问数据 class Solution { public:int direct[4][2]{{0,1},{0,-1},{1,0},{-1,0}};int res0;void dfs(…...

C++ - 完美语义(右值引用的中篇) - lambda表达式

前言 之前对右值引用的理解&#xff0c;用使用场景做了详细说明&#xff0c;具体看博客&#xff1a;C - 右值引用 和 移动拷贝-CSDN博客 在 有值引用 当中还有一个 完美转发&#xff0c;请看本篇博客。 完美转发 我们现在看这个例子&#xff1a; void Fun(int& x) { …...

常见排序算法详解

目录 排序的相关概念 排序&#xff1a; 稳定性&#xff1a; 内部排序&#xff1a; 外部排序&#xff1a; 常见的排序&#xff1a; 常见排序算法的实现 插入排序&#xff1a; 基本思想&#xff1a; 直…...

监控搭建-Prometheus

监控搭建-Prometheus 1、背景2、目标3、选型4、Prometheus4.1、介绍4.2、架构4.3、构件4.4、运行机制4.5、环境介绍4.6、数据准备4.7、网络策略4.7.1、主机端口放行4.7.2、设备端口放行 4.8、部署4.9、验证4.10、配置 1、背景 随着项目信息化进程的推进&#xff0c;操作系统、…...

指纹浏览器开发指南-EasyBR

想开发一款指纹浏览器&#xff0c;指纹浏览器名字叫做EasyBR&#xff0c;大致构思了下开发的步骤。 EasyBR指纹浏览器开发指南&#xff1a; 后台技术、前端技术和指纹修改 简介&#xff1a; EasyBR指纹浏览器是一款旨在提供个性化服务和广告定位的浏览器&#xff0c;通过收…...

qml入门

window import QtQuick 2.15 import QtQuick.Window 2.15 import QtQuick.Controls 2.5Window { //root控件&#xff0c;父窗口是主界面width: 640height: 480visible: true//相对于父控件的偏移量x: 100y:100minimumWidth: 400 //最小宽度minimumHeight: 300 //最小高度ma…...

一文熟练使用python修改Excel中的数据

使用python修改Excel中的内容 1.初级修改 1.1 openpyxl库的功能&#xff1a; openpyxl模块是一个读写Excel 2010文档的Python库&#xff0c;如果要处理更早格式的Excel文档&#xff0c;需要用到额外的库&#xff0c;例如Xlwings。openpyxl是一个比较综合的工具&#xff0c;能…...

java Spring Boot在配置文件中关闭热部署

之前更大家一起搭建了一个热部署的开发环境 但是 大家要清楚一个情况 我们线上程序运行突然内部发生变化这是不可能的。 所以 他就只会对我们开发环境有效 是否开启 我们可以通过 application配置文件来完成 我这里是yml格式的 参考代码如下 spring:devtools:restart:enabled…...

【物联网】Arduino+ESP8266物联网开发(一):开发环境搭建 安装Arduino和驱动

ESP8266物联网开发 1.开发环境安装 开发软件下载地址&#xff1a; 链接: https://pan.baidu.com/s/1BaOY7kWTvh4Obobj64OHyA?pwd3qv8 提取码: 3qv8 1.1 安装驱动 将ESP8266连接到电脑上&#xff0c;安装ESP8266驱动CP210x 安装成功后&#xff0c;打开设备管理器&#xff0c…...

自定义UI对象转流程节点

自定义UI对象转流程节点 实体自定义对象转bpmn activitiy学习 (动态加签&#xff0c;动态流程图&#xff0c;指定节点跳转&#xff0c;指定多人节点跳转) 前端页面仿的这个 提供一个思路 实体 ActivitiValueVo import io.swagger.annotations.ApiModel; import io.swagger.a…...

P1-P5_动手学深度学习-pytorch(李沐版,粗浅的笔记)

目录 预告  1.学习深度学习的关键是动手  2.什么是《动手学深度学习》  3.曾经推出的版本&#xff08;含github链接&#xff09; 一、课程安排  1.目标  2.内容  3.上课形式  4.你将学到什么  5.资源 二、深度学习的介绍  1.AI地图  2.深度学习在一些应用上…...

Android Studio修改模拟器AVD Manger目录

Android Studio修改虚拟机AVD Manger目录 1、在AS的设备管理器Device Manager中删除原来创建的所有虚拟机&#xff08;Android Virtual Device&#xff09;&#xff1b; 2、新建一个自定义的AVD目录&#xff0c;例如&#xff1a;D:\Android\AndroidAVD 3、在高级系统设置中增加…...

STM32--MQ2烟雾传感器

本文主要介绍STM32F103C8T6和烟雾传感器模块的控制算法 简介 烟雾模块选用MQ-2气体传感器&#xff0c;根据传感器的电导率随空气中可燃气体浓度的增加而增大的特性检测空气中可燃气体&#xff0c;然后将电导率的变化转换成对应的电信号 MQ系列烟雾传感分类如下&#xff1a; 该…...

GitHub要求开启2FA,否则不让用了。

背景 其实大概在一个多月前&#xff0c;在 GitHub 网页端以及邮箱里都被提示&#xff1a;要求开启 2FA &#xff0c;即双因子认证&#xff1b;但是当时由于拖延症和侥幸心理作祟&#xff0c;直接忽略了相关信息&#xff0c;毕竟“又不是不能用”。。 只到今天发现 GitHub 直接…...

Python 编程基础 | 第三章-数据类型 | 3.6、元组

一、元组 Python 的元组与列表类似&#xff0c;不同之处在于元组的元素不能修改。元组使用小括号&#xff0c;列表使用方括号。 1、创建元组 元组创建很简单&#xff0c;只需要在括号中添加元素&#xff0c;并使用逗号隔开即可&#xff0c;例如&#xff1a; tup1 (physics, ch…...

2023/10/7 -- ARM

【程序状态寄存器读写指令】 1.指令码以及格式 mrs:读取CPSR寄存器的值 mrs 目标寄存器 CPSR&#xff1a;读取CPSR的数值保存到目标寄存器中msr:修改CPSR寄存器的数值msr CPSR,第一操作数:将第一操作数的数值保存到CPSR寄存器中//修改CPSR寄存器&#xff0c;也就表示程序的状…...

yolov5加关键点回归

文章目录 一、数据1&#xff09;数据准备2&#xff09;标注文件说明 二、基于yolov5-face 修改自己的yolov5加关键点回归1、dataloader,py2、augmentations.py3、loss.py4、yolo.py 一、数据 1&#xff09;数据准备 1、手动创建文件夹: yolov5-face-master/data/widerface/tr…...

untitle

实用的科研图形美化处理教程分享 ​ 显微照片排版标记 除了统计图表之外&#xff0c;显微照片也是文章中必不可少的实验结果呈现方式。除了常规实验的各种组织切片照片&#xff0c;在空间转录组文章中显微照片更是常见。显微照片的呈现方式也是有讲究的&#xff0c;比如对照片…...

《论文阅读》监督对抗性对比学习在对话中的情绪识别 ACL2023

《论文阅读》监督对抗性对比学习在对话中的情绪识别 前言摘要相关知识最坏样本干扰监督对比学习生成式对抗网络纳什均衡琴森香农散度范式球模型架构监督对抗性对比学习模型结构图实验结果问题前言 你是否也对于理解论文存在困惑? 你是否也像我之前搜索论文解读,得到只是中文…...