目录
首先我们解释一下计划任务,计划任务是指有计划的定时运行或者周期性运行的程序,我们最常见的就是Linux “crontab”与Windows “计划任务程序”,我们也常常借助他们实现我们的计划任务,因它们的时间调度程序非常成熟,无需我们再开发一套。
起初,我们也跟大多数人一样采用crontab调度程序,但随着项目越来越大,系统越来越复杂,就抱漏出许多问题。
首先是高可用HA需求,当运行计划任务的服务器一旦出现故障,所有的计划任务将停止工作。
其次是性能问题,越来越多的大型计划任务程序出现,对CPU/IO密集操作,单个节点已经不能满足我们的需求。
让计划任务7*24*365不间断运行,必需有一套行之有效的方案才行,我意识到必须开发一个全新的分布式计划任务框架,这样开发人员无需关注怎样实现分布式运行,集中写任务即可。
我首先提出这个框架必需具备几个特性:
分布式计划任务需具备以下特性
何时使用分布式计划任务
两个节点部署
两个节点可以实现“主”、“备”方案,队列(排队)运行方案与并行方案,其中并行方案又分为不同运行于异步运行,还涉及到互斥运行。
两个以上节点部署
多节点建议采用队列运行方案,并行方案,但不建议使用互斥并行方案(浪费资源)
当我们的分布式计划任务框架一旦完成,任务的编写部分非常轻松,只需继承框架程序便具备分布式运行的特性。
计划任务是一个相当复杂的一块,有操作系统计划任务,有运用程序计划任务,有基于TCP/IP的访问的,有基于命令行访问的,有定时执行的,有周期运行的,还有基于某些条件触发运行的。总之解决计划任务灾备,要比web,cache, database 复杂的多。
图 1. 分时方案
严格划分时间片,交替运行计划任务,当主系统宕机后,备用系统仍然工作,只不过处理周期拉长了。缺点:周期延长了
图 2. HA 高可用方案
正常情况下主系统工作,备用系统守候,心跳检测发现主系统出现故障,备用传统启动。缺点:单一系统,不能负载均衡,只能垂直扩展(硬件升级),无法水平扩展
图 3. 多路心跳方案
上面的HA是三层的基于VIP技术实现,下面这个方案我采用多路心跳,做服务级,进程级,IP与端口级别的心跳检测,做正常情况下主系统工作,备用系统守候,心跳检测发现主系统出现故障,备用传统启动,当再次检测到主系统工作,将执行权交回主系统.缺点:开发复杂,程序健壮性要求高
图 4. 任务抢占方案
A,B 两台服务器同时工作,启动需要一前一后,谁先启动谁率先加锁,其他服务器只能等待,他们同时对互斥锁进行监控,一旦发现锁被释放,其他服务谁先抢到谁运行,运行前首先加排他锁。 优点:可以进一步优化实现多服务器横向扩展。 缺点:开发复杂,程序健壮性要求高,有时会出现不释放锁的问题。
图 5. 任务轮循或任务轮循+抢占排队方案
任务轮循或任务轮循+抢占排队方案
互斥锁也叫排它锁,用于并发时管理多进程或多线程同一时刻只能有一个进程或者线程操作一个功能。如果你理解什么是互斥锁,便很容易理解分布式锁。
我们将进程,线程中的锁延伸到互联网上,实现对一个节点运行的进程或线程加锁,解锁操作。这样便能控制节点上进程或线程的并发。
+------------------+ +------------------+
| Server A | | Server B |
+------------------+ +---------------+ +------------------+
| Thread-1 | | Cluster Mutex | | Thread-1 |
| Thread-2 |----> +---------------+ <----| Thread-2 |
| Thread-3 | | A Thread-2 | | Thread-3 |
+------------------+ +---------------+ +------------------+
|
V
+---------------+
| Cluster Mutex |
+---------------+
| A Thread-2 |
+---------------+
上图中有两台服务器上运行任务,其中Server A 的 Thread-2 做了加锁操作,其他程序必须等待它释放锁才能运行。
你会问如果 Server A 宕机怎么办,是否会一直处于被锁状态?我的答案是每个锁都有一个超时阀值,一旦超时便自动解锁。
另外我们还要考虑“域”的问题,你也可以叫它命令空间,主要是防止锁出现同名被覆盖。
排队运行
+------------------+ +------------------+
| Server A | | Server B |
+------------------+ +---------------+ +------------------+
| Thread-1 | | Task Queue A | | Thread-1 |
| Thread-2 |----> +---------------+ <----| Thread-2 |
| Thread-3 | | A Thread-2 | | Thread-3 |
+------------------+ | B Thread-1 | +------------------+
| B Thread-3 |
| A Thread-3 |
+---------------+
|
| <sync>
V
+---------------+
| Task Queue B |
+---------------+
| A Thread-2 |
| B Thread-1 |
| B Thread-3 |
| A Thread-3 |
+---------------+
从上图中我可以看到Task Queue中排队情况,运行是自上而下的。
注意Task Queue 需要两个节点,它们是主从结构,A 节点实时向 B 节点同步sh状态。如果 A 节点出现故障, B 节点立即取代 A 节点。
计划任务可以分布式运行了,但并不能保证万无一失,配套其他服务器也要做调整。例如数据库,缓存等等。