redis连接池之同步

今天国泉分享在项目中redis同步连接池的设计思路,异步方式后面整理好后在上博。

 

连接池是什么?

  国泉的理解是程序在初始化时就建立多个长连接对象,程序运行中循环利用,节省新建连接时带来的开销,充分利用多核优势,这样性能瓶颈就会在redis,而不是在我们这。

 

那怎么设计呢?国泉的想法是这样的

  1、我们应该有一个redis操作类取名redisClient,他有连接、断开redis-server、操作(具体业务)的接口,他就是一个长连接对象,一个可以干活的工人。

  2、接下来我们创建N个工人来服务,这样就需要一个容器来存放这N个对象,国泉的想法是用queue队列,优点是用的时候不需要去遍历,缺点是有指针变量的内存开销,我把它比喻成排好队等着去干活的工人。

  3、到这里这伙人需要一个头,那就是一个包工头来给他们安排工作,取名redisPool,当一个请求过来,若他需要redis操作,他就从包工头这里要拿走一个(pop)工人,当他用完后将这个工人(push)还给包工头,如此反复。  

 

有了上面的思路后,我们就可以先着手设计这两个类了,c++操作redis的库有很多,这里国泉用的是hiredis,下面我们先来设计redisClient,他能支持连接、断开、SET、DEL字符串操作redis的接口

 1 #pragma once
 2 
 3 #include <hiredis/hiredis.h>
 4 #include <string>
 5 
 6 
 7     class RedisClient
 8     {
 9     public:
10         RedisClient()
11         {
12             _port = 0;
13             _context = 0;
14         }
15     public:
16         bool Connect(const char *ip, int port)
17         {
18             _ip = ip;
19             _port = port;
20             _context = ::redisConnect(ip, port);
21             if (_context && _context->err)
22                 return false;
23         
24             return true;
25         }
26         void CloseConnect()
27         {
28             ::redisFree(_context);
29         }
30     public:
31         bool setString(const char * key, const char * value)
32         {
33             redisReply *_reply = (redisReply*)::redisCommand(_context, "set %s %s",key,value);
34             bool b = false;
35             if(_reply)
36             {
37                 b = (_reply->type == REDIS_REPLY_STATUS && strcasecmp(_reply->str, "OK") == 0);
38                 ::freeReplyObject(_reply);
39                 _reply = NULL;
40             }
41             return b;
42         }
43         bool getString(const char * key, std::string & value)
44         {
45             redisReply *_reply = (redisReply*)::redisCommand(_context, "get %s",key);
46             bool b = false;
47             if(_reply && _reply->type == REDIS_REPLY_STRING)
48             {
49                 value = _reply->str;
50                 ::freeReplyObject(_reply);
51                 _reply = NULL;
52                 b = true;
53             }
54             return b;
55         }
56             
57     private:
58         redisContext * _context;
59         std::string _ip;
60         int _port;
61     };

 

 

接下来我们的包工头类redisPool

由于std::queue不是线程安全的,需要让他变成线程安全,这里我封装了一个线程安全队列concurrentQueuehttps://www.cnblogs.com/GuoQuanLiu/articles/9498425.html

 1 #include "concurrentQueue.h"
 2 class RedisPool
 3 {
 4 public:
 5     RedisPool();
 6     ~RedisPool();
 7 
 8     void setClientNum(int num)
 9     {_client_num=num;}
10 
11     bool start()
12     {
13         for(int i = 0;i < _client_num;i++)
14         {
15             RedisClientPtr ptr(new RedisClient());
16             if(ptr->Connect(configInfo::getInstance().redis_ip(),configInfo::getInstance().redis_port()))
17                 _clients.push(ptr);
18         }
19 
20         return !_clients.empty();
21     }
22 
23     RedisClientPtr getNextClient()
24     {
25         if(_clients.empty())
26         {
27             return RedisClientPtr(new RedisClient());
28         }
29         
30         RedisClientPtr ptr;
31         _clients.pop(ptr);
32         if(ptr == nullptr)
33         {
34             return RedisClientPtr(new RedisClient());
35         }
36         return ptr;
37     }    
38 
39     void push(RedisClientPtr rc)
40     {_clients.push(rc);}
41 
42 private:
43     int _client_num;
44     concurrentQueue<RedisClientPtr> _clients;
45 };

 

使用方式

#include "RedisPool"

//创建一个全局或者单例包工头
RedisPool g_rdPool;

//假设有一个登录协议,利用构造和析构获取和归还工人,最好写基类里,这样不会出错。
class login
{
public:
    login()
    {
        _ptr = nullptr;
    }
    ~login()
    {
        if(_ptr)
            g_rdPool.push(_ptr);
    }
    
    void action()
    {
        getRedis()->setString("20180818","GuoQuan");
        std::string str;
        getRedis()->getString("20180818",str);
        
        ////
    }
private:
    RedisClientPtr getRedis()
    {
        if(nullptr == _ptr)
            _ptr = g_rdPool.getNextClient();
        return _ptr;
    }
    
    RedisClientPtr _ptr;
}

void main()
{
  g_rdPool.setClientNum(10);
  g_rdPool.start();
login lg; lg.action(); }

 

总结:国泉这样设计,一开始有一个明显的弊端,若并发量很高的时候,工人不够用的情况下会被创建很多出来,最坏的情况就是超出redis-server最大连接数,这也是同步方式的坏处,如果你有什么好办法,欢迎讨论。

 

转载于:https://www.cnblogs.com/GuoQuanLiu/articles/9469743.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值