开发者学堂课程【全面讲解开源数据库中间件MyCat使用及原理(三):MyCat - 架构剖析 - MyCat 线程池架构】学习笔记,与课程紧密联系,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/757/detail/13297
MyCat - 架构剖析 - MyCat 线程池架构
内容介绍:
一、线程池实现
二、MyCat 中的线程架构
一、线程池实现
本节剖析 mycat 中的线程架构以及实现方式,在 mycat 中会涉及到线程是毋庸置疑的,因为它需要接收客户端的请求,接收客户端发送过来的 SQL 语句然后执行语句再给客户端返回对应的结果。
此时就会涉及到大量的线程,那么为了避免频繁的创建和销毁线程而造成的系统性能的浪费,在 mycat 中采用了线程池在 Mycat 中使用的线程池是 JDK 中提供的线程池 ThreadPoolExecutor 的子类 NameableExecutor ,构造方法如下∶
public class NameableExecutor extends ThreadPoolExecutor {
protected String name;
public NameableExecutor(String name,int size,BlockingQueue<Runnable> queue,ThreadFactory factory) {
super(size,size,Long.MAX_VALUE,TimeUnit.NANOSECONDS,queue,factory);
this . name =name;
}
public String getName( { return name;}
}
再来看 ThreadPoolExecutor 的父类构造池函数
父类构造为:
public ThreadPoolExecutor (int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize,maximumPoolsize,keepAliveTime,unit,workQueue,
threadFactory. defaultHandler);
}
对于该函数,其中有几个参数,构造参数含义:
corePoolsize :核心池大小
maximumPoolsize :最大线程数
keepAliveTime: 线程没有任务执行时,最多能够存活多久
timeUnit: 时间单位
workQueue : 阻塞任务队列
threadFactory: 线程工厂,用来创建线程
简单介绍如上几个参数:
线程池中存放线程,比如一个池子中已经初始化了很多线程,那么该池子在初始化时到底有多少个线程呢?就出现 corePoolsize,即初始时其中有多少线程。
例如现在在初始化时指定 corePoolsize 为 5,就说明在初始化该线程池时里面有五个线程,一次性初始化五个线程。实际上线程池外还有一个对应的队列,要想线程池执行任务,需要一直在队列中提交 task 任务,每次在队列中提交任务时,提交一个任务,该任务最终被线程执行,此时就会从线程池中拿出一个线程来执行。如果线程池中的线程用完并且没有归还给线程池,此时就会创建线程池,最大可以创建线程的数量就是 maximumPoolsize 最大线程数。
执行完任务后会将线程池归还给外面池子,如果此时由于突发情况比如一时刻任务量较大而线程池中没有线程,创建了很多线程,这些线程最终都会归还给线程池,归还给线程池后,线程池中空闲的线程就会不动,等待接收任务。
如果长时间未接受到任务,那么线程池中的线程就会销毁一部分。该时间就是keepAliveTime,单位是 timeUnit,如果长时间还是没有任务执行,就会将线程池中的多余线程直接销毁,即剩下核心线程来等待任务执行。workQueue 阻塞任务队列中放的就是任务。threadFactory: 线程工厂,用来创建线程。
以上是JDK中提供的 ThreadPoolExecutor 几个参数,接下来再来看在 mycat 中是如何实现 NameableExecutor 的呢?
NameableExecutor 就是 ThreadPoolExecutor 的一个子类,在一段代码中会看到参数
size,size,Long.MAX_VALUE,TimeUnit.NANOSECONDS,queue,factory
。
二、MyCat 中的线程架构
再来讲解 MyCat 中的线程架构
在启动 mycat 时会有对应的 main 方法就有主线程,主线程会开启两个线程池: timerExecutor 和 businessExecutor
在 Mycat 中主要有两大线程池:
timerExecutor 和 businessExecutor。
1) . timerExecutor 线程池主要完成系统时间定时更新、处理器定时检查、数据节点定时连接空闲超时检查、数据节点定时心跳检测等任务。即通过 timerExecutor主要是定时的完成一些任务。
2) . businessExecutor 是 MyCat 最重要的线程资源池,该资源池的线程使用的范围非常广,涵盖以下方面:
接收到客户端 SQL 语句请求后解析 SQl 语句执行 SQL 语句进行拦截合并等:
A.后端用原生协议连接数据
B.JDBC 执行 SQL 语句
C.SQL 拦截
D.数据合并服务
E.批量 SQL 作业
F.查询结果的异步分发
G.基于 guava 实现异步回调
最后使用跟踪的方式来介绍 timerExecutor 和 businessExecutor 两个线程池在何处创建:
打开 idea 中的源码,找到 main 方法即打开代码 MyCatStartup,在 main 方法中调用了 Server.Startup(),点击 Startup 进入该方法后找到 businessExecutor 方 法,可以看到此处创建了一个线程池 businessExecutor,还有另一个线程池timerExecutor,这两个线程池的类型都是 NameableExecutor,点击NameableExecutor 进入后就是 ThreadPoolExecutor 的一个子类。
继续观察,此处调用了 ExecutorUtil 方法,create 了一个 BusinessExecutor 线程池,然后传递了线程池的线程数 threadPoolSize:
businessExecutor=ExecutorUtil.create(name: "BusinessExecutor"
而在方法 create 中 new 了一个 NameableExecutor,然后传递后面的参数:
return new NameableExecutor(name,size,new LinkedTransferQueue<Runnable>(),factory);
然后调用父类的构造方法来创建一个线程池:
public NameableExecutor(String name,int size,BlockingQueue<Runnable>queue,ThreadFactory factory) {
super(size,size,Long.MAX_VALUE,TimeUnit.NANOSECONDS,queue,factory);
this.name = name ;
}
timerExecutor 线程池创建的过程类似于 businessExecutor。
以上重点介绍了在主线程中创建的两个线程池。