服务连接池,数据库连接池,缓存连接池,连接池是微服务分层架构中不可或缺的组件。这篇文章讲的是连接池的原理和实现细节。您通常如何通过连接访问下游?在工程架构中接入下游有很多需求。下游包括但不限于服务/数据库/缓存。通信步骤为:与下游建立连接;通过此连接发送和接收请求;结束交互,关闭连接,释放资源;不管是服务/数据库/缓存,官方都会提供不同语言的Driver、Document、DemoCode来引导用户建立连接和调用接口。以MongoDB的C++官方DriverAPI为例:DBClientConnection*c=newDBClientConnection();c->connect("127.0.0.1:8888");c->insert("db.s",BSON("shenjian"));c->关闭();画外音:建立连接、发送请求、关闭连接都非常清晰。此DBClientConnection是与MongoDB的连接。官方Driver通过它提供了几个API,让用户可以连接MongoDB,进行增、删、查、修改、关闭等操作,从而实现不同的业务逻辑。为什么需要连接池?当并发量很低时,可以临时建立连接,但是当服务吞吐量达到数百或数千时,连接建立connect和连接关闭销毁就会成为瓶颈。这个时候怎么优化呢?服务启动时,首先建立几个连接Array[DBClientConnection];当请求到来时,从Array中取出一个,进行下游操作,执行完放回去;从而避免重复建立和破坏连接,提高性能。而这个维护Array[DBClientConnection]的数据结构就是连接池。有了连接池,数据库操作的伪代码就变成了:DBClientConnection*c=ConnectionPool::GetConnection();c->insert("db.s",BSON("shenjian"));ConnectionPool::FreeConnection(c);画外音:取出连接,发送请求,放回连接,也很清楚。连接池的核心原理和实现是什么?可以看到连接池ConnectionPool主要有3个核心接口:Init:InitializeArray[DBClientConnection],该接口只在服务启动时调用一次;GetConnection:不是每次需要访问数据库时都请求连接一个新的连接,而是通过连接池的这个接口来获取连接;FreeConnection:每次请求访问数据库时,不是关闭一个连接,而是将连接放回连接池;连接池的核心数据结构是什么?连接池至少包含两个核心数据结构:连接数组ArrayDBClientConnection[N];互斥体数组Arraylock[N];连接池的核心接口,如何通过对核心数据结构的操作来实现连接池的功能呢?Init(){fori=1toN{ArrayDBClientConnection[i]=new();数组DBClientConnection[i]->connect();数组锁[i]=0;}}VO:把所有连接和互斥锁初始化。GetConnection()fori=1toN{if(数组锁[i]==0){数组锁[i]=1;返回数组DBClientConnection[i];}}}VO:找到一个可用的连接,锁定它,然后返回连接。FreeConnection(c)fori=1toN{if(ArrayDBClientConnection[i]==c){数组锁[i]=0;}}}画外音:找到连接并释放锁。你会发现连接池管理核心并没有想象中那么复杂。除了核心代码,连接池还需要考虑哪些因素?需要实现连接可用性检测。如果出现连接失败,需要重建连接;通过freeArray、connectionMap等数据结构,取出连接和返回连接的时间复杂度可以达到O(1);connection可以通过hash获取,实现idstring,每个connection被取到的概率必须相同,实现负载均衡;如果下游出现故障,则必须消除故障连接以实现自动故障转移;如果有新的下游连接,需要动态扩展连接池,实现服务自动发现;想法比结论更重要,希望大家有所收获。
