【分布式系统】 分布式调度 设计,要考虑 哪些问题?

news/2025/1/8 1:49:25/文章来源:https://www.cnblogs.com/o-O-oO/p/18658786
一、分布式调度框架的核心功能二、什么是 任务调度 ?三、什么是分布式 任务调度 ?四、分布式调度框架的主要功能五、分布式调度框架的核心业务场景六、分布式任务调度的核心组件七、分布式任务调度的架构模式八、常见的分布式调度框架包括九、XXL-Job的工作流程十、 Quartz 的工作流程十一、ElasticJob 的工作流程十二、Quartz  ElasticJob  Quartz  如何选型?十三、分布式调度的 常见问题 

一、分布式调度框架的核心功能

分布式调度框架 , 如何协调多个节点或服务执行任务成为一个关键问题。
分布式调度框架 , 如何可以调度成千上万的任务,在多个节点上分配、执行和监控任务,确保任务按时执行和任务结果的可靠性。

二、什么是 任务调度 ?

任务调度,顾名思义,是指对计算任务进行合理安排和调度的过程。
在计算机领域,任务调度主要指的是对计算任务在计算资源上的分配和执行顺序的管理。
通过任务调度,可以有效利用计算资源,提高系统的运行效率和性能。

三、什么是分布式 任务调度 ?

高并发、大数据、AI智能体 时代来了。
单一计算节点已经无法满足海量数据处理和复杂计算的需求。
分布式系统以其能够横向扩展、高可靠性等优势成为了解决这一问题的主要手段。而
在分布式系统中,任务调度则更加复杂和关键,需要考虑到网络通信、节点故障、数据一致性等诸多因素,以确保任务能够按时完成并保持系统的稳定运行。
什么是分布式 任务调度 ?
分布式调度指的 调度器会根据预设的任务计划或者触发条件,在多个节点上启动任务、监控任务。
与传统的单机调度不同,分布式调度的核心在于: 如何确保在分布式环境下,任务可以被多个节点安全、有效地执行,并保证系统的可扩展性和高可用性。

四、分布式调度框架的主要功能

任务调度:

负责触发定时任务,并将任务分配给多个工作节点。调度任务可以按时间计划执行(例如:定时、周期性任务)或者事件触发执行。

任务管理:

任务可以动态调整,如暂停、恢复、重启、取消等操作。任务的执行情况也可以通过调度框架进行管理,比如监控当前任务执行进度和状态。

任务监控:

监控调度系统中的每个任务的执行情况,提供实时反馈,确保任务能够按时完成。

任务容错:

当任务在执行过程中遇到错误时,分布式调度框架需要提供自动重试、失败处理等机制,确保任务最终能正确执行。

五、分布式调度框架的核心业务场景

定时任务调度:

企业级应用中常常有定时执行的任务,如定时生成报表、定时备份数据等。
分布式调度框架可以实现定时任务的分布式调度,确保任务在指定的时间准确执行,提高任务执行的可靠性和可扩展性。

大数据处理:

如Hadoop、Spark等大数据平台需要处理海量数据,而分布式调度则负责协调这些任务的执行。
ETL(Extract,Transform,Load)处理调度:在数据仓库和数据湖的建设中,需要从多个数据源抽取数据、进行清洗和转换,并加载到目标存储中。分布式调度框架可协调多个任务并行执行 ETL 操作,提高数据处理效率和可靠性。
尼恩2021年做了一年的大数据中台架构, 通过封装和改造azkban 实现了一个 分布式调度框架,具备DAG 的任务编排能力。

金融系统定时任务:

如银行定期批处理交易,生成对账单等。

电商系统中的任务调度:

如促销活动的定时开始和结束、库存同步等。

六、分布式任务调度的核心组件

调度器(Scheduler)

调度器负责接收任务,并根据预先设定的调度策略,决定将任务分配给哪些执行器执行。

任务执行器(Executor)

任务执行器负责接收调度器分配的任务,并在本地执行任务的具体操作,如计算、存储等。

任务队列(Task Queue)

任务队列用于存储待执行的任务,调度器从任务队列中取出任务进行调度。

七、分布式任务调度的架构模式

7.1 集中式 vs 去中心化

集中式架构模式下,存在一个 中心调度器 Scheduler 负责任务调度和资源管理,
去中心化架构模式下, 不存在一个 中心调度器 Scheduler ,而是 将任务调度和资源管理分散到各个节点上,通过协作完成任务调度。
集中式架构简单直观,但容易成为单点故障;
去中心化架构则更具弹性和扩展性,但需要解决节点间通信和协调的问题。

7.2 主从(Master-Slave)架构

主从架构中,存在一个主节点负责全局调度 Scheduler 和资源管理 Resource Manager,而从节点负责执行任务,承担 Executor 角色。
主从架构简单易于实现,但主节点压力较大,可能成为瓶颈。
在故障情况下,需要确保主从切换的高可用性。

7.3 对等(Peer-to-Peer)架构

对等架构中,各个节点对等地协作完成任务调度和资源管理,不存在明显的主从关系。
对等架构分散了调度和资源管理的压力,更具弹性和容错性,但需要解决节点间协调和一致性的问题。

八、常见的分布式调度框架包括:

8.1 Quartz:

经典的开源调度框架,支持复杂的调度表达式和分布式任务调度。
Quartz 核心组件:包括 Scheduler(调度器)、Trigger(触发器)、Job(作业) 等。
Scheduler 负责管理所有任务的调度,是 Quartz 的核心

8.2 ElasticJob:

当当开发的基于 ZooKeeper 的分布式调度框架,具有强大的任务分片功能。

ElasticJob 核心组件:Resource Management、Cloud Scheduler、Cloud Executor
ElasticJob 使用 Zookeeper 作为注册中心。
Zookeeper 用于存储和管理作业的配置信息、服务器信息以及作业运行状态。
Zookeeper 的高可用性和分布式特性使得 ElasticJob 能够在复杂的分布式环境中稳定地运行。
这些核心组件共同协作,使得 ElasticJob 能够在分布式环境中高效地进行任务调度和管理,具备强大的弹性伸缩能力和高可用性。

8.3 XXL-JOB:

