概述
SkySchedule是基于netty实现的分布式任务调度框架,不依赖zookeeper等其他服务。主要原理是:客户端启动时通过netty与SkySchedule服务端建立长连接,通过长连接发送心跳消息,服务端可以统计到当前存活的客户端列表,为每个客户端分配“任务编号”。通过“任务id”对“客户端总数”取模,每个客户端获取对应“任务编号”的任务,实现分布式任务调度。
该分布式任务调度适用场景
生产者:假设有一个系统时刻都在生成任务,这些任务不会立即执行,需要暂时存放到一个mysql任务表表。
任务表:每个任务以一条记录的形式存放在msyql任务表中,表结构大致如下:
列名 |
类型 |
描述 |
id |
Int |
连续自增主键 |
task_data |
varchar(50) |
任务数据 |
task_time |
datetime |
任务执行时间 |
status |
Int |
状态:0 新建 1执行成功 2-执行失败 |
消费者:为了防止任务积压,并且能在最短的时间内把已经到期的任务执行完,我们需要多台机器并行执行这些任务。
分布式任务调度器SkySchedule职责:
1、通过分布式调度,把任务均分到各个消费者服务器。
2、保证任务100%被执行。一般情况下任务执行有三种情况(跟mq的消息处理类似):
At most once 任务可能没有执行,但绝不会重复执行。
At least one 任务绝对会执行,但可能会重复执行。
Exactly once 每条任务肯定会被执行一次且仅执行一次,很多时候这是用户所想要的。
由于在我所在项目的业务场景中,允许重复执行,目前SkySchedule只支持到At least one。Exactly once后续会提供。
3、某台消费者服务器挂掉,能自动重新分配任务到其他消费者服务器(重新分配的这部分任务执行可能会有短时间延迟)。如果增加消费者服务器,也能对剩余的任务自动重新分配。
4、为了防止单点故障,SkySchedule服务需要部署到多台服务器上的,如果其中某台挂掉或出现故障,存活的SkySchedule服务器能正常对外服务。
部署方式:
多分组集群模式:
这种方式,理论上可以为任意数量的系统提供分布式任务调度服务。
SkySchedule以集群方式部署,并做分组,比如:每三台server做为一个分组。每个分组,可以为多个系统提供分布式调度服务。上图红色框部分表示SkySchedule集群。
蓝色框部分,表示多个需要分布式任务调度的子系统。以“系统1”为例:“系统1”中的每台服务器启动时,向指定的SkySchedule分组中的每台server建立一个长连接。
这种方式需要开发一个集群管理页面来管理自己的服务器列表,并进行分组管理。以及系统接入注册管理页面,为某个系统分配到指定的SkySchedule分组为其服务。这部分相关管理功能没有提供开源源码,可以根据自己公司业务实现。
单分组集群模式:
如果只有少量或单个系统需要分布式任务调度服务,可以把SkySchedule只部署在少量服务器上(比如三台,防止单点故障),并把服务ip和端口整理成配置文件。
在需要分布式调度服务的系统中,引入该配置文件。程序启动时读取配置文件,与SkySchedule服务端建立长连接。
目前SkySchedule服务端只实现了“单分组集群模式”,多分组模式还需要添加相关管理功能才能实现。
以下内容都是基于“单分组集群模式”进行讲解。
客户端任务分配:
1、在需要任务调度的“子系统”中引入SkySchedule客户端jar包。
2、引入SkySchedule服务ip、端口列表配置文件。
3、使用jar包中ClientNode中的两个常量来调整从mysql任务表获取任务的sql语句
ClientNode. totalNode : 表示“总节点数”,也就是所有存活的“消费者客户端”个数。
ClientNode. nodeNum : 表示当前客户端“任务编号”。
当某个“消费者客户端”挂掉或新增时,SkySchedule服务端会计算最新的“总节点数”和每台客户端对应的“任务编号”,同步给每个客户端。
每台客户端获取已到期任务sql语句为:
Select * from task_info where (id % totalNode) = nodeNum and task_time < now()
task_info为任务表名,where条件有个取模操作,表示只取自己的属于任务。具体原理如下:
任务分配原理:
为了方便理解 举个例子:假如当前总共有3个客户端,分别的:
对于每个客户端而言totalNode都为3。
对于“客户端1”其任务编号nodeNum=0
对于“客户端2”其任务编号nodeNum=1
对于“客户端3”其任务编号nodeNum=2
在通过使用msyql任务表中的任务id对3取模,
如果结果为0,说明该任务会分配给“客户端1”执行,
如果结果为1,说明该任务会分配给“客户端2”执行,
如果结果为2,说明该任务会分配给“客户端3”执行,
提供这种方式就可以保证所有任务会被100%的执行。
另外mysql任务表的id是连续自增的,也可以理论上做到平台分配。
这就是SkySchedule分布式任务调度的实现原理,其实跟淘宝的tbschedule原理差不多,只是SkySchedule是通过netty实现,不需要借助其他服务,相对较为轻量。tbschedule需要借助zookeeper才能正常服务。
下图为: 生产者、消费者、SkySchedule服务端、SkySchedule客户端关系图:
源码github地址:https://github.com/gantianxing/skySchedule
目前只是初始版,能满足上述分布式任务调度基本需求。同时欢迎意见,后续会抽时间慢慢完善,同时也希望感兴趣的朋友贡献代码。
代码使用详解
源码中一共有四个模块:sky-server、sky-common、sky-client、sky-test,关系如下:
1、服务端:涉及到的模块为:sky-server、sky-common,通过maven编译打包会生成一个sky-server-1.0-SNAPSHOT.war的war,直接部署这个war包到jdk1.8+tomcat 8中环境中。注意这里必须是jdk1.8+tomcat 8的环境,如果无法启动检查下端口“9991”是否被占用。
假设一共部署了3台服务器:192.168.1.100、192.168.1.101、192.168.1.102。
2、服务端:涉及到的模块为:sky-client、sky-common,通过maven编译打包上述代码,还会生成两个jar包:sky-client-1.0-SNAPSHOT.jar、sky-common-1.0-SNAPSHOT.jar。把这两个jar包引入到需要做分布式调度的“应用服务工程”中。再进行少量配置即可,配置过程如下:
sky-test是一个模拟测试“应用服务工程”,必须为spring工程,建议spring使用4.0以上(sky-test使用的是4.3.1.RELEASE)。创建SkySchedule-client.properties,内容为:
#SkySchedule 服务ip端口列表 多个以","间隔 server.ip.port = localhost:9991,localhost:9992 #如果服务端挂掉,重连服务端间隔时间,单位秒 RE.CONN.WAIT.SECONDS=5 #向服务端发现心跳请求间隔时间,单位秒 TASK.REQ.WAIT.SECONDS=15 #25秒没有收到服务器返回,断开链接,放到重连map READ.WAIT.SECONDS=25 #如果空闲20秒发送一次ping信息 WRITE.WAIT.SECONDS=20 #系统编号 group.id=1000 #用户名 user.name=moon #密码 password=walker
把server.ip.port属性改为你自己的SkySchedule服务端列表,其他属性可以保持不变。比如把server.ip.port改为上述提到的:192.168.1.100:9991,192.168.1.101:9991,192.168.1.102:9991。
如果有多个不同类型的应用需要接入SkySchedule,需要对每个应用指定一个不同的“系统编号”group.id,默认是1000,如果只有一个系统需要接入,可以不用修改。
然后需要解析SkySchedule-client.properties到spring的Environment中,可以使用xml装配方式,也可以使用spring bean装配方式。由于sky-test构造的无web.xml配置的web工程,这里采用的后者,装配方式如下:
@Configuration @ComponentScan(basePackages = {"com.sky.schedule.client"}) @PropertySource("classpath:SkySchedule-client.properties") public class RootConfig { @Bean public PropertySourcesPlaceholderConfigurer placeholderConfigurer(){ return new PropertySourcesPlaceholderConfigurer(); } }
启动多个sky-test实例,每个实例分配的totalNode、nodeNum是否正常。
测试
下面我们直接使用源码中的代码进行测试,启动一个sky-server(SkySchedule服务端);依次启动两个sky-test,用来模拟两个需要分布式调度的“应用服务端”(SkySchedule客户端)。下面是测试步骤:
1、启动一个sky-test:观察sky-server服务端日志,每隔20秒,会打印一条消息:
return task info groupId:1000,total:1,nodeNum:0
说明当前客户端总节点数为1,当前客户端“任务编号”为0,也就是说所有任务都由该客户端执行。
2、再启动一个sky-test:观察sky-server服务端日志,每隔20秒,会打印两条消息:
return task info groupId:1000,total:2,nodeNum:1
return task info groupId:1000,total:2,nodeNum:0
说明当前客户端数为2,两个客户端的“任务编号”分别为0、1,也就是说一个客户端会执行id尾数为:0,2,4,6,8的任务,另一个客户端会执行id尾数为:1,3,5,7,9。
动态的增加客户端,会重新分配任务,测试通过。
3、停止其中一个sky-test(模拟其中一个客户端挂掉):观察sky-server服务端日志,每隔20秒,只会打印一条消息:
return task info groupId:1000,total:1,nodeNum:0
模拟其中一个客户端挂掉,会重新分配任务,测试通过。
通过这三个测试,可以发现当客户端个数发生变化时,SkySchedule会动态的重写分配任务。
另外,如果你是直接运行github中的代码,没有修改SkySchedule-client.properties中的server.ip.port属性。在进行上述测试时,细心的你会发现每隔5秒会打印一条错误信息:
ERROR netty.NettyClient - connect server failed-localhost:9992
主要原因是server.ip.port配置了两个服务端:localhost:9991,localhost:9992。但实际上我们只部署一个localhost:9991实例。假如你再启动一个localhost:9992服务端实例,这个错误会消失。这里模拟的就是:如果一个SkySchedule服务端挂掉(或者重启),恢复正常后,客户端能自动重连。
也就是说客户端在启动时,至少要保证有一个SkySchedule服务端是启动的。在客户端已经启动的情况下,假如所有服务端都挂掉,也不影响,只是这时如果一个客户端挂掉,就无法实现任务动态分配了。简单的说,只要有一个SkySchedule服务端存活就能保证分布式任务调度正常运行,但最好启动多个SkySchedule服务端,防止单点故障。
总结
最后总结下,使用SkySchedule做分布式调度,你只需要3步即可完成:
1、启动多个服务端,不用改任何配置,使用默认配置即可。
2、把sky-client-1.0-SNAPSHOT.jar、sky-common-1.0-SNAPSHOT.jar这两个jar包引入到需要分布式任务调度的“应用服务”工程。
3、把sky-test工程下的SkySchedule-client.properties复制到“应用服务”工程的classpath下,修改其中server.ip.port、group.id两个属性。通过xml或者spring bean装配的方式,把配置文件注入到spring容器中(Environment)。
由于底层是采用netty建立的长连接,性能非常优异,对“应用系统”来说几乎毫无感知,也无需配置zookeeper等服务。并且服务端代码也非常轻量,感兴趣的朋友可以看下源码。
其实现在要实现分布式任务调度的手段其实很多,比如我同事采用redis+mq实现过一个分布式任务调度,但个人觉得依赖的外部基础服务太多,如果redis、mq其中任何一个出现问题就会对应用服务产生影响。但采用SkySchedule就可以完全避免这些问题,并且对应用服务不会产生额外负担,只需维持一个netty长连接即可。
最后感谢我的同事“曾老师”对group分组支持部分的完善。
在使用过程中有任何问题或建议,请直接留言或者站内信。一周内会不定期回复。
SkySchedule server端管理页面使用介绍:http://moon-walker.iteye.com/blog/2391954
转载请注明出处:
http://moon-walker.iteye.com/blog/2386504
相关推荐
niubi-job是一个专门为定时任务设计的高可用分布式任务调度框架,它旨在解决在大型分布式系统中管理和执行定时任务的问题。这个框架提供了丰富的功能和优秀的扩展性,使得在复杂的企业级应用环境中,能够轻松地实现...
分布式任务调度框架Elastic-Job-Lite是当当网推出的一款轻量级无中心化任务调度解决方案,旨在解决大规模分布式环境下的任务调度问题。这款框架的设计理念是将任务的执行与调度分离,使得任务调度器无需关注具体的...
本源码项目是基于Java的分布式任务调度框架设计,包含1201个文件,主要使用Java、CSS、JavaScript和Shell编程语言。该项目是一个分布式任务调度框架,旨在帮助开发者更高效地管理和调度任务。系统提供了实时任务、...
本项目为基于Java核心的XXL-JOB分布式任务调度框架设计源码,集成了多种编程语言,包括JavaScript、CSS和HTML。项目文件共计293个,涵盖126个...该框架轻量级、易于扩展,旨在提供高效、可靠的分布式任务调度解决方案。
分布式调度任务XXL-JOB架构图是一种基于quartz调度器的分布式任务调度框架,旨在解决大规模任务的调度和执行问题。下面是该架构图中的关键知识点: 1. 分布式调度中心:XXL-JOB架构图的核心组件是分布式调度中心,...
分布式架构网上商城--论文分布式架构网上商城--论文分布式架构网上商城--论文分布式架构网上商城--论文分布式架构网上商城--论文分布式架构网上商城--论文分布式架构网上商城--论文分布式架构网上商城--论文分布式...
LTS是一个轻量级分布式任务调度框架。有三种角色, JobClient, JobTracker, TaskTracker。 4. uncode-schedule 基于zookeeper+spring task的分布式任务调度组件,非常小巧,无需任何修改就可以使spring task具备...
分布式任务调度平台XXL-JOB
这里分享自己已经跑起来的Oracle版本分布式任务调度平台XXL-JOB资源。 官方只有Mysql版本的DEMO,由于近期单位项目需要,将原来DEMO稍作修改后改成了oracle版本,主要修改工作包括修改配置文件,数据库连接方式,pom...
综上所述,这个项目旨在利用SpringBoot的便利性,结合分布式锁技术和特定的分布式任务调度框架,实现一个能够在多节点环境下高效运行和协调任务的系统。通过对"mvnw.cmd"和"mvnw"的理解,我们可以知道这可能是使用...
分布式任务调度框架 ElasticJob 中文文档 PDF 带目录
基于java的开发源码-OSGi 分布式通讯组件 R-OSGi.zip 基于java的开发源码-OSGi 分布式通讯组件 R-OSGi.zip 基于java的开发源码-OSGi 分布式通讯组件 R-OSGi.zip 基于java的开发源码-OSGi 分布式通讯组件 R-OSGi.zip ...
该项目是轻量级分布式任务调度框架XXL-JOB的Java实现设计源码,包含293个文件,涵盖126个Java源文件、33个JavaScript文件、25个XML配置文件、12个CSS样式文件、11个HTML模板文件、9个属性配置文件以及其他类型文件,...
分布式服务框架Zookeeper是Apache Hadoop的一个子项目,专门用于管理分布式环境中的数据。它提供了一个高可用、高性能、分布式的命名服务、配置管理以及同步服务,是构建大规模分布式系统的基石。Zookeeper的设计...
该项目是采用Java语言编写的XXL-JOB分布式任务调度框架设计源码,共包含304个文件,涵盖了136个Java源文件、36个PNG图片文件、33个JavaScript文件、27个XML文件、12个CSS文件、11个Freemarker模板文件、9个属性文件...
"XXL-JOB分布式任务调度系统培训PPT" XXL-JOB分布式任务调度系统是当前 Java 生态中的一种流行的分布式任务调度系统。它提供了多种功能特性,如支持 cron 表达式、支持多种任务类型、支持任务优先级、支持任务依赖...
该项目是一款基于Java构建的轻量级分布式任务调度框架XXL-JOB,源码总文件量为293个,涵盖了126个Java源文件、36个PNG图片文件、33个JavaScript文件、25个XML配置文件、12个CSS样式文件、11个FreeMarker模板文件、9...
《Earth-Frost:轻量级分布式任务调度框架详解》 在现代互联网应用中,任务调度是不可或缺的一部分,它能够有效地管理、协调系统中的各种任务,提高整体效率。Earth-Frost便是一个专为此目的设计的轻量级分布式任务...
该项目为基于Java核心技术的轻量级分布式任务调度框架XXL-JOB的设计源码,包含304个文件,涵盖136个Java源文件、36个PNG图片文件、33个JavaScript文件、27个XML配置文件、12个CSS样式文件、11个FreeMarker模板文件、...
本项目为基于Java开发的多语言支持的PowerJob分布式任务调度框架设计源码,共计640个文件,涵盖542个Java源文件、30个XML配置文件、15个JavaScript脚本、7个Markdown文档、7个SQL脚本、7个属性文件、5个YAML文件、5...