1、源码入口

使用xxl-job的时候,需要引入一个jar,然后还需要往Spring容器注入XxlJobSpringExecutor

<dependency>
<groupId>com.xuxueli</groupId>
<artifactId>xxl-job-core</artifactId>
<version>2.3.0</version>
</dependency> @Bean
public XxlJobSpringExecutor xxlJobExecutor() {
XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
xxlJobSpringExecutor.setAppname(appname);
xxlJobSpringExecutor.setAddress(address);
xxlJobSpringExecutor.setIp(ip);
xxlJobSpringExecutor.setPort(port);
xxlJobSpringExecutor.setAccessToken(accessToken);
xxlJobSpringExecutor.setLogPath(logPath);
xxlJobSpringExecutor.setLogRetentionDays(logRetentionDays);
return xxlJobSpringExecutor;
}

我们就可以顺着这个XxlJobSpringExecutor,分析下这个xxl-job-core做了些什么。

2、执行器启动

XxlJobSpringExecutor代码比较简洁,大致框架如下:

public class XxlJobSpringExecutor extends XxlJobExecutor implements ApplicationContextAware, SmartInitializingSingleton, DisposableBean {

	@Override
public void afterSingletonsInstantiated() {
// init JobHandler Repository (for method)
initJobHandlerMethodRepository(applicationContext); // refresh GlueFactory
GlueFactory.refreshInstance(1); // super start
try {
super.start();
} catch (Exception e) {
throw new RuntimeException(e);
}
} @Override
public void destroy() {
super.destroy();
} // ...... }

1、实现了SmartInitializingSingleton接口,在项目启动的时候,会调到afterSingletonsInstantiated方法。这个方法又可以作为进一步阅读的下个入口了;

2、实现了DisposableBean接口,在系统停止的时候,调用destroy方法。

3、实现ApplicationContextAware,只是为了获取applicationContext对象,这个没啥好说的。

先来看看初始化

2.1、解析Xxl-job注解

在使用xxl-job的时候,我们会写@XxlJob注解,来告诉xxl-job这是个定时任务的方法。

那么xxl-job就得解析这个注解并做一系列处理。对吧?

这个事情,就是在afterSingletonsInstantiated方法里面调initJobHandlerMethodRepository去做的。代码略长,就不贴出来了。具体实现逻辑如下:

  1. 通过applicationContext.getBeanNamesForType获取全部bean

  2. 遍历所有bean,找到有@XxlJob标记的方法,并判断@XxlJob标记的name值是否有重复,如果重复则报错

  3. 如果有配置init和destroy方法,则通过反射找到他们

  4. 将信息组装成MethodJobHandler对象,保存到ConcurrentHashMap中

    registJobHandler(name, new MethodJobHandler(bean, executeMethod, initMethod, destroyMethod));

2.2、处理glue模式

一般基于Spring使用的时候,都是bean模式,即

任务以JobHandler方式维护在执行器端;需要结合 "JobHandler" 属性匹配执行器中任务;

而glue模式是指

任务以源码方式维护在调度中心;

这里初始化了一个SpringGlueFactory

2.3、启动

这里调用父类XxlJobExecutor的start方法。方法主要执行下面几个大的步骤:

public class XxlJobExecutor  {
...
public void start() throws Exception { // init logpath
XxlJobFileAppender.initLogPath(logPath); // init invoker, admin-client
initAdminBizList(adminAddresses, accessToken); // init JobLogFileCleanThread
JobLogFileCleanThread.getInstance().start(logRetentionDays); // init TriggerCallbackThread
TriggerCallbackThread.getInstance().start(); // init executor-server
initEmbedServer(address, ip, port, appname, accessToken);
}
...
}
  1. 初始化执行器日志路径,默认 /data/applogs/xxl-job/jobhandler 。

    这个XxlJobFileAppender是个单独写日志文件的工具类。在xxl-job-admin界面上,可以通过界面查看定时任务调度执行的日志。我们在业务代码中,也可以通过XxlJobHelper.log方法,写自己的日志(老版本是XxlJobLogger.log)

  2. 根据配置的adminAddresses地址,构造AdminBiz列表(后面注册、调服务端接口等,会需要调到这个地址)

  3. 启动一个daemon线程,每天定期清理调度日志文件(上述1步骤目录下的文件)

  4. 定义一个LinkedBlockingQueue,这个queue里面放job执行结果。然后启动triggerCallbackThread和triggerRetryCallbackThread 两个线程,向job-admin反馈job执行结果。

    这里为啥是2个线程去给admin端反馈执行结果呢?

    原来,正常情况下,只有triggerCallbackThread从queue里面拿数据,提交到admin。

    但是当它提交失败的时候,triggerCallbackThread就会写一个callbacklog文件。再由triggerRetryCallbackThread读取callbacklog文件,并向admin提交执行结果。

  5. 构造EmbedServer并启动

    构造EmbedServer需要url,这里涉及到三个配置。

    ### 执行器注册 [选填]:优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址。从而更灵活的支持容器类型执行器动态IP和动态映射端口问题。
    xxl.job.executor.address=
    ### 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务";
    xxl.job.executor.ip=
    ### 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口;
    xxl.job.executor.port=9999

    EmbedServer是基于netty实现的一个服务,监听9999端口,并主要响应xxl-job-admin的调度请求。从EmbedHttpServerHandler中可以看出,admin调度中心往执行器发的请求,主要有以下5个:

    beat:调度中心检测执行器是否在线时使用
    idleBeat:调度中心检测 指定执行器 上 指定任务 是否忙碌(运行中)时使用 (注意这里2个“指定”)
    run:触发任务执行
    kill:终止任务
    这里是根据jobId找到对应的jobThread,再改变执行标记后,调interrupt方法。
    (所以如果你想优雅地停止一个线程,也可以通过线程标记+interrupt方式)
    log:查看执行日志

    执行器什么时候往调用中心注册的呢?

    答案是:在EmbedServer的start方法中,启动了一个thread,在其内部调了【startRegistry(appname, address);】

    而这行代码里面,又启动了一个registryThread,每30秒注册当前执行器。

至此,xxl-job客户端的逻辑大致分析清楚了,下一节再看看admin的代码

xxl-job源码阅读一(客户端)的更多相关文章

  1. 【原】AFNetworking源码阅读(六)

    [原]AFNetworking源码阅读(六) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这一篇的想讲的,一个就是分析一下AFSecurityPolicy文件,看看AF ...

  2. 【原】AFNetworking源码阅读(三)

    [原]AFNetworking源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇的话,主要是讲了如何通过构建一个request来生成一个data tas ...

  3. 【原】AFNetworking源码阅读(二)

    [原]AFNetworking源码阅读(二) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇中我们在iOS Example代码中提到了AFHTTPSessionMa ...

  4. 【原】SDWebImage源码阅读(四)

    [原]SDWebImage源码阅读(四) 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 SDWebImage中主要实现了NSURLConnectionDataDelega ...

  5. 【原】SDWebImage源码阅读(三)

    [原]SDWebImage源码阅读(三) 本文转载请注明出处 —— polobymulberry-博客园 1.SDWebImageDownloader中的downloadImageWithURL 我们 ...

  6. Android源码阅读 – Zygote

    @Dlive 本文档: 使用的Android源码版本为:Android-4.4.3_r1 kitkat (源码下载: http://source.android.com/source/index.ht ...

  7. 如何阅读Java源码 阅读java的真实体会

    刚才在论坛不经意间,看到有关源码阅读的帖子.回想自己前几年,阅读源码那种兴奋和成就感(1),不禁又有一种激动. 源码阅读,我觉得最核心有三点:技术基础+强烈的求知欲+耐心.   说到技术基础,我打个比 ...

  8. SparkConf加载与SparkContext创建(源码阅读一)

    即日起开始spark源码阅读之旅,这个过程是相当痛苦的,也许有大量的看不懂,但是每天一个方法,一点点看,相信总归会有极大地提高的.那么下面开始: 创建sparkConf对象,那么究竟它干了什么了类,从 ...

  9. CI框架源码阅读笔记3 全局函数Common.php

    从本篇开始,将深入CI框架的内部,一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说,全局函数具有最高的加载优先权,因此大多数的框架中BootStrap ...

  10. CI框架源码阅读笔记1 - 环境准备、基本术语和框架流程

    最开始使用CI框架的时候,就打算写一个CI源码阅读的笔记系列,可惜虎头蛇尾,一直没有行动.最近项目少,总算是有了一些时间去写一些东西.于是准备将之前的一些笔记和经验记录下来,一方面权作备忘,另一方面时 ...

随机推荐

  1. N皇后求解。万万没想到,只用一个一维数组就搞定了。还体现了回溯。

    一.啥是N皇后?先从四皇后入手 给定一个4x4的棋盘,要在棋盘上放置4个皇后.他们的位置有这样的要求,每一列,每一行,每一对角线都能有一个皇后. 你可能会对这个对角线有疑惑,其实就是每一个小正方形的对 ...

  2. Hi3559AV100 NNIE开发(7) Ruyistudio 输出mobileface_func.wk与板载运行mobileface_chip.wk输出中间层数据对比

    前面随笔讲了关于NNIE的整个开发流程,并给出了Hi3559AV100 NNIE开发(5)mobilefacenet.wk仿真成功量化及与CNN_convert_bin_and_print_featu ...

  3. MyBatis工程搭建&MyBatis实现Mapper配置查询

    一.MyMyBatis工程搭建 新建Maven项目:mybatis-demo 准备数据源 1 # 删除mybatis_demo数据库 2 drop database if exists mybatis ...

  4. 计划任务统一集中管理系统cronsun(替代crontab)

    一.背景 crontab 是 Linux 系统里面最简单易用的定时任务管理工具,相信绝大多数开发和运维都用到过,很多业务系统的定时任务都是通过 crontab 来定义的,时间长了后会发现存在很多问题: ...

  5. 删除文件--rm

    rm file         删除文件 rm -r dir      删除指定文件夹及文件夹下的所有内容 rm -rf dir     强制删除指定文件夹及文件夹下的所有内容

  6. shell算数和逻辑运算

    算术运算 Shell允许在某些情况下对算术表达式进行求值,比如:let和declare 内置命令,(( ))复合命令和算术扩 展.求值以固定宽度的整数进行,不检查溢出,尽管除以0 被困并标记为错误.运 ...

  7. 99%的Python用户都不知道的f-string隐秘技巧

    f-string想必很多Python用户都基础性的使用过,作为Python3.6版本开始引入的特性,通过它我们可以更加方便地向字符串中嵌入自定义内容,但f-string真正蕴含的功能远比大多数用户知道 ...

  8. Tk 的基本概念-组件—Tkinter 教程系列03

    Tk 的基本概念-组件-Tkinter 教程系列03 前言 Tk 系列教程: Tkinter教程系列01--引言和安装Tk Tkinter教程系列02--第一个真正的 GUI 程序 通过上一节的程序实 ...

  9. Android-SQLite的介绍 以及四个基本操作~

    在Android 开发中SQLite起着很重要的作用,网上SQLite的教程有很多很多,不过那些教程大多数都讲得不是很全面.本人总结了一些SQLite的常用的方法,借着论坛的大赛,跟大家分享分享的.一 ...

  10. 请使用管理员权限执行pip install命令

    重要的事情说三遍! 请使用管理员权限执行pip install命令 请使用管理员权限执行pip install命令 请使用管理员权限执行pip install命令 踩坑记录 当时一切还是从一题Misc ...