轻量级的分布式调度框架,提供了简单的 API 设计和监控界面,适合中小型项目。
XXL-JOB 核心组件:调度中心(SchedulerCenter )和 执行器(Executor)

XXL-JOB调度中心(SchedulerCenter /admin )功能:

任务管理:负责任务的创建、编辑、删除等操作,支持多种调度类型,如 Cron 表达式调度、固定频率调度等。

执行器管理:管理执行器节点,包括执行器的注册、心跳检测和负载均衡。

日志管理:记录任务的执行日志,便于问题排查和审计。

监控与报警:监控任务的执行状态,并在任务执行失败时提供报警通知。

Scheduler /admin 基于 Spring Boot 开发,提供 Web 界面,用户可以通过图形化界面进行任务的管理和监控。

XXL-JOB执行器(Executor)功能:

任务执行:接收调度中心下发的任务请求,并执行具体的业务逻辑。

心跳机制:定期向调度中心发送心跳,以保持连接状态并报告自身状态。

任务分片:支持任务分片功能,能够将任务拆分成多个子任务,由不同的执行器节点并行执行。

部署:XXL-JOB执行器(Executor) 可以部署在多个节点上,实现任务的并行执行和负载均衡。

九、第一大调度: XXL-Job的工作流程

XXL-Job 调度平台触发任务流程

XXL-JOB 调度平台触发任务的流程可以分为以下几个关键步骤:

🌈阶段1:调度中心(Admin)触发任务
调度中心(scheduleCenter)操作:
第1步:扫描任务:
调度中心会定期扫描数据库中的任务表(xxl_job_info),查找即将到达触发时间的任务。
调度中心会按照任务配置的调度规则(比如 Cron 表达式等)来扫描最近 5 秒内需要触发的任务。
当判断某个任务到达触发时间时,便会启动后续的任务分配流程。
第2步:获取执行器:
根据任务的路由策略,从注册的执行器列表中获取对应的执行器 IP 和端口。
scheduleCenter 会依据预先设定的路由策略(例如轮询、随机、一致性哈希等路由策略),获取对应的执行器(Executor)的 IP 地址,通过执行器的 IP 及端口信息选取合适的 Executorbiz。
第3步:更新状态:
将任务的状态更新为“触发中”,并设置任务的下次触发时间(nextTriggerTime),以避免任务被重复触发.
scheduleCenter 会更新任务信息表(xxl_job_info)中该任务的下一次触发时间(nextTriggerTime),同时将任务相关信息放置到一个类似 “ring 环” 的结构中,并且修改下次触发时间,这样做的目的是避免该任务在本轮处理中再次被错误捞取到,保证任务调度的有序性。
🌈阶段2:任务下发与执行器接收阶段
scheduleCenter 通过 Executorbiz Client 传递任务。
scheduleCenter 借助 Executorbiz Client 向选取到的执行器(Executor)发送任务执行请求,告知执行器有任务需要执行。
Executorbiz Client 通过 NettyClient发送 HTTP 请求,将触发任务的指令发送给对应的执行器(Executor),请求中包含任务 ID、任务参数等信息.
🌈阶段3:执行器内部任务处理阶段
第1步:接收请求:
执行器接收到调度中心的触发请求后,会创建一个 JobThread 线程来处理任务。
在执行器端,每个任务对应的 JobThread 发挥作用。
每个 jobId 对应一个 JobThread,并且一个 JobThread 可能会被触发多次,当被触发多次时,会将这些触发任务按顺序放置在 Queue(队列)中,然后 JobThread 会按照顺序依次处理队列中的任务。
第2步:加入任务队列:
如果任务被多次触发,JobThread 会被放入任务队列(JobThreadQueue)中等待执行。
第3步:顺序执行:
JobThread 按顺序从队列中取出任务进行执行。执行任务时,会调用用户定义的 run 方法来运行具体的业务逻辑.
JobThread 处理完任务之后,会通过 TriggerCallbackTread 的 pushCallBack 方法,将任务执行的相关信息加入到回执队列(callBackQueue)中,准备向调度中心反馈执行结果。
🌈阶段4:任务执行结果反馈阶段
任务执行完成后,执行器会将执行结果通过 TriggerCallbackThread发送回调请求给调度中心,更新任务的执行状态和日志信息。
TriggerCallbackTread 处理反馈的流程:

TriggerCallbackTread 会轮询处理 callBackQueue 中存放的数据,它会将执行器端任务执行的结果信息发送回调度中心(Adminbiz Server)。
一旦执行结果信息放入到队列之后,执行器这边会立刻返回调度结果给调度中心,告知调度中心任务已执行完毕且结果已在反馈队列中等待进一步处理。

🌈阶段5:调度中心 scheduleCenter 处理回调
调度中心(Adminbiz Server)接收到执行结果反馈后,会进行相应的处理,比如更新任务的状态(成功、失败等),并且会上锁操作相关数据记录,确保在更新状态等操作时数据的一致性和准确性,完成整个任务触发及执行结果反馈的完整流程。
第1步:接收回调:
调度中心接收到执行器发送的回调请求后,会根据任务 ID 查找对应的任务记录.
第2步:更新任务状态:
根据执行结果更新任务的状态,如成功、失败等,并记录任务的执行日志到 xxl_job_log 表中.
第3步:释放锁:
如果任务执行过程中使用了锁(如 xxl_job_lock 表),在任务执行完成后会释放锁,以便其他任务可以继续执行.
🌈阶段6:调度中心 scheduleCenter任务监控与管理:
【任务监控】:
调度中心会持续监控任务的执行情况,包括任务的执行状态、执行时间、执行结果等.
【任务管理】:
提供任务管理界面,用户可以查看任务的详细信息,进行任务的暂停、恢复、删除等操作.
整个任务触发流程涉及到调度中心和执行器之间的协同工作,通过任务队列和回调机制来确保任务的有序执行和状态更新,从而实现高效、可靠的分布式任务调度。
Executor 和 ScheduleCenter 双向 RPC的流程
ScheduleCenter 初始化两个 核心组件: Adminbiz Server和 Executorbiz Client;
Executor 初始化两个 核心组件: Adminbiz Client 和 Executorbiz Server。

Executor 和 ScheduleCenter 双向 RPC的流程:
ExecutorBiz Server: Executor端的RPC服务,使用Netty开发的 Http Server。

