分布式任务调度
最后更新于
这有帮助吗?
最后更新于
这有帮助吗?
pipe():创建2个文件描述符,fd[0]可读,fd[1]可写
fork():创建子进程,fd[1]被继承到子进程
dup2():重定向子进程stdout/stderr到fd[1]
exec():在当前进程内,加载并执行二进制程序
在golang中通过os/exec
可以提交执行任务
go get github.com/gorhill/cronexpr
将数据存储在集群中的高可用K-V存储
允许应用实时监听存储中的K-V的变化
能够容忍单点故障,能够应对网络分区
单点存储:节点宕机,服务将不可用
主从模式:主库宕机,从库暂时还能提供读功能,但是由于同步延迟会有一部分主库信息未能及时同步到从库,导致信息丢失(因为主库宕机了,无法继续同步)。同时从库身份切换成主库需要一定时间,会有段时间写功能异常。
一个班级有60人,如果将一个秘密告诉其中的31人,那么随便挑选31个人,一定有一个人知道秘密
raft是强一致的集群日志同步算法,一个分布式协议
etcd是一个分布式KV存储
etcd利用raft算法在集群中同步key-value
集群中有2N+1个节点(N>=1)
replication:日志在leader生产,向follower复制,达到各个节点的日志序列最终一致
term:任期,重新选举产生的leader,其term单调递增
log index:日志行在日志序列的下标
a~g都可能是第一任leader
f是第二任leader
f是第三任leader
e是第四任leader
a、c、d、g都可能是第四任leader
c是第六任leader
d是第七任leader
选举leader需要半数以上节点参与
节点commit日志最多的允许选举为leader
commit日志同样多,则term、index越大的允许被选举为leader
提交成功的请求,一定不会丢
各个节点的数据将最终一致
SDK内置gRPC协议,性能高效(一般的服务是基于HTTP+JSON协议实现的,性能低效)
底层存储是按key有序排列的,可以顺序遍历
因为key有序,所以etcd天然支持按目录结构高效遍历
支持复杂事务,提供类似if...then...else...的事务能力
基于租约机制实现key的TTL过期
提交版本(revision)在etcd中单调递增。同key维护多个历史版本,用于实现watch机制(可以通过key+version在etcd中访问到旧值)
历史版本过多,可以通过执行compact命令完成删减
通过watch机制,可以监听某个key,或者某个目录(key前缀)的连续变化
常用于分布式系统的配置分发、状态同步。
机器故障,任务停止调度,甚至crontab配置都找不回来
任务数量多,单机的硬件资源耗尽,需要人工迁移到其他机器
需要人工去机器上配置corn,任务执行状态不方便查看
调度器:需要高可用,确保不会因为单点故障停止调度
执行器:需要扩展性,提供大量任务的并行处理能力
分布式网络环境不可靠,RPC异常属于常态
Master下发任务RPC异常,导致Master与Worker状态不一致
Worker上报任务RPC异常,导致Master状态信息落后
状态不一致:Master下发任务给node1异常,实际上node1收到并开始执行
并发执行:Master重试下发任务给node2,结果node1与node2同时执行一个任务
状态丢失:Master更新zookeeper中任务状态异常,此时Master宕机切换Standby,任务仍旧处于旧状态
但凡需要经过网络的操作,都可能出现异常
将应用状态放在存储中,必然会出现内存与存储状态不一致
应用直接利用raft管理状态,可以确保最终一致性,但是成本太高
如:zookeeper、etcd等都应用了该理论
对于应用来说必须要具备P 分区容错性
,然后在C和A之间取舍,也就是说只能实现CP或AP
MySQL实现的就是CP(通过主从复制提供可用性,但是主节点挂掉,只能提供读,写服务异常),对于写入的数据可以立即被读取到。
架构简化,减少有状态服务,秉承最终一致性
架构折衷,确保异常可以被程序自我治愈
利用etcd同步全量任务列表到所有worker节点
每个worker独立调度全量任务,无需与Master通信(单worker调度全量任务会不会很慢?不会,因为在程序中遍历任务很快)
各个worker利用分布式锁抢占,解决并发调度导致相同任务同时执行的问题(抢到锁的就执行,没有就跳过)
任务管理HTTP接口:新建、修改、查看、删除
任务日志HTTP接口:查看任务执行历史日志
任务控制HTTP接口:提供强制结束任务的接口
实现web管理界面:基于jquery+bootstrap的Web控制台,前后端分离
保存到etcd的任务会被实时同步到所以worker
请求MongoDB,按任务名查看最近的执行日志
/cron/killer/任务名 -> "" (key部分是将要杀死的任务名写入到/corn/killer中,value部分可以填写任意值)
worker监听/corn/killer
目录下put修改操作
master将要结束的任务名put到/cron/killer目录下,触发worker立即结束shell任务
任务同步:监听etcd中/cron/jobs目录变化
任务调度:基于corn表达式计算,触发过期任务
任务执行:协程池并发执行多任务,基于etcd分布式锁抢占
日志保存:捕获任务执行输出,保存到MongoDB
利用watch API,监听/corn/jobs和/cron/killer目录的变化
将变化事件通过channel推送给调度协程,更新内存中的任务信息
监听任务变更event,更新内存中维护的任务列表
检查任务cron表达式,扫描到期任务,交给执行协程运行
监听任务控制event,强制中断正在执行中的子进程
监听任务执行result,更新内存中任务状态,投递执行日志
在etcd中抢占分布式乐观锁:/cron/lock/任务名
抢占成功则通过Command类执行Shell任务
捕获Command输出并等待子进程结束,将任务执行结果投递给调度协程
监听调度发来的执行日志,放入一个batch中
对新batch启动定时器,超时未满自动提交
若batch被放满,那么立即提交,并取消自动提交定时器