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

news2024/11/17 3:50:17

文章目录

  • 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 seconds
    c = 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。如果该键不存在,它将被创建并初始化为0
        reply = 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.conf

ldconfig -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);
    // 删除某个key
    long 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);
	//原子加减1
    long 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 index

	int			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 10

CacheConn::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;
	}
	// 返回的结果存放在redisReply
	redisReply *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();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1073388.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker+jenkinsPipeline 运行实现python自动化测试

一、实现思路 在 Linux 服务器安装 docker创建 jenkins 容器jenkins 中创建 pipeline 项目根据自动化项目依赖包构建 python 镜像(构建自动化 python 环境)运行新的 python 容器&#xff0c;执行 jenkins 从仓库中拉下来的自动化项目执行完成之后删除容器 二、环境准备 Lin…

centos7上 ytalk 命令找不到?yum没有ytalk的软件包

1.描述 [rootoomcserver ~]# yum search ytalk Loaded plugins: langpacks, product-id, search-disabled-repos, subscription-manager This system is not registered with an entitlement server. You can use subscription-manager to register. Repodata is over 2 weeks …

一文搞懂到底什么是元宇宙

一、背景 2021年&#xff0c;“元宇宙”是科技界的开端。 2021”元宇宙”这个词在Facebook更名后被点燃了&#xff0c;无疑是21世纪科技界最爆的起点。各式各样的定义、解读都出现了&#xff0c;有人说它是炒作&#xff0c;甚至是骗局&#xff0c;但也有人说它就是互联网的未…

公众号点击原文下载文件怎么设置

作为微信生态下的产品&#xff0c;公众号推文中添加外部超链接的能力&#xff0c;多年一直没有开放给广大的运营者&#xff0c;只有开通了微信支付的服务号&#xff0c;才能在公众号文章正文添加链接&#xff0c;对于经常在公众号分享课件资料或者需要添加各种申请表的运营者来…

信号采样基本概念 —— 7.数模转换(DAC ADC)

文章目录 数字转模拟&#xff08;DAC&#xff09;一个简单的数字转模拟方案 模拟转数字&#xff08;ADC&#xff09; 数字转模拟&#xff08;DAC&#xff09; 通常来说&#xff0c;我们在设备上存储的数据是以二进制进行传输的&#xff0c;但是当我们试图将数据传输到更远的地…

《深入理解计算机系统》(1):系统组成

一、系统硬件组成 1、控制器&#xff08;CPU&#xff09;&#xff1a;解释和执行内存中的指令 &#xff08;1&#xff09;、控制器 程序控制器&#xff1a;指令指针&#xff0c;指向主存中的机器语言指令&#xff0c;为一个字大小的存储设备或寄存器。 指令寄存器、指令译码器、…

基于docker+Keepalived+Haproxy高可用前后的分离技术

基于dockerKeepalivedHaproxy高可用前后端分离技术 架构图 服务名docker-ip地址docker-keepalived-vip-iphaproxy-01docker-ip自动分配 未指定ip192.168.31.252haproxy-02docker-ip自动分配 未指定ip192.168.31.253 安装haproxy 宿主机ip 192.168.31.254 宿主机keepalived虚…

k8s-9 ingress-nginx 特性

TLS加密 创建证书 测试 auth认证 创建认证文件 rewrite重定向 进入域名 会自动重定向hostname.html 示例二&#xff1a; 测试 后面必须跟westos 这个关键字 canary金丝雀发布 基于header灰度 场景&#xff1a;版本的升级迭代&#xff0c;比如一个service 升级到另…

react学习之---jsx转成虚拟dom的过程

jsx----经过Bebal编译返回可供React.createElement()可调用的对象—React.createElement调用后生成虚拟dom—diff算法—生成新的真实dom 经过babel编译&#xff1a; import {greet} from ‘./utils’; const App {greet(‘scott’)}; ReactDOM.render(App, document.getEl…

芯引擎·新力量 | 第五届浦东新区长三角集成电路技能邀请赛圆满落幕