ExecutorBiz Client: ScheduleCenter 为每一个 Executor 新建一个RpcBean( XxlRpcReferenceBean) ,通过一个NettyClient发送rpc消息给Executor 的 ExecutorBiz Server 。这个是一个RPC的流程。
AdminBiz Server: ScheduleCenter 使用netty实现一个Http Servrer。负责 Executor的 注册发现。

AdminBiz Client: Executor 新建一个XxlRpcReferenceBean,通过一个NettyClient发送 ScheduleCenter 信息。

十、第二大调度:Quartz 的工作流程

Quartz 的 Scheduler(调度器)和 Trigger(触发器)之间的工作流程可以总结如下:

10.1 Quartz 初始化阶段

【创建调度器】:
首先,需要创建一个 Scheduler 实例。
Quartz 提供了多种方式来创建调度器,例如使用 StdSchedulerFactory 工厂类来获取一个调度器实例。
【配置调度器】:
在创建Scheduler调度器的过程中,可以对调度器进行配置,例如设置线程池的大小、调度器的名称等。
这些配置信息可以通过配置文件或程序代码来指定。
【创建触发器】:
定义一个或多个 Trigger 实例,用于指定任务的执行时间规则。
Quartz 提供了多种类型的触发器,如 CronTrigger(基于日历的触发器)和 SimpleTrigger(基于固定时间间隔的触发器)等。
在创建触发器时,需要设置触发器的属性,如触发时间、重复间隔等。

10.2 Quartz 注册任务阶段

【创建作业详情】:
定义一个 JobDetail 实例,描述要执行的任务。
JobDetail 包含了任务的类名、任务名称、任务所属的组名等信息。
任务类需要实现 Job 接口,实现 execute 方法来定义具体的任务逻辑。
【关联触发器和作业】:
将 JobDetail 实例与一个或多个 Trigger 实例关联起来。
通过调用调度器的 scheduleJob 方法,将作业和触发器注册到调度器中。
此时,Scheduler 调度器会根据触发器的配置信息, 来计算任务的执行时间。

10.3 Quartz 调度执行阶段

【Trigger触发器触发】:
调度器会根据触发器的配置信息,计算任务的下一次执行时间。
当到达Trigger 触发器指定的触发时间时,Trigger 触发器会触发任务的执行。
【调度任务执行】:
Scheduler 调度器接收到触发器的触发信号后,会从线程池中分配一个线程来执行任务。
Scheduler 调度器会根据作业详情中的信息,实例化任务类,并调用任务类的 execute 方法来执行任务逻辑。
【任务执行完成】:
任务执行完成后,Scheduler 调度器会更新任务的执行状态,并根据Trigger 触发器的配置信息来决定是否需要再次触发任务。
例如,对于 SimpleTrigger,如果设置了重复执行,则会根据重复间隔计算下一次执行时间;
对于 CronTrigger,会根据 Cron 表达式计算下一次执行时间。

10.4 Quartz 任务管理阶段

【任务暂停和恢复】:
Scheduler 调度器提供了暂停和恢复任务的功能。通过调用 Scheduler 调度器的 pauseJob 和 resumeJob 方法,可以暂停或恢复指定任务的执行。
【任务删除】:
如果不再需要某个任务,可以通过调用Scheduler 调度器的 unscheduleJob 方法来删除任务。
删除任务时,调度器会停止任务的执行,并从调度器中移除任务和触发器的信息。

十一、第三大调度:ElasticJob 的工作流程

ElasticJob 的工作流程主要涉及作业配置、任务分片、调度执行和监控等环节,具体如下:

11.1 ElasticJob 作业配置与注册

配置作业信息

用户通过 Java 代码或配置文件等方式定义作业的基本信息,包括作业类型(如 SimpleJob、DataflowJob)、执行周期、分片策略、任务处理逻辑等。

注册作业到 ZooKeeper:

将配置好的作业信息注册到 ZooKeeper 中,ZooKeeper 是 ElasticJob 的分布式协调中心,负责存储作业的元数据、分片信息、节点状态等,为作业的分布式执行提供协调和数据存储支持。

11.2 任务分片与分配

【计算分片策略】:
ElasticJob 根据用户配置的分片策略(如平均分配策略、一致性哈希策略等)以及当前集群中作业节点的数量,计算出每个节点应该负责的任务分片。
【分配任务分片】:
通过 ZooKeeper 的分布式协调机制,将计算好的任务分片信息分配到各个作业节点上。
每个节点从 ZooKeeper 中获取自己需要执行的任务分片,知道自己要处理哪些具体的任务部分。

11.3 作业调度与执行

【定时触发】:
每个作业节点根据配置的执行周期和任务分片信息,定时触发作业执行。
作业节点内部维护了一个定时任务调度器,按照设定的时间间隔检查是否到了执行任务的时间。
【任务执行】:
当到达执行时间时,作业节点根据获取的任务分片信息,执行相应的任务逻辑。
作业节点会调用用户定义的作业处理器(JobHandler)中的方法来处理具体的业务逻辑,完成任务的处理。
【执行结果反馈】:
作业节点在完成任务执行后,会将执行结果反馈到 ZooKeeper 中。
ZooKeeper 存储了任务的执行状态、执行时间等信息,以便其他节点和监控系统能够获取任务的执行情况。

11.4 分布式协调与监控

【节点状态监控】:
ZooKeeper 实时监控各个作业节点的状态。
作业节点会定期向 ZooKeeper 发送心跳信息,表明自己的存活状态。
如果某个节点长时间没有发送心跳,ZooKeeper 会认为该节点出现故障,触发任务的重新分配。
【动态调整】:
当集群中新增或减少作业节点时,ZooKeeper 会感知到节点的变化,并触发任务分片的重新计算和分配。新增节点时,会将部分任务分片从其他节点迁移到新节点上;节点减少时,会将该节点上的任务分片重新分配到其他存活的节点上,保证任务的正常执行和负载均衡。
【监控与管理】:
ElasticJob 提供了监控和管理控制台,通过与 ZooKeeper 交互,管理员可以在控制台上查看作业的运行状态、任务分片情况、执行历史记录等信息,还可以对作业进行启停、重新配置等操作,方便对分布式作业进行统一管理和监控。

