searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

Teledbx pooler介绍

2024-09-24 10:07:27
10
0

Pooler进程是Teledbx的内部连接池,它是一个独立进程,入口是PoolManagerInit,下面从几个方面介绍一下pooler相关的内容。

一、Pooler线程模型

Pooler是多线程架构,主线程负责新的连接建立以及连接请求的处理和分发,采用poll IO多路复用。新的连接到来时,会为这个连接分配一个agent用来保存上下文。poll每次返回时,需要遍历agent,找出有事件到来的agent,解析请求的命令。然后根据命令的属性等因素,决定命令是否是在主线程处理,如果不是在主线程处理,则把请求分发给其他线程组处理。Pooler中分为如下几类线程:

主线程PoolerLoop:接收postgres backend的请求,处理一些非IO请求,并对请求进行分发,根据不同的请求类型发送往不同的线程组。

同步IO处理线程组pooler_async_utility_thread:处理主线程分发的命令,包括同步的连接建立和连接状态管理请求等。

异步连接IO线程组pooler_async_connection_management_thread:处理异步的批量IO操作,主要是异步连接建立和关闭。

辅助线程组pooler_sync_remote_operator_thread:负责处理连接管辅助功能,包括连接预热,连接内存占用检测等功能。

二、Pooler 连接管理

Pooler中相应的连接存储在DatabasePool中,一个dbpool对应一组数据库和用户名的组合,dbpool在用户首次登陆这个数据库时创建,使用哈希表来管理该用户往各个节点的对应数据库的连接池。每个节点的连接池存放在以nodeid为键的哈希表中。


如上图所示,各个dbpool使用一个链表维护,每个dbpool有自己一个哈希表nodePools,哈希表里面存储的是一个个PGXCNodePool,一个PGXCNodePool就对应一个Oid,在使用的时候根据databaseusername找到dbpool,再根据oid找到PGXCNodePoolPGXCNodePool里面采用PGXCNodePoolSlot的数组来存放连接,一个PGXCNodePoolSlot对应着一个连接。在nodepool初始化时以MaxPoolSize来初始化slot数组,以nodepool为粒度维护着连接个数和空闲连接个数。Slot数组里面都是空闲的连接,当连接被申请走时,连接对应的slot槽位会被置为NULL,如果是agent申请走的,连接会挂在对应的agent上面。


 

三、Pooler 命令流程

主线程处理命令的入口是agent_handle_input,根据不同的命令走不同的处理流程。这里以获取连接为例介绍pooler命令的执行流程。

       agent_handle_input中首先获取qtype,得到命令类型,根据命令类型走不同处理流程。获取连接时在handle_get_connections解析需要获取的nodeid,根据nodeid

1) agent已经获取的连接中查找,agent会把已经拿到的连接存放在dn_connectionscoord_connections

2) 如果agent上没有,则在dbpool中根据nodeid查找,如果找到,则从dbpool中获取一个连接

3) 如果dbpool中没有空闲连接,则把请求分发给pooler_sync_remote_operator_thread线程,由该线程处理获取连接的操作

4) 如果要获取的所有连接在主线程都能直接拿到,则主线程直接把连接返回给客户端(backend),如果有些连接是分发给pooler_sync_remote_operator_thread线程处理,则需要在pooler_sync_remote_operator_thread线程中等所有分发任务处理完后由pooler_sync_remote_operator_thread线程将连接返回给客户端(backend)。

5) 获取连接时,会根据需要扩充线程池,主线程触发一个内部命令COMMAND_CONNECTION_BUILD,请求分发到

pooler_async_connection_management_thread线程处理,该线程异步批量创建连接,然后将连接返回给主线程,主线程在下次主循环前,调用pooler_sync_connections_to_nodepool将新创建的连接加到dbpool里面。

    Pooler两种命令处理流程如下所示:



四、Pooler 请求分发

主线程和线程组之间的通信方式为消息队列和信号量。

1) 请求分发

当需要进行请求分发时,会把请求的消息封装成一个个PGXCPoolAsyncReq,把请求相应的上下文存在req里面,然后使用pooler_async_task_pick_thread在线程组中选择一个线程,将req放到该线程的消息队列里面。 主线程收到的一个命令请求时,可能会分解成若干个req分发给其他线程处理,由PGXCASyncTaskCtl记录任务的状态等信息。


在线程创建的时候,会为该线程创建一个PGPipe类型的请求队列和响应队列,以及一个信号量,把req放到请求队列里面后,就使用信号量唤醒响应的线程来处理请求。

2) 请求处理与回复

pooler_sync_remote_operator_thread等线程平时就在信号量处等待,当信号量被唤醒时,在请求队列里面取出请求,根据请求的类型和上下文对请求进行处理,因为一个请求可能被分解为若干个独立的子任务,因此在最终回复客户端之前需要等待所有的子任务都处理完,所有任务处理完后就由改线程将处理结果回复给客户端(backend)。


3) 响应主线程

子线程中的任务处理完了之后,还会将主线程分发的req返回给主线程。主线程在每次主循环的pooler_handle_sync_response_queue中处理,释放相应的内存。


 

0条评论
0 / 1000
西格玛
2文章数
0粉丝数
西格玛
2 文章 | 0 粉丝
西格玛
2文章数
0粉丝数
西格玛
2 文章 | 0 粉丝
原创

