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