openGauss源码解析(28)
openGauss源码解析:第3章公共组件源码解析(3)
3.4 线程池技术
openGauss在多线程架构的基础上,实现了线程池。线程池机制实现了会话和处理线程分离,在大并发连接的情况下仍然能够保证系统有很好的SLA响应。另外不同的线程组可绑到不同的NUMA(non-uniform memory access,非一致性内存访问)核上,天然匹配NUMA化的CPU架构,从而提升openGauss的整体性能。
3.4.1 线程池原理
openGauss线程池机制原理如图3-2所示,图中的主要对象如表3-3所示。
图3-2 线程池机制原理
表3-3 线程池对象
对象 | 说明 |
---|---|
Postmaster | 主线程。负责监听客户端发出的请求 |
ThreadPoolControler | 线程池总控。负责线程池的初始化和资源管理 |
ThreadSessionControler | 会话生命周期管理 |
ThreadPoolGroup | 线程组。可以定义灵活的线程数量和绑核策略 |
ThreadPoolListener | 线程组监听线程。负责事件的分发和管理 |
ThreadPoolWorker | 工作线程 |
session | 客户端连接的一个会话 |
NUMA NODE | NUMA节点。表示一个线程组在NUMA结构下可以映射到一个NUMA节点上 |
这些对象相互配合实现了线程池机制,它们的主要交互过程如下。
(1) 客户端向数据库发起连接请求,Postmaster线程接收到连接请求并被唤醒。Postmaster线程创建该连接对应的socket(套接字,用于描述IP地址和端口,是一个通信链的句柄),调用ThreadPoolControler函数创建会话(session)数据结构。ThreadPoolControler函数遍历当前所有的Thread Group(线程组),找到当前活跃会话数量最少的Thread Group,并把最新的会话分发给该Thread Group,加入该Thread Group的epoll(epoll是Linux内核为处理大批量句柄而作了改进的poll(轮询),能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率)列表之中。
(2) Thread Group的listener线程负责监听epoll列表中所有的客户连接。
(3) 客户端发起任务请求,listener线程被唤醒。listener线程检查当前的Thread Group是否有空闲worker线程;如果有,则把当前会话分配给该worker线程,并唤醒该worker线程;如果没有,则把该会话放在等待队列之中。
(4) worker线程被唤醒后,读取客户端连接上的请求,执行相应请求,并返回请求结果。在一次事务结束(提交、回滚)或者事务超时退出的时候,worker线程的一次任务完成。worker线程将会话返回给listener线程,listener线程继续等待该会话的下一次请求。worker线程返还会话后,检查会话等待队列;如果存在等待响应请求的会话,则直接从该队列中取出新的会话并继续工作;如果没有等待响应的会话,则将自身标记为free(空闲)状态,等待listener线程唤醒。
(5) 客户端断开连接时,worker线程被唤醒,关闭连接,同时清理会话相关结构,释放内存和fd(文件句柄)等资源。
(6) 如果worker线程FATAL级别错误退出,退出时worker线程会从worker队列中注销掉。此时listener线程会重新启动一个新的worker线程,直到达到指定数量的worker线程。