Teledbx pooler介绍

2024-09-24 10:07:27
10
0

Pooler进程是Teledbx的内部连接池,它是一个独立进程,入口是PoolManagerInit,下面从几个方面介绍一下pooler相关的内容。

一、Pooler线程模型

Pooler是多线程架构,主线程负责新的连接建立以及连接请求的处理和分发,采用poll IO多路复用。新的连接到来时,会为这个连接分配一个agent用来保存上下文。poll每次返回时,需要遍历agent,找出有事件到来的agent,解析请求的命令。然后根据命令的属性等因素,决定命令是否是在主线程处理,如果不是在主线程处理,则把请求分发给其他线程组处理。Pooler中分为如下几类线程:

主线程PoolerLoop:接收postgres backend的请求,处理一些非IO请求,并对请求进行分发,根据不同的请求类型发送往不同的线程组。

同步IO处理线程组pooler_async_utility_thread:处理主线程分发的命令,包括同步的连接建立和连接状态管理请求等。

异步连接IO线程组pooler_async_connection_management_thread:处理异步的批量IO操作,主要是异步连接建立和关闭。

辅助线程组pooler_sync_remote_operator_thread:负责处理连接管辅助功能,包括连接预热,连接内存占用检测等功能。

二、Pooler 连接管理

Pooler中相应的连接存储在DatabasePool中,一个dbpool对应一组数据库和用户名的组合,dbpool在用户首次登陆这个数据库时创建,使用哈希表来管理该用户往各个节点的对应数据库的连接池。每个节点的连接池存放在以nodeid为键的哈希表中。


如上图所示,各个dbpool使用一个链表维护,每个dbpool有自己一个哈希表nodePools,哈希表里面存储的是一个个PGXCNodePool,一个PGXCNodePool就对应一个Oid,在使用的时候根据databaseusername找到dbpool,再根据oid找到PGXCNodePoolPGXCNodePool里面采用PGXCNodePoolSlot的数组来存放连接,一个PGXCNodePoolSlot对应着一个连接。在nodepool初始化时以MaxPoolSize来初始化slot数组,以nodepool为粒度维护着连接个数和空闲连接个数。Slot数组里面都是空闲的连接,当连接被申请走时,连接对应的slot槽位会被置为NULL,如果是agent申请走的,连接会挂在对应的agent上面。


 

三、Pooler 命令流程

主线程处理命令的入口是agent_handle_input,根据不同的命令走不同的处理流程。这里以获取连接为例介绍pooler命令的执行流程。

       agent_handle_input中首先获取qtype,得到命令类型,根据命令类型走不同处理流程。获取连接时在handle_get_connections解析需要获取的nodeid,根据nodeid

1) agent已经获取的连接中查找,agent会把已经拿到的连接存放在dn_connectionscoord_connections

2) 如果agent上没有,则在dbpool中根据nodeid查找,如果找到,则从dbpool中获取一个连接

3) 如果dbpool中没有空闲连接,则把请求分发给pooler_sync_remote_operator_thread线程,由该线程处理获取连接的操作

4) 如果要获取的所有连接在主线程都能直接拿到,则主线程直接把连接返回给客户端(backend),如果有些连接是分发给pooler_sync_remote_operator_thread线程处理,则需要在pooler_sync_remote_operator_thread线程中等所有分发任务处理完后由pooler_sync_remote_operator_thread线程将连接返回给客户端(backend)。

5) 获取连接时,会根据需要扩充线程池,主线程触发一个内部命令COMMAND_CONNECTION_BUILD,请求分发到

pooler_async_connection_management_thread线程处理,该线程异步批量创建连接,然后将连接返回给主线程,主线程在下次主循环前,调用pooler_sync_connections_to_nodepool将新创建的连接加到dbpool里面。

    Pooler两种命令处理流程如下所示:



四、Pooler 请求分发

主线程和线程组之间的通信方式为消息队列和信号量。

1) 请求分发

当需要进行请求分发时,会把请求的消息封装成一个个PGXCPoolAsyncReq,把请求相应的上下文存在req里面,然后使用pooler_async_task_pick_thread在线程组中选择一个线程,将req放到该线程的消息队列里面。 主线程收到的一个命令请求时,可能会分解成若干个req分发给其他线程处理,由PGXCASyncTaskCtl记录任务的状态等信息。


在线程创建的时候,会为该线程创建一个PGPipe类型的请求队列和响应队列,以及一个信号量,把req放到请求队列里面后,就使用信号量唤醒响应的线程来处理请求。

2) 请求处理与回复

pooler_sync_remote_operator_thread等线程平时就在信号量处等待,当信号量被唤醒时,在请求队列里面取出请求,根据请求的类型和上下文对请求进行处理,因为一个请求可能被分解为若干个独立的子任务,因此在最终回复客户端之前需要等待所有的子任务都处理完,所有任务处理完后就由改线程将处理结果回复给客户端(backend)。


3) 响应主线程

子线程中的任务处理完了之后,还会将主线程分发的req返回给主线程。主线程在每次主循环的pooler_handle_sync_response_queue中处理,释放相应的内存。


 

文章来自个人专栏
teledbx数据库
2 文章 | 1 订阅
0条评论
0 / 1000
请输入你的评论
0
0