2

连接池居然这么简单?

 1 year ago
source link: https://www.51cto.com/article/722522.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

连接池居然这么简单?

精选
作者:架构师之路 2022-11-11 09:41:04
服务连接池,数据库连接池,缓存连接池,连接池是微服务分层架构中不可或缺的一个组件,本篇讲讲连接池的原理,以及实现细节。

​服务连接池,数据库连接池,缓存连接池,连接池是微服务分层架构中不可或缺的一个组件,本篇讲讲连接池的原理,以及实现细节。

f63c46d416aee054cc00725d8a8def3e6f2bec.jpg

 通常如何通过连接访问下游?

工程架构中有很多访问下游的需求,下游包括但不限于服务/数据库/缓存,其通讯步骤是为:

  • 与下游建立一个连接;
  • 通过这个连接,收发请求;
  • 交互结束,关闭连接,释放资源;

不管是服务/数据库/缓存,官方会提供不同语言的Driver、Document、DemoCode来指导使用方建立连接与调用接口。

以MongoDB的C++官方DriverAPI为例:

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

画外音:建立连接、发送请求、关闭连接,都非常清晰。

这个DBClientConnection就是一个与MongoDB的连接,官方Driver通过它提供了若干API,让用户可以对MongoDB进行连接,增删查改,关闭的操作,从而实现不同的业务逻辑。

为什么需要连接池?

当并发量很低的时候,连接可以临时建立,但当服务吞吐量达到几百、几千的时候,建立连接connect和销毁连接close就会成为瓶颈,此时该如何优化呢?

  • 当服务启动的时候,先建立好若干连接Array[DBClientConnection];
  • 当请求到达的时候,再从Array中取出一个,执行下游操作,执行完放回;

从而避免反复的建立和销毁连接,以提升性能。

而这个对Array[DBClientConnection]进行维护的数据结构,就是连接池。

有了连接池之后,数据库操作的伪代码变为:

DBClientConnection* c = 
    ConnectionPool::GetConnection();
c->insert(“db.s”, BSON(”shenjian”));
ConnectionPool::FreeConnection(c);

画外音:取出连接、发送请求、放回连接,也非常清晰。

连接池核心原理与实现是怎么样的呢?

可以看到连接池ConnectionPool主要有三个核心接口:

  • Init:初始化Array[DBClientConnection],这个接口只在服务启动时调用一次;
  • GetConnection:请求每次需要访问数据库时,不connect一个新连接,而是通过连接池的这个接口来拿连接;
  • FreeConnection:请求每次访问完数据库时,不是close一个连接,而是把这个连接放回连接池;

连接池核心数据结构是怎样的呢?

连接池至少包含两个核心数据结构:

  • 连接数组Array DBClientConnection[N];
  • 互斥锁数组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;
   }
  }
}

画外音:找到连接,把锁释放。

图片

会发现,连接池管理核心并没有想象的复杂。

除了核心代码,连接池还需要考虑哪些因素呢?

  • 需要实施连接可用性检测,如果有连接失效,需要重建连接;
  • 通过freeArray,connectionMap等数据结构,可以让取出连接和放回连接都达到O(1)时间复杂度;
  • 可以通过hash取连接,实现id串行化;
  • 每条连接被取到的概率必须相同,以实现负载均衡;
  • 如果有下游故障,失效连接必须剔除,以实现故障自动转移;
  • 如果有下游新增,需要动态扩充连接池,以实现服务自动发现;

思路比结论更重要,希望大家有收获。​


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK