Elastic-Job动态添加任务

背景

在使用Elastic-Job的过程中,有很多人遇到了这么一个问题,就是如何动态的去添加任务?

在官方的文档中也有对此作出回答,如下:

动态添加作业这个概念每个人理解不尽相同。

elastic-job-lite为jar包,由开发或运维人员负责启动。启动时自动向注册中心注册作业信息并进行分布式协调,因此并不需要手工在注册中心填写作业信息。 但注册中心与作业部署机无从属关系,注册中心并不能控制将单点的作业分发至其他作业机,也无法将远程服务器未启动的作业启动。elastic-job-lite并不会包含ssh免密管理等功能。

elastic-job-cloud为mesos框架,由mesos负责作业启动和分发。 但需要将作业打包上传,并调用elastic-job-cloud提供的REST API写入注册中心。 打包上传属于部署系统的范畴elastic-job-cloud并未涉及。

综上所述,elastic-job已做了基本动态添加功能,但无法做到真正意义的完全自动化添加。

接下来谈谈我对动态任务的理解,我眼中的动态任务分为2种:

  • 一种是全新的任务,包括实现的逻辑也是全新的,也就是当我们的程序打成一个jar包后,线上已经在运行了,这个时候我加了一个新的任务,如何能做到不停服务,将这个任务集成到已有的任务中去,这个实现起来难度比较大,涉及到Java类的热加载等,不过最近阿里又有一开源大作JarsLink,GitHub地址:https://github.com/alibaba/jarslink,可以支持在运行时动态加载到系统中,实现不需要重启和发布系统新增功能。还有一种实现思路我们可以利用Groovy脚本来做这样的事情,一般情况下重启来发布新的任务会比较常见,如果各位一定要实现动态的任务可以自己尝试着去研究下我提供的思路。
  • 另一种就是执行的业务逻辑不变,只是运行的时间发生变化。比如文章的定时发布,可以设置文章在某天的某分钟进行自动发布,实现这个功能有多种方式,你可以不停的扫描任务,一到时间点就自动发布,比较优雅的方式就是为每篇文章的自动发布都设置一个任务,通过Cron表达式来指定执行时间,不同的是每个任务都有自己的参数,业务逻辑都是固定的定时发布。

接下来我给大家介绍下Elastic-Job实现上面讲的第二种动态任务的方式,也就是任务的实现逻辑已经是存在的,只是需要发布成多个不同时间去触发的任务。

实战

实现任务的动态添加比较简单,只需要接收任务的信息,然后初始化一下就可以了,在实现的过程中笔者遇到了一个麻烦的问题?

在多节点分片任务却只有一个节点能执行,问题原因在于当有任务A和任务B,2个节点的时候,我们调用A节点的接口进行任务的动态添加,在A节点中初始化了任务调度器,数据也存储到了注册中心,但是B节点是不知道有新的任务添加,默认的使用方法是每个节点在启动时去初始化任务调度器,而我们的B节点已经启动过了,任务是新添加的。

解决这个问题最简单的方式就是将任务的节点都集中管理起来,无论动态任务在哪个节点上进行注册,都需要将这个请求转发到其他的节点上进行初始化操作,这样就可以保证多节点分片的任务正常执行。

还有一种对使用者更友好的办法是对Zookeeper中的节点进行监听,当有新的节点创建时,就自动获取这个节点的配置信息,在本地进行任务初始化,通过这样的方式就可以不用去转发请求到其他节点了,只要在任何节点有添加操作,都能被监听到,并自己去初始化。

监控代码如下:

/**
 * 开启任务监听,当有任务添加时,监听zk中的数据增加,自动在其他节点也初始化该任务
 */
public void monitorJobRegister() {
    CuratorFramework client = zookeeperRegistryCenter.getClient();
    @SuppressWarnings("resource")
    PathChildrenCache childrenCache = new PathChildrenCache(client, "/", true);  
    PathChildrenCacheListener childrenCacheListener = new PathChildrenCacheListener() {  
    public void childEvent(CuratorFramework client, PathChildrenCacheEvent event) throws Exception {  
        ChildData data = event.getData();  
        switch (event.getType()) {  
                case CHILD_ADDED: 
                    String config = new String(client.getData().forPath(data.getPath() + "/config"));
                    Job job = JsonUtils.toBean(Job.class, config);
                    addJob(job);
                    break;  
                default:  
                    break;  
        }  
     }  
  };  
    childrenCache.getListenable().addListener(childrenCacheListener);  
    try { 
        childrenCache.start(StartMode.POST_INITIALIZED_EVENT);
    } catch (Exception e) {
        e.printStackTrace();
    } 
}

为了方便大家使用,我将动态添加任务的功能集成到了我之前的elastic-job-spring-boot-starter(https://github.com/yinjihuan/elastic-job-spring-boot-starter)中集成了动态添加的逻辑,大家引入依赖即可使用。

使用方式比较简单,只需要在启动类上加一个ComponentScan注解,让Spring能够扫描到elastic-job-spring-boot-starter提供的代码即可:

@SpringBootApplication
@EnableElasticJob
//开启动态任务添加API
@ComponentScan(basePackages = {"com.cxytiandi"})
public class JobApplication {
    public static void main(String[] args) {
        new SpringApplicationBuilder().sources(JobApplication.class).web(true).run(args);
        try {
            new CountDownLatch(1).await();
        } catch (InterruptedException e) {
        }
    }
}

配置好之后,启动项目就可以通过REST API来动态的注册任务,API列表如下:

  • /job

添加任务是POST请求,数据格式为JSON体提交,格式如下:

{
"jobName":"DynamicJob13",
"cron":"0 33 16 * * ?",
"jobType":"SIMPLE",
"jobClass":"com.cxytiandi.job.demo.DynamicJob",
"jobParameter":"2222222",
"shardingTotalCount":1
}

完整字段请参考:

https://github.com/yinjihuan/elastic-job-spring-boot-starter/blob/master/spring-boot-elastic-job-starter/src/main/java/com/cxytiandi/elasticjob/dynamic/bean/Job.java

注意:jobClass必须事先存在于服务中

  • /job/remove 删除任务是GET请求,参数只要任务名称即可,比如:/job/remove?jobName=任务名。可以用于任务完成之后清空注册中心的任务信息。

原文发布于微信公众号 - 猿天地(cxytiandi)

原文发表时间:2018-04-03

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏数据和云

1024 程序员节:给 DBA 们的福音

通过 rpm 安装包,支持 yum 安装,这基本上将单实例的企业版数据库安装简化为一条命令:

17920
来自专栏GAN&CV

Ubuntu16.04安装opencv2&&ImportError: No module named cv2.cv

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_25737169/article/d...

56130
来自专栏云计算教程系列

如何将Ubuntu从16.04升级到18.04

Ubuntu 18.04是一个长期支持(LTS)版本,LTS 版本每两年发布一次,而 Ubuntu 18.04 是自 2016 年以来的第一个长期支持版本。Ub...

3.2K40
来自专栏无题

分布式Session一致性解决方案

在分布式架构或微服务架构下,必须保证一个应用服务器上保存Session后,其它应用服务器可以同步或共享这个Session Web应用在单机部署的情况下,Ses...

44160
来自专栏搜云库

Spring Cloud(二)Consul 服务治理实现

Spring Cloud Consul 项目是针对Consul的服务治理实现。Consul是一个分布式高可用的系统,具有分布式、高可用、高扩展性。 Consul...

60080
来自专栏JMCui

Hybris安装和各个Extention简单介绍

前言:突然想好好梳理一下这几个月所学的内容了,顺便让自己的知识有一个系统的框架。这种安装仅仅适用于开发环境,不适于生产环境。 一、  安装JDK 请安装最新的O...

534110
来自专栏程序员宝库

Laravel 开发 RESTful API 的一些心得

最近用 Laravel 写了一段时间的 API,总结一下自己的心得吧。 Start API开发我们可以看到,有些网站用token验证身份,有些用OAuth2.0...

54590
来自专栏腾讯移动品质中心TMQ的专栏

JAVA代码覆盖率工具JaCoCo-实践篇

上周 JAVA代码覆盖率工具JaCoCo-原理篇 简单介绍了JaCoCo其生成覆盖率的基本原理,这周的实践篇的主要内容就是将原理应用到实践中,本篇内容全部都是具...

1.1K90
来自专栏linux运维学习

linux学习第六十篇:Linux监控平台介绍,zabbix监控介绍,安装zabbix,忘记Admin密码如何做

Linux监控平台介绍 常见开源监控软件:cacti、nagios、zabbix、smokeping、open-falcon等等 cacti、smokeping...

41140
来自专栏Vamei实验室

Linux开机启动(bootstrap)

计算机开机是一个神秘的过程。我们只是按了开机键,就看到屏幕上的进度条或者一行行的输出,直到我们到达登录界面。然而,计算机开机又是个异常脆弱的过程,我们满心期望的...

27680

扫码关注云+社区

领取腾讯云代金券