MongoDB连接池的实现

版权声明:本文为博主原创文章,如果转载请注明,谢谢。 https://blog.csdn.net/baijiwei/article/details/86404776

几乎每一种数据库都会有连接池, 来减少频繁的创建删除连接的开销, 在MongoDB里面是通过信号量线程同步方式来对创建、销毁进行管理。

信号量基础

int sem_init(sem_t *sem, int pshared, unsigned int value)

sem是要初始化的信号量,pshared表示此信号量是在进程间共享(=1)还是线程间共享(=0),value是信号量的初始值。

int sem_destroy(sem_t *sem);

其中sem是要销毁的信号量。只有用sem_init初始化的信号量才能用sem_destroy销毁。

int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

sem_wait:等待信号量,如果信号量的值大于0, 将信号量的值减1,立即返回。如果信号量的值为0,则线程阻塞。相当于P操作。成功返回0,失败返回-1。
sem_trywait:与sem_wait类似, 只是如果递减不能立即执行,调用将返回错误(errno 设置为EAGAIN)而不是阻塞。
sem_timewait:sem_wait() 类似,只不过 abs_timeout 指定一个阻塞的时间上限,如果调用因不能立即执行递减而要阻塞。

int sem_post(sem_t *sem); 

释放信号量,让信号量的值加1。相当于V操作。

连接池的实现

使用TicketHoder来封装类线程控制信号量函数:

TicketHolder::TicketHolder(int num) : _outof(num) {
    _check(sem_init(&_sem, 0, num));
}

TicketHolder::~TicketHolder() {
    _check(sem_destroy(&_sem));
}

bool TicketHolder::tryAcquire() {
    while (0 != sem_trywait(&_sem)) {
        switch (errno) {
            case EAGAIN:
                return false;
            case EINTR:
                break;
            default:
                _check(-1);
        }
    }
    return true;
}

void TicketHolder::waitForTicket() {
    while (0 != sem_wait(&_sem)) {
        switch (errno) {
            case EINTR:
                break;
            default:
                _check(-1);
        }
    }
}

void TicketHolder::release() {
    _check(sem_post(&_sem));
}

在MongoDB启动的时候, 会先创建一个PortMessageServer,并在里面指定一个有配置参数或者命令行参数指定的最大的连接数, 然后通过setupSockets创建一个socket并绑定, 并将其加入到Listener里面的std::vector _socks;

static void _initAndListen(int listenPort) { 
    ...
    auto handler = std::make_shared<MyMessageHandler>();
    MessageServer* server = createServer(options, std::move(handler));
    server->setAsTimeTracker();

    if (!server->setupSockets()) {
        error() << "Failed to set up sockets during startup.";
        return;
    }
    ...
    server->run();
 }

MessagePortServer里面, 通过Listener::initAndListen函数,最终在 PortMessageServer::accepted里面来创建新的线程处理本次的操作。
这里可以看到, 每一次新的操作, TicketHoder::tryAcquire会试图进入信号量的代码区, 如果信号量的连接数大于1, 就会获得信号量锁, 并且将连接数减少1; 如果此时的连接数为0 , tryAcquire会返回失败, 表明连接数已满, 无法连接。

获得信号量锁之后, 会建立一下新的处理线程, 指定其处理函数为:
PortMessageServer::handleIncomingMsg.

class PortMessageServer : public MessageServer, public Listener {
    virtual void accepted(std::shared_ptr<Socket> psocket, long long connectionId) {
        ScopeGuard sleepAfterClosingPort = MakeGuard(sleepmillis, 2);
        std::unique_ptr<MessagingPortWithHandler> portWithHandler(
            new MessagingPortWithHandler(psocket, _handler, connectionId));

        if (!Listener::globalTicketHolder.tryAcquire()) {
            log() << "connection refused because too many open connections: "
                  << Listener::globalTicketHolder.used() << endl;
            return;
        }

        {
                stdx::thread thr(stdx::bind(&handleIncomingMsg, portWithHandler.get()));
                thr.detach();
        }
  }

猜你喜欢

转载自blog.csdn.net/baijiwei/article/details/86404776