十二、三大调度 Quartz / ElasticJob / Quartz 如何选型?

Quartz、ElasticJob 和 XXL-JOB 各有特点,可从以下几个方面进行选型:

12.1 三大调度 的 功能特性PK

Quartz:功能强大且灵活,支持丰富的调度策略和复杂的调度表达式,如 Cron 表达式。但原生不支持分布式,需要借助额外的插件或框架实现分布式调度。 Quartz 适用于对调度功能要求高,任务类型多样且复杂,单机或对分布式要求不高的场景。
ElasticJob:基于 ZooKeeper 实现分布式调度,具有弹性扩展、任务分片等功能,支持多种作业类型,如 Simple、Dataflow 等。 ElasticJob 适用于大规模分布式系统,任务需要弹性扩展、动态分配,对数据处理和分片有较高要求的场景。
XXL-JOB:轻量级框架,功能较为全面,提供简单易用的 API 和可视化的任务管理界面,支持分布式调度、任务监控、失败重试等功能。 XXL-JOB 适合中小型项目,对开发效率要求高,希望快速搭建调度系统,且对功能复杂度要求不是特别高的场景。

12.2 三大调度 的 技术复杂度

Quartz:使用和配置相对简单,但在实现分布式调度时,需要深入了解相关插件或框架的原理和使用方法,技术复杂度会有所增加。

ElasticJob:涉及到分布式协调、任务分片等复杂概念和技术,配置和使用相对复杂,需要对 ZooKeeper 等分布式技术有一定的了解。

XXL-JOB:整体技术复杂度较低,框架设计简洁,易于理解和掌握,开发和维护成本相对较低。

12.3 三大调度 的 社区活跃度和生态

Quartz:是一款成熟的开源框架,社区活跃度高,有丰富的文档、教程和插件资源,遇到问题容易找到解决方案。

ElasticJob:由当当开发并开源,社区活跃度较高,有完善的文档和技术支持,在分布式调度领域有一定的影响力。

XXL-JOB:社区活跃度也不错,有详细的中文文档和案例,且在不断更新和完善,用户交流和反馈比较活跃。

12.4 三大调度 的 性能和稳定性PK

Quartz:性能较高,稳定性较好,经过多年的发展和实践检验,在单机和集群环境下都能可靠运行。

ElasticJob:在分布式环境下性能表现良好,通过 ZooKeeper 实现分布式协调和任务分配,能够保证任务的可靠执行和系统的稳定性。

XXL-JOB:性能能够满足中小型项目的需求,在分布式场景下也有较好的稳定性,通过监控和重试机制等保证任务的正常执行。

12.5 三大调度 的 集成难度PK

Quartz:可以与多种框架和技术集成,如 Spring、Hibernate 等,兼容性较好,能够方便地融入现有系统架构中。

ElasticJob:与 Spring 等框架集成较为方便,且对大数据处理等场景有较好的支持,如果现有系统是基于 Spring 框架构建,且有大数据处理需求,ElasticJob 是一个不错的选择。

XXL-JOB:提供了多种集成方式,与 Spring Boot 等框架集成简单快捷,能够快速与现有项目整合,尤其是对于 Spring 生态系统的项目,集成成本较低。

12.6 三大调度 的 大型任务的分片能力 PK

ElasticJob 、XXL-JOB 具有强大的任务分片功能。Quartz 没有直接提供像 ElasticJob 和 XXL-JOB 那样完善的任务分片功能
ElasticJob 和 XXL-JOB 都具有强大的任务分片能力,以下是它们各自的分片方式:

12.7 ElasticJob 的任务分片 能力

ElasticJob 利用 ZooKeeper 作为大型任务的分片 协调工具。
在集群环境下,各个作业节点通过 ZooKeeper 进行通信和协调。
ZooKeeper 负责存储作业的配置信息、分片信息以及节点的状态等数据,确保各个节点能够获取到一致的信息,并实现分布式环境下的任务调度和协调。
ElasticJob 作业分片策略:

平均分配策略:将任务按照节点数量进行平均分配,尽量保证每个节点处理的任务数量相同。例如,有 3 个作业节点和 10 个任务分片,那么每个节点会分配到 3 个或 4 个分片,以实现任务的均衡负载。

一致性哈希策略:根据节点的哈希值和任务分片的哈希值进行分配,使得任务分片能够均匀地分布在各个节点上,并且在节点数量发生变化时,尽可能减少任务的重新分配。

自定义分片策略:用户可以根据自己的业务需求实现自定义的分片策略。通过实现ShardingStrategy接口,用户可以编写自己的逻辑来决定如何将任务分片分配到各个节点上。

动态分片:当集群中新增或减少节点时,ElasticJob 会自动触发分片调整。新增节点时,会将部分任务分片从其他节点迁移到新节点上;节点减少时,会将该节点上的任务分片重新分配到其他存活的节点上。

12.8 XXL-JOB的任务分片 能力

XXL-JOB 的任务分片信息存储在数据库中,通过数据库的事务机制来保证数据的一致性。
在任务调度时,调度中心会根据配置的分片数量和节点信息,从数据库中获取相应的分片任务,并将其分配给各个执行器节点。
XXL-JOB 任务分片 策略:

分片广播策略:XXL-JOB 可以将任务分片广播到所有的执行器节点上,每个节点都会执行所有的任务分片。这种方式适用于需要在每个节点上都执行相同任务的场景,例如数据同步、日志收集等。

分片轮询策略:XXL-JOB 按照顺序依次将任务分片分配给各个执行器节点。例如,有 3 个执行器节点和 10 个任务分片,那么第一个节点会执行第 1、4、7、10 个分片,第二个节点会执行第 2、5、8 个分片,第三个节点会执行第 3、6、9 个分片。
自定义分片参数:在任务配置中,用户可以设置自定义的分片参数,如分片总数、当前分片索引等。执行器节点在执行任务时,可以根据这些参数来确定自己需要处理的具体分片数据。通过这种方式,用户可以灵活地控制任务的分片逻辑,满足不同的业务需求。

12.9 Quartz的任务分片 能力

Quartz 没有直接提供像 ElasticJob 和 XXL-JOB 那样完善的任务分片功能, 只有简单的 集群 互斥 、 故障转移 能力。
Quartz 使用数据库锁 实现集群节点间通信与协调
Quartz 集群中,节点之间 ,通过数据库锁来实现对调度资源的互斥访问。
当一个节点获取到锁后,它会执行任务并更新相关的调度状态信息。其他节点在锁被释放前,无法执行相同的任务,从而避免任务的重复执行。
Quartz 故障转移 机制:
Quartz 通过 心跳检测机制,实现 故障转移 。
各个节点会定期向数据库中更新自己的心跳信息,表明自己的存活状态。
如果某个节点在一定时间内没有更新心跳,其他节点会认为该节点已失效,并接管其未完成的任务。

尼恩的选项建议


大型调度场景, 建议使用 elastic-job
中型 调度场景, 建议使用 xxl-job,
小型 调度场景, 建议使用 quartz
尼恩建议, 一般情况,都可以使用 xxl-job

十三、分布式调度的 常见问题

问题 1:分布式任务的 高 可靠性

在分布式系统中,节点故障、网络中断等问题是无法避免的, 这样就会出现调度的 低可靠问题,比如:

1、如果调度节点发生故障,任务是否会被跳过或重复执行?
2、当任务执行过程中节点宕机,如何保证任务不丢失?

总之, 分布式调度框架必须能够保证任务的高 可靠性
那么, 如何实现呢?
高 可靠性策略之一: 任务持久化
持久化后的任务可以防止任务因节点故障而丢失。
当任务被调度时,任务状态和执行计划应该被持久化存储,例如存储到数据库/或 ZooKeeper 中,以便在节点故障时可以恢复任务的执行状态。
以下是 XXL-JOB 中一些主要的任务持久化表及注释说明:

-- 任务信息表
CREATE TABLE `xxl_job_info` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '任务ID',`job_group` int(11) NOT NULL COMMENT '任务组ID,可将任务分组管理',`job_cron` varchar(128) NOT NULL COMMENT '任务的Cron表达式,用于定义任务的调度规则,例如:"0 0 1 * * *" 表示每天凌晨1点执行任务',`job_desc` varchar(255) NOT NULL COMMENT '任务描述,对任务的简单说明',`add_time` datetime DEFAULT NULL COMMENT '任务添加时间',`update_time` datetime DEFAULT NULL COMMENT '任务更新时间',`author` varchar(64) DEFAULT NULL COMMENT '任务的创建者或维护者',`alarm_email` varchar(255) DEFAULT NULL COMMENT '报警邮件地址,当任务执行失败时,会发送邮件到该地址',`executor_route_strategy` varchar(50) DEFAULT NULL COMMENT '执行器路由策略,例如:"FIRST"(第一个)、"ROUND"(轮询)、"RANDOM"(随机)等,用于确定任务分配给哪个执行器',`executor_handler` varchar(255) NOT NULL COMMENT '执行器处理器名称,对应执行器中实现任务的具体类或方法',`executor_param` varchar(512) DEFAULT NULL COMMENT '任务执行时的参数,以字符串形式存储,可在执行任务时传递给执行器',`executor_block_strategy` varchar(50) DEFAULT NULL COMMENT '执行器阻塞策略,如:"SERIAL_EXECUTION"(串行执行)、"CONCURRENT_EXECUTION"(并行执行)等,用于控制任务的执行方式',`executor_timeout` int(11) NOT NULL DEFAULT '0' COMMENT '任务执行超时时间,单位为秒,超过该时间任务视为执行失败',`executor_fail_retry_count` int(11) NOT NULL DEFAULT '0' COMMENT '任务执行失败重试次数,当任务失败时会根据此次数进行重试',`glue_type` varchar(50) NOT NULL COMMENT '任务执行模式,如:"BEAN"(基于Java Bean)、"GLUE_SCRIPT"(脚本)等',`glue_source` mediumtext COMMENT '脚本任务的源代码,当 glue_type 为 GLUE_SCRIPT 时存储脚本内容',`glue_remark` varchar(128) DEFAULT NULL COMMENT '任务脚本的备注信息',`glue_updatetime` datetime DEFAULT NULL COMMENT '任务脚本更新时间',`child_jobid` varchar(255) DEFAULT NULL COMMENT '子任务ID,可用于设置任务之间的依赖关系,当父任务执行完成后,触发子任务',`trigger_status` tinyint(4) NOT NULL COMMENT '任务触发状态,如:0(暂停)、1(运行),控制任务是否处于可触发状态',`trigger_last_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '任务上一次触发时间,以毫秒为单位',`trigger_next_time` bigint(13) NOT NULL DEFAULT '0' COMMENT '任务下一次触发时间,以毫秒为单位',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='任务信息表,存储任务的各种配置信息';-- 任务调度日志表
CREATE TABLE `xxl_job_log` (`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '日志ID',`job_group` int(11) NOT NULL COMMENT '任务组ID,与任务信息表中的任务组对应',`job_id` int(11) NOT NULL COMMENT '任务ID,与任务信息表中的任务ID对应',`executor_address` varchar(255) DEFAULT NULL COMMENT '执行器地址,即执行任务的执行器的IP地址和端口号',`executor_handler` varchar(255) DEFAULT NULL COMMENT '执行器处理器名称,与任务信息表中对应',`executor_param` varchar(512) DEFAULT NULL COMMENT '任务执行时的参数,与任务信息表中对应',`trigger_time` datetime DEFAULT NULL COMMENT '任务触发时间',`trigger_code` int(11) NOT NULL COMMENT '任务触发状态码,如:200(成功)、500(失败)等',`trigger_msg` text COMMENT '任务触发信息,包含执行的详细信息或失败原因等',`handle_time` datetime DEFAULT NULL COMMENT '任务处理时间,即任务开始执行的时间',`handle_code` int(11) NOT NULL COMMENT '任务处理状态码,与 trigger_code 类似,但针对任务处理阶段',`handle_msg` text COMMENT '任务处理信息,包含处理的详细信息或失败原因等',`alarm_status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '报警状态,如:0(未报警)、1(已报警),当任务执行失败且需要报警时会更新此状态',PRIMARY KEY (`id`),KEY `I_trigger_time` (`trigger_time`),KEY `I_handle_code` (`handle_code`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='任务调度日志表,记录任务的调度和执行过程中的日志信息';-- 任务调度锁表
CREATE TABLE `xxl_job_lock` (`lock_name` varchar(50) NOT NULL COMMENT '锁名称,如:"schedule_lock",用于实现调度中心的锁机制,防止多个调度中心节点同时执行任务调度操作',PRIMARY KEY (`lock_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='任务调度锁表,用于实现任务调度时的锁机制,保证调度的一致性和可靠性';