9月27日&#xff0c; 2023年全国工业和信息化技术技能大赛上海选拔赛、上海职工职业技能系列赛暨第五届浦东新区长三角集成电路技能邀请赛汽车芯片设计竞赛及颁奖典礼在上海集成电路设计产业园举办&#xff0c;上海市经济和信息化委员会人事教育处处长黄春华&#xff0c;上海市…

ESP32网络开发实例-Web服务器控制GPIO

Web服务器控制GPIO 文章目录 Web服务器控制GPIO1、软件准备2、硬件准备3、代码实现本文将介绍如何Arduino IDE编程环境创建一个具有ESP32的独立web服务器,ESP32控制输出(两个LED)。可以通过作为本地网络上的浏览器的任何设备访问。下面将逐步介绍如何创建web服务器以及代码的…

【Redis】Redis的几个应用场景(string数据类型的应用)

Redis的几个应用场景 缓存功能 ⽐较典型的缓存使⽤场景&#xff0c;其中Redis作为缓冲层&#xff0c;MySQL作为存储层&#xff0c;绝⼤部分请求的数据都是从Redis中获取。由于Redis具有⽀撑⾼并发的特性&#xff0c;所以缓存通常能起到加速读写和降低后端压⼒的作⽤。 计数功能…

MS2400隔离式调制器可pin对pin兼容AD7400

MS2400是一款二阶Σ-Δ调制器&#xff0c;集成片上数字隔离器&#xff0c;能将模拟输入信号转换为高速1位码流。可pin对pin兼容AD7400&#xff0c;可兼容AMC1303。调制器对输入信号连续采样&#xff0c;无需外部采样保持电路。模拟信号输入满量程为320mV&#xff0c;转换后的数…

物联网入门系列(一):快速搭建一站式数据存储与实时分析平台

本系列文章将以教程形式介绍物联网领域用户在安装部署、分布式数据库设计、数据写入和查询、流计算和高可用测试等过程中的常见问题、相应的解决方案与注意事项&#xff0c;以帮助新用户快速入门&#xff0c;体验 DolphinDB 的极致性能。 本次介绍内容为——如何快速搭建一站式…

pgpool密码验证失败问题

当使用命令 psql -p 9999 -U postgres -d postgres 登录pgpool时 出现错误&#xff1a;md5 authentication failed 认证问题 推测问题&#xff1a; 1.密码设置错误 2.密码加密方式不一致 在主库操作 查看用户密码 select rolname,rolpassword from pg_authid 可以看出之前密码…

3561-24-8|荧光染料6-fam(Br4)|可作为成像剂

产品简介&#xff1a;6-fam(Br4)是一种荧光染料&#xff0c;广泛应用于生物医学领域中的荧光探针、标记物和成像剂等方面。其分子结构独特&#xff0c;具有良好的荧光量子产率和稳定性&#xff0c;能够在生物体内快速、准确地标记和追踪生物分子和细胞。其优异的荧光性能和化学…

(十四)VBA常用基础知识:当前excel文件所有sheet循环,获取sheet名,获取最大行数最大列数

获取当前excel的所有sheet以及sheet名 Sub test()Dim ws As WorksheetFor Each ws In Worksheetsws.ActivateDebug.Print ws.NameNext ws End Sub 输出结果&#xff1a; Sheet1 Sheet2 Sheet3 Sheet4 Sheet52.获取最大行数 首先要明白取最大行的方式&#xff0c;为什么这样取…

Go:关于 Channel

文章目录 写在前面内容模型图与代码发送流程接收流程 写在前面 本篇主要是通过 Channel 的模型图&#xff0c;对 Channel 的原理做一个基本的概述 内容 模型图与代码 我们先来看下 Channel 的模型图&#xff1a; 以上的图是一个简要的模型图&#xff0c;意味着丢失一些细节…

【Spring Boot】SpringBoot 单元测试

SpringBoot 单元测试 一. 什么是单元测试二. 单元测试的好处三. Spring Boot 单元测试单元测试的实现步骤 一. 什么是单元测试 单元测试&#xff08;unit testing&#xff09;&#xff0c;是指对软件中的最⼩可测试单元进⾏检查和验证的过程就叫单元测试。 二. 单元测试的好处…