链接池原来这么简单(一分钟系列)

应网友要求,写一写链接池实现细节。数据库

1、如何经过链接访问下游

工程架构中有不少访问下游的需求,下游包括但不限于服务/数据库/缓存,其通信步骤是为:
(1)与下游创建一个链接
(2)经过这个链接,收发请求
(3)交互结束,关闭链接,释放资源数组

这个链接是什么呢,经过链接怎么调用下游接口?服务/数据库/缓存,官方会提供不一样语言的Driver、Document、DemoCode来教使用方创建链接与调用接口,以MongoDB的C++官方Driver API为例(伪代码):缓存

DBClientConnection* c = new DBClientConnection();
c->connect(“127.0.0.1:8888”);
c->insert(“db.s”, BSON(”shenjian”));
c->close();

链接池原来这么简单(一分钟系列)
这个DBClientConnection就是一个与MongoDB的链接,官方Driver经过它提供了若干API,让用户能够对MongoDB进行链接,增删查改,关闭的操做,从而实现不一样的业务逻辑。数据结构

2、为何须要链接池

当并发量很低的时候,上述伪代码没有任何问题,但当服务单机QPS达到几百、几千的时候,创建链接connect和销毁链接close就会成为瓶颈,此时该如何优化?架构

结论也很简单,服务启动的时候,先创建好若干链接Array[DBClientConnection],当有请求过来的时候,从Array中取出一个,执行下游操做,执行完再放回,从而避免反复的创建和销毁链接,以提高性能。并发

而这个对Array[DBClientConnection]进行维护的数据结构,就是链接池。有了链接池以后,数据库操做的伪代码变为:
DBClientConnection* c = ConnectionPool::GetConnection();
c->insert(“db.s”, BSON(”shenjian”));
ConnectionPool::FreeConnection(c);负载均衡

3、链接池核心接口与实现

经过上面的讨论,能够看到链接池ConnectionPool主要有三个核心接口:
(1)Init:初始化好Array[DBClientConnection],这个接口只在服务启动时调用一次
(2)GetConnection:请求每次须要访问数据库时,不是connect一个链接,而是经过链接池的这个接口来拿
(3)FreeConnection:请求每次访问完数据库时,不是close一个链接,而是把这个链接放回链接池ide

链接池核心数据结构:
(1)链接数组Array DBClientConnection [N]
(2)互斥锁数组Array lock[N]性能

链接池核心接口实现:优化

Init(){
 for i = 1 to N {
  Array DBClientConnection [i] = new();
  Array DBClientConnection [i]->connect();
  Array lock[i] = 0;
 }
}

说明:把全部链接和互斥锁初始化

GetConnection()
 for i = 1 to N {
  if(Array lock[i] == 0){
   Array lock[i] = 1;
   return Array DBClientConnection[i];
   }
 }
}

说明:找一个可用的链接,锁住,并返回链接

FreeConnection(c)
 for i = 1 to N {
 if(Array DBClientConnection [i] == c){
   Array lock[i] = 0;
   }
  }
}

说明:找到链接,把锁释放

链接池原来这么简单(一分钟系列)
能够发现,简单的链接池管理并非很复杂,基本原理即如上所述。

4、未尽事宜

上述伪代码忽略了一些细节,在实现链接池中是须要考虑的:
(1)若是链接所有被占用,是返回失败,仍是让上游等待
(2)须要实施链接可用性检测
(3)为了让调用方更友好,可能还须要包装一层DAO层,让“链接”这个东西对调用方都是黑盒的
(4)经过freeArray,connectionMap可让取链接和放回链接都达到O(1)时间复杂度
(5)能够经过hash实现id串行化
(6)负载均衡、故障转移、服务自动扩容均可以在这一层实现

但愿这一分钟你们有收获。==【完】==相关阅读:“id串行化”究竟是怎么实现的?消息“时序”与“一致性”为什么这么难?

相关文章
相关标签/搜索