xxl_job_info:存储任务的核心信息,包括调度规则、任务描述、执行器信息、任务参数等,是任务调度的基础。

xxl_job_log:记录任务调度和执行过程中的详细信息,方便查看任务的执行状态、结果和分析问题。

xxl_job_lock:在调度中心执行调度操作时,通过该表实现锁机制,避免多个调度中心节点冲突。

高 可靠性策略之二:幂等性
确保每个任务都是幂等的,即即使任务被重复执行,结果也不会发生变化。
🍓幂等性策略1: 任务唯一标识
通过为每个任务生成唯一的 ID 并记录每次任务的执行情况,来确保任务的幂等性。

机制:每个任务都有一个唯一的标识(如任务 ID),系统会根据这个标识来判断任务是否已经执行过。

作用:当任务被提交时,系统会检查该任务是否已经存在,如果存在则不会重复执行。

🍓幂等性策略2:任务状态机制

机制:XXL-JOB 管理任务的执行状态,包括待执行、执行中、执行成功和执行失败等状态。

作用:通过对任务执行状态的管理,确保任务在分布式环境下的幂等性。

🍓幂等性策略3:分布式锁互斥
在分布式调度框架中,分布式锁通常用于协调多个节点之间的任务执行。 使用分布式锁 ,可以确保多个节点不会重复执行同一任务,使用 Redis 或 ZooKeeper 实现分布式锁。

机制:使用分布式锁来保证同一个任务只会被一个执行器执行。

作用:例如,可以使用 Redis 实现分布式锁,确保任务在并发环境下不会被重复执行

以下是 XXL-JOB 中一些主要的任务调度锁表及注释说明:

-- 任务调度锁表
CREATE TABLE `xxl_job_lock` (`lock_name` varchar(50) NOT NULL COMMENT '锁名称,如:"schedule_lock",用于实现调度中心的锁机制,防止多个调度中心节点同时执行任务调度操作',PRIMARY KEY (`lock_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='任务调度锁表,用于实现任务调度时的锁机制,保证调度的一致性和可靠性';

问题2: 任务调度 的高扩展性 问题?

在大规模分布式系统中,调度框架需要处理大量的并发任务,如何确保系统的性能和扩展性成为关键问题。

1、当任务量增加时,系统是否能保持稳定的性能?
2、如何保证任务能够分布到不同的节点,以避免某些节点的过载?

高扩展性策略之一: 水平扩展
通过增加调度节点,分担任务调度的压力,使用负载均衡器来将任务分配到不同的调度节点上。
xxl-job执行器的水平扩展
xxl-job执行器 可以多节点集群部署。 多个执行器可以向调度中心注册。执行器启动时,会将自身的信息(如 IP 地址、端口号、执行器名称等)通过 HTTP 协议注册到调度中心的注册中心。例如,执行器在启动时调用调度中心的注册接口,将自己的信息存储在xxl_job_registry表中。
xxl-job执行器的 注册发现机制: 调度中心会自动发现已注册的执行器,并根据任务的路由策略(如轮询、随机、一致性哈希等)将任务分配到不同的执行器上执行。
xxl-job调度中心的水平扩展
xxl-job调度中心 可以多节点集群部署。
将调度中心的配置和任务信息存储在共享数据库中,多个调度中心节点可以访问相同的数据库。
例如,多个调度中心节点都连接到同一个xxl_job_info、xxl_job_log等表,保证任务信息和日志的一致性。
xxl-job调度中心通过 分布式锁机制互斥:使用数据库锁(其实完全可以基于 Redis 的分布式锁)确保多个调度中心节点不会同时触发同一个任务。例如,在调度中心触发任务时,先获取锁,完成调度后释放锁,防止重复调度。
高扩展性策略之二: 任务分片
任务分片是指将一个大的任务划分为多个小的子任务,由不同的节点并行执行。任务分片的策略会直接影响到系统的扩展性和性能。
常见的分片策略:

1、静态分片:任务的分片在调度开始前就固定分配给某些节点。这种策略简单高效,但在节点故障或扩展时,分片的重新分配会比较麻烦。
2、动态分片:在任务执行时,根据当前集群中节点的数量和状态动态分配任务,能够保证任务均匀分布在集群的各个节点上。

在 XXL-JOB 中,任务分片是将一个任务拆分成多个子任务,然后将这些子任务分配到不同的执行器节点或同一个执行器的不同线程中执行,以提高任务处理的并行性和性能。
XXL-JOB实现任务分片的步骤

1、任务配置

在 XXL-JOB 的任务配置界面,设置任务的分片数量,例如设置分片总数为 n。
这可以在任务创建或编辑时通过任务管理界面完成。

2、执行器端代码实现

在执行器的任务处理逻辑中,通过 XxlJobHelper 获取当前任务的分片参数,包括当前分片索引和分片总数。
以下是一个 Java 代码示例:

import com.xxl.job.core.context.XxlJobHelper;public class ShardingJobHandler {public void execute() {// 获取分片参数int shardIndex = XxlJobHelper.getShardIndex();int shardTotal = XxlJobHelper.getShardTotal();// 打印分片信息System.out.println("当前分片索引: " + shardIndex + ", 分片总数: " + shardTotal);// 根据分片索引和分片总数处理任务if (shardTotal > 0) {// 以下是一个简单的示例,根据分片索引处理不同的数据for (int i = shardIndex; i < 100; i += shardTotal) {// 这里可以根据分片信息处理不同的数据或逻辑System.out.println("处理数据: " + i);}}}
}

在上述示例 ,通过循环和分片信息将数据处理逻辑分成多个部分,每个分片处理不同的数据。
例如,如果 shardTotal 为 3,

  • shardIndex 为 0 的分片可能处理数据 0, 3, 6, 9...,
    
  • shardIndex 为 1 的分片可能处理数据 1, 4, 7, 10...,
    

上面的两个核心api:

  • XxlJobHelper.getShardIndex():获取当前任务的分片索引,范围是从 0 到 shardTotal - 1。
    
  • XxlJobHelper.getShardTotal():获取任务的分片总数,即之前在任务配置中设置的分片数量。
    

XXL-JOB 任务分片 策略:

分片广播策略:XXL-JOB 可以将任务分片广播到所有的执行器节点上,每个节点都会执行所有的任务分片。这种方式适用于需要在每个节点上都执行相同任务的场景,例如数据同步、日志收集等。
分片轮询策略:XXL-JOB 按照顺序依次将任务分片分配给各个执行器节点。例如,有 3 个执行器节点和 10 个任务分片,那么第一个节点会执行第 1、4、7、10 个分片,第二个节点会执行第 2、5、8 个分片,第三个节点会执行第 3、6、9 个分片。
自定义分片参数:在任务配置中,用户可以设置自定义的分片参数,如分片总数、当前分片索引等。执行器节点在执行任务时,可以根据这些参数来确定自己需要处理的具体分片数据。通过这种方式,用户可以灵活地控制任务的分片逻辑,满足不同的业务需求。

除了将任务分片分配到不同的执行器节点或线程外,还可以使用分片广播 策略,即每个执行器都执行所有的分片任务。
如果要使用 分片广播 策略,可以通过在任务配置中设置相关参数来实现。
在执行器端,对于广播的任务,每个执行器会收到所有分片的任务信息,并且可以根据自身情况进行处理。

问题3:任务调度 的 强容错 问题?

任务执行过程中,节点可能会出现宕机、任务执行失败等问题,如何确保系统的强容错,也成为关键问题。

1、当某个节点宕机时,如何确保任务能够顺利迁移到其他节点执行?
2、如何避免任务长时间执行或卡死?

强容错策略之1: 任务状态监控
任务状态监控:定期检查任务执行状态,当任务执行超时或失败时,及时采取补救措施。
xxl-job 如何实现任务状态监控? 从以下几个方面入手:
1、xxl-job 调度中心的任务状态监控
任务列表监控:
在 XXL-JOB 的调度中心 Web 界面,可以直观地看到任务的基本信息和状态。
包括任务名称、任务描述、执行器信息、调度规则(Cron 表达式)、任务状态(运行中、暂停、触发时间等)。
用户可以在任务列表中快速查看任务的整体状态,及时发现异常任务。
任务日志监控:
每次任务调度和执行过程都会在xxl_job_log表中记录日志。在调度中心的日志界面,可以查看每个任务的调度日志,包括任务的触发时间、执行时间、执行结果(成功、失败、超时等)、执行代码、执行信息等。
日志搜索和问题定位:
提供搜索和筛选功能,用户可以根据任务 ID、执行时间范围、执行结果等条件筛选日志,方便快速定位问题。
2、执行器状态监控
执行器的注册发现:
在xxl_job_registry表中存储了执行器的注册信息,包括执行器的 IP 地址、端口号、更新时间等。
调度中心会根据执行器的更新时间判断其是否存活。如果执行器长时间未更新注册信息,调度中心可认为该执行器失效。
执行器的健康状态:
执行器会定期向调度中心发送心跳,更新注册信息中的更新时间。
可以在调度中心界面查看执行器的心跳更新情况,判断执行器的健康状态。
3、监控和告警
在任务配置中,可以设置告警邮箱。
当任务执行失败、超时或满足其他预设条件时,调度中心会向告警邮箱发送邮件通知。
强容错策略之2: 任务自动迁移
当某个节点宕机时,未完成的任务可以被自动迁移到其他节点执行。
通常可以 监控节点状态,当检测到节点失效时,自动分配任务到健康的节点上。
XXL-JOB 执行器 定期更新自己在 xxl_job_registry 表中的注册信息,通过更新 update_time 字段,向调度中心表明自己处于存活状态。
调度中心会定期检查 xxl_job_registry 表中执行器的 update_time 字段,若发现某个执行器长时间未更新,认为该执行器故障。
任务重新分配:当检测到执行器故障时,调度中心会根据任务的路由策略(如轮询、随机、一致性哈希等)将原本分配给 其他健康的执行器。
强容错策略之3: 超时控制
为任务设置执行超时时间,如果任务长时间未能完成,自动中断并重试。
在 XXL-JOB 中,任务超时控制主要通过任务配置中的executor_timeout字段来实现。
在创建或编辑任务时,可以在任务信息表xxl_job_info中设置executor_timeout,它表示任务执行的超时时间,单位为秒。
XXL-JOB 调度中心按照任务的调度规则(如 Cron 表达式)触发任务,并将任务发送给执行器。
XXL-JOB 的调度中心提供了任务超时监测的功能,以下是其具体机制:
超时设置

1、任务超时时间配置:在 XXL-JOB 中,用户可以在任务配置中设置任务的超时时间。超时时间可以以毫秒为单位进行设置。
2、执行超时中断:当任务执行时间超过预设的超时时间时,XXL-JOB 会主动中断任务的执行。

超时监测机制

1、任务执行监控:调度中心会实时监控任务的执行状态和执行时间。当任务执行时间接近或超过超时时间时,调度中心会触发相应的超时处理机制。
2、执行器反馈:执行器在执行任务时,会定期向调度中心发送心跳和执行状态信息。如果执行器在超时时间内没有完成任务并反馈结果,调度中心会认为任务超时。

超时处理

1、任务终止:XXL-JOB 调度中心会根据设置的超时时间, 等待执行器的执行结果反馈。一旦检测到任务超时,调度中心会发送终止信号给执行器,要求立即停止任务的执行。
2、状态更新和报警:任务被终止后,调度中心会更新任务的状态为“超时终止”,并记录相关的超时信息。若在超时时间内未收到执行器的反馈,调度中心会将该任务标记为超时,并记录在任务调度日志表xxl_job_log中。同时,系统会触发报警机制,通知管理员任务超时的情况。

通过这些机制,XXL-JOB 能够有效地监测和处理任务超时问题,确保任务调度的可靠性和系统的稳定性。
强容错策略之4:失败重试机制
在任务执行失败时,可以通过配置自动重试机制,使任务在一定的重试次数内被重新调度执行。
在 XXL-JOB 的任务配置中,executor_fail_retry_count字段存储了任务执行失败时的重试次数。
用户可以在创建或编辑任务时设置该值,以决定当任务执行失败时,系统将自动重试的次数。
XXL-JOB 通过调度中心与执行器的配合,实现失败重试机制。

1、调度中心的操作

任务触发与监控:
调度中心按照任务的调度规则(如 Cron 表达式)触发任务,并将任务发送给执行器。
同时,调度中心会监控任务的执行情况,包括任务的执行结果(成功、失败、超时等)。
重试决策:
当收到执行器反馈的任务执行失败消息时,调度中心会根据executor_fail_retry_count来决定是否对该任务进行重试。

2、执行器的操作

执行器接收任务并执行任务逻辑,将执行结果(包括成功或失败信息)反馈给调度中心。

问题4:分布式调度中的数据一致性问题 ?

分布式系统中的数据一致性问题是任务调度中常见的挑战,尤其是在涉及到数据库操作或外部系统交互时,如何确保数据的一致性是关键。
XXL-JOB 使用 MySQL 的悲观锁来实现分布式锁,确保任务调度的一致性。
通过在数据库层面加锁,避免多个调度中心同时触发同一个任务的执行,从而保证任务的唯一性和数据一致性.

问题5:分布式调度中的高并发性 ?

百亿级任务 的调度平台,如何实现?
百亿级任务 的调度平台 面临 几个大的难题,具体请参见尼恩的文章:
从0到1,百亿级任务调度平台的架构与实现

原创 尼恩架构团队 技术自由圈

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/865651.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一文搞懂L1-L3业务流程体系分析,价值流、端到端流程、职能流程

今天我们聊聊业务流程体系分析这个话题。 业务流程的概念 业务流程是企业为实现目标而制定的一套系统化的工作方法。它由一系列有序的业务活动组成,按照既定规则将资源(输入)转化为有价值的结果(输出)。 在业务架构分析阶段,业务流程发挥着关键作用: • 明确业务运作的方…

[SSL/TLS/PKI] 基于`Let’s Encrypt`,获取免费SSL证书

缘起:云厂商卖的SSL证书太贵 概述:Let’s Encrypt 支持申请免费SSL证书的CA机构支持提供免费SSL证书的CA机构有:Let’s Encrypt ZeroSSL SSL For Free Cloudflare BuyPass AWS本文我们来试试 Let’s Encrypt 怎么用。https://letsencrypt.org/zh-cn/getting-started/Let’s …

.NET 响应式编程 System.Reactive 系列文章(二):深入理解 IObservableT 和 IObserverT

在 Rx 中,数据流的生产和消费是通过 观察者模式(Observer Pattern) 实现的。这种模式定义了两种角色: - IObservable.NET 响应式编程 System.Reactive 系列文章(二):深入理解 IObservable<T> 和 IObserver<T>引言:为什么我们调整了学习顺序? 在上一篇文章…

WebPack站点实战(一)

以下文章来源于一位不愿透露姓名的热心网友 ,作者不愿透露姓名的热心网友 文章配套B站视频,很多话语简略了,建议配着视频看。 地址:https://www.bilibili.com/video/BV13F411P7XB/ 开始之前了,简单过一下下面几个方法加深印象,便于更好理解加载器。也可以直接从webpack标…

CUDA概念

1.1.0f加个f,成单精度计算,不加会默认成double 2.快速指令:__add()加两个下划线 3.CUDA计算能力1.3以上才支持双精度,4.0往后支持双精度浮点计算 单精度浮点型(float )专指占用32位存储空间的单精度(single-precision )值。单精度在一些处理器上比双精度更快而且只占用…

12.09百度机器翻译SDK实验

一、实验要求百度机器翻译SDK实验(2024.11.15日完成) 任务一:下载配置百度翻译Java相关库及环境(占10%)。 任务二:了解百度翻译相关功能并进行总结,包括文本翻译-通用版和文本翻译-词典版(占20%)。 任务三:完成百度翻译相关功能代码并测试调用,要求可以实现…

弹性波动力学笔记(十)罗格里德斯公式推导

在应力计算中大量需要轴旋转公式计算,因此本笔记给出了罗格里德斯轴旋转公式Note: Derivation of the Rodriguez Formula In this Note, we will derive a formula for \(\mathbf{R}(\widehat{\mathbf{n}},\theta)\) . Consider the three dimensional rotation of a vecto…

【Java编程】JDK 源码好用的类方法

Java JDK 提供了丰富的工具类和方法,涵盖了字符串处理、集合操作、日期时间处理、文件操作等多个方面。熟练掌握这些工具类和方法,可以显著提高开发效率和代码质量。如果你有特定的需求或问题,欢迎随时提问

C++中调用C语言代码(extern “C”)

extern "C" 可以使我们在C++程序中调用C语言代码.extern "C"向程序编译器表明这段代码需要用C语言的方式编译。extern "C" { #include <stdio.h> void sayHello() { printf("Hello C\n"); }}int main() { sa…

Tita项目管理软件:管过程,管合同,两手抓

在这个日新月异的商业世界里,项目经理们时常面临重重挑战,而高效的协同作业、严谨的项目合同管理以及精准的回款把控,无疑是决定项目成败的关键要素。正是洞察到了这些需求,Tita项目管理软件应运而生,它以一站式的解决方案,助力项目经理们轻松驾驭项目的每一个环节。 一、…

快消零售的智胜之道:智能AI加速构建员工培训SOP策略

引言 在快节奏的快消零售行业中,员工的高效培训与标准化操作是提升服务质量、增强顾客满意度的关键。然而,传统培训方式往往耗时费力,效果难以保证。随着人工智能技术的不断发展,利用智能AI快速建立员工培训SOP(标准操作程序)已成为众多零售企业的新选择。本文将分享如何…

教育机构的智能跃迁:知识中台如何驱动转型与升级

引言 在数字化转型的浪潮中,教育机构正面临着前所未有的挑战与机遇。传统的教育模式已难以满足当前多元化、个性化的学习需求,而知识中台作为连接数据与业务的关键桥梁,正逐步成为教育机构实现智能转型的重要抓手。本文将探讨教育机构如何借助知识中台,通过智能化手段优化教…