应网友要求,写一写链接池实现细节。数据库
工程架构中有不少访问下游的需求,下游包括但不限于服务/数据库/缓存,其通信步骤是为:
(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进行链接,增删查改,关闭的操做,从而实现不一样的业务逻辑。数据结构
当并发量很低的时候,上述伪代码没有任何问题,但当服务单机QPS达到几百、几千的时候,创建链接connect和销毁链接close就会成为瓶颈,此时该如何优化?架构
结论也很简单,服务启动的时候,先创建好若干链接Array[DBClientConnection],当有请求过来的时候,从Array中取出一个,执行下游操做,执行完再放回,从而避免反复的创建和销毁链接,以提高性能。并发
而这个对Array[DBClientConnection]进行维护的数据结构,就是链接池。有了链接池以后,数据库操做的伪代码变为:
DBClientConnection* c = ConnectionPool::GetConnection();
c->insert(“db.s”, BSON(”shenjian”));
ConnectionPool::FreeConnection(c);负载均衡
经过上面的讨论,能够看到链接池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; } } }
说明:找到链接,把锁释放
能够发现,简单的链接池管理并非很复杂,基本原理即如上所述。
上述伪代码忽略了一些细节,在实现链接池中是须要考虑的:
(1)若是链接所有被占用,是返回失败,仍是让上游等待
(2)须要实施链接可用性检测
(3)为了让调用方更友好,可能还须要包装一层DAO层,让“链接”这个东西对调用方都是黑盒的
(4)经过freeArray,connectionMap可让取链接和放回链接都达到O(1)时间复杂度
(5)能够经过hash实现id串行化
(6)负载均衡、故障转移、服务自动扩容均可以在这一层实现
但愿这一分钟你们有收获。==【完】==相关阅读:“id串行化”究竟是怎么实现的?消息“时序”与“一致性”为什么这么难?