xxl-job架构原理讲解
1、调度中心
调度中心是一个单独的Web服务,主要是用来触发定时任务的执行
它提供了一些页面操作,我们可以很方便地去管理这些定时任务的触发逻辑
调度中心依赖数据库,所以数据都是存在数据库中的
调度中心也支持集群模式,但是它们所依赖的数据库必须是同一个
所以同一个集群中的调度中心实例之间是没有任何通信的,数据都是通过数据库共享的
2、执行器
执行器是用来执行具体的任务逻辑的
执行器你可以理解为就是平时开发的服务,一个服务实例对应一个执行器实例
每个执行器有自己的名字,为了方便,你可以将执行器的名字设置成服务名
3、任务
一个执行器中也是可以有多个任务的
总的来说,调用中心是用来控制定时任务的触发逻辑,而执行器是具体执行任务的,这是一种任务和触发逻辑分离的设计思想,这种方式的好处就是使任务更加灵活,可以随时被调用,还可以被不同的调度规则触发。
来个Demo
1、搭建调度中心
调度中心搭建很简单,先下载源码
github.com/xuxueli/xxl…
然后改一下数据库连接信息,执行一下在项目源码中的/doc/db下的sql文件
启动可以打成一个jar包,或者本地启动就是可以的
启动完成之后,访问下面这个地址就可以访问到控制台页面了
http://localhost:8080/xxl-job-admin/toLogin
用户名密码默认是 admin/123456
2、执行器和任务添加
添加一个名为sanyou-xxljob-demo执行器
任务添加
执行器选择我们刚刚添加的,指定任务名称为TestJob,corn表达式的意思是每秒执行一次
创建完之后需要启动一下任务,默认是关闭状态,也就不会执行
创建执行器和任务其实就是CRUD,并没有复杂的业务逻辑
按照如上配置的整个Demo的意思就是
每隔1s,执行一次sanyou-xxljob-demo这个执行器中的TestJob任务
3、创建执行器和任务
引入依赖
org.springframework.boot spring-boot-starter-web 2.2.5.RELEASE com.xuxueli xxl-job-core 2.4.0
配置XxlJobSpringExecutor这个Bean
@Configuration public class XxlJobConfiguration { @Bean public XxlJobSpringExecutor xxlJobExecutor() { XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor(); //设置调用中心的连接地址 xxlJobSpringExecutor.setAdminAddresses("http://localhost:8080/xxl-job-admin"); //设置执行器的名称 xxlJobSpringExecutor.setAppname("sanyou-xxljob-demo"); //设置一个端口,后面会讲作用 xxlJobSpringExecutor.setPort(9999); //这个token是保证访问安全的,默认是这个,当然可以自定义, // 但需要保证调度中心配置的xxl.job.accessToken属性跟这个token是一样的 xxlJobSpringExecutor.setAccessToken("default_token"); //任务执行日志存放的目录 xxlJobSpringExecutor.setLogPath("./"); return xxlJobSpringExecutor; } }
XxlJobSpringExecutor这个类的作用,后面会着重讲
通过@XxlJob指定一个名为TestJob的任务,这个任务名需要跟前面页面配置的对应上
@Component public class TestJob { private static final Logger logger = LoggerFactory.getLogger(TestJob.class); @XxlJob("TestJob") public void testJob() { logger.info("TestJob任务执行了。。。"); } }
所以如果顺利的话,每隔1s钟就会打印一句TestJob任务执行了。。。
启动项目,注意修改一下端口,因为调用中心默认也是8080,本地起会端口冲突
最终执行结果如下,符合预期
讲完概念和使用部分,接下来就来好好讲一讲Xxl-Job核心的实现原理
从执行器启动说起
前面Demo中使用到了一个很重要的一个类
XxlJobSpringExecutor
这个类实现了SmartInitializingSingleton接口
所以经过Bean的生命周期,一定会调用afterSingletonsInstantiated这个方法的实现
这个方法干了很多初始化的事,这里我挑三个重要的讲,其余的等到具体的功能的时候再提
1、初始化JobHandler
JobHandler是个什么?
所谓的JobHandler其实就是一个定时任务的封装
一个定时任务会对应一个JobHandler对象
当执行器执行任务的时候,就会调用JobHandler的execute方法
JobHandler有三种实现:
- MethodJobHandler
- GlueJobHandler
- ScriptJobHandler
MethodJobHandler是通过反射来调用方法执行任务
所以MethodJobHandler的任务的实现就是一个方法,刚好我们demo中的例子任务其实就是一个方法
所以Demo中的任务最终被封装成一个MethodJobHandler
GlueJobHandler比较有意思,它支持动态修改任务执行的代码
当你在创建任务的时候,需要指定运行模式为GLUE(Java)
之后需要在操作按钮点击GLUE IDE编写Java代码
代码必须得实现IJobHandler接口,之后任务执行的时候就会执行execute方法的实现
如果你需要修改任务的逻辑,只需要重新编辑即可,不需要重启服务
ScriptJobHandler,通过名字也可以看出,是专门处理一些脚本的
运行模式除了BEAN和GLUE(Java)之外,其余都是脚本模式
而本节的主旨,所谓的初始化JobHandler就是指,执行器启动的时候会去Spring容器中找到加了@XxlJob注解的Bean
解析注解,然后封装成一个MethodJobHandler对象,最终存到XxlJobSpringExecutor成员变量的一个本地的Map缓存中
缓存key就是任务的名字
至于GlueJobHandler和ScriptJobHandler都是任务触发时才会创建
除了上面这几种,你也自己实现JobHandler,手动注册到JobHandler的缓存中,也是可以通过调度中心触发的
2、创建一个Http服务器
除了初始化JobHandler之外,执行器还会创建一个Http服务器
这个服务器端口号就是通过XxlJobSpringExecutor配置的端口,demo中就是设置的是9999,底层是基于Netty实现的
这个Http服务端会接收来自调度中心的请求
当执行器接收到调度中心的请求时,会把请求交给ExecutorBizImpl来处理
这个类非常重要,所有调度中心的请求都是这里处理的
ExecutorBizImpl实现了ExecutorBiz接口
当你翻源码的时候会发现,ExecutorBiz还有一个ExecutorBizClient实现
ExecutorBizClient的实现就是发送http请求,所以这个实现类是在调度中心使用的,用来访问执行器提供的http接口
3、注册到调度中心
当执行器启动的时候,会启动一个注册线程,这个线程会往调度中心注册当前执行器的信息,包括两部分数据
- 执行器的名字,也就是设置的appname
- 执行器所在机器的ip和端口,这样调度中心就可以访问到这个执行器提供的Http接口
前面提到每个服务实例都会对应一个执行器实例,所以调用中心会保存每个执行器实例的地址
这里你可以把调度中心的功能类比成注册中心
任务触发原理
弄明白执行器启动时干了哪些事,接下来讲一讲Xxl-Job最最核心的功能,那就是任务触发的原理
任务触发原理我会分下面5个小点来讲解
- 任务如何触发?
- 快慢线程池的异步触发任务优化
- 如何选择执行器实例?
- 执行器如何去执行任务?
- 任务执行结果的回调
1、任务如何触发?
调度中心在启动的时候,会开启一个线程,这个线程的作用就是来计算任务触发时机,这里我把这个线程称为调度线程
这个调度线程会去查询xxl_job_info这张表
这张表存了任务的一些基本信息和任务下一次执行的时间
调度线程会去查询下一次执行的时间