我有一个分布式作业调度器,它运行在三台机器上,从数据库中选择事件(弹性搜索)。
我面临的问题是有时所有的节点选择相同的事件。这对系统资源是不必要的负荷。
我想做的是实现一种机制,使分布式调度程序的每个节点都应该选择唯一的事件。
我正在研究以下选项:
-
在作业本身中添加节点ip,同时从弹性搜索中获取数据,我也可以传递节点id。(这是我正在使用的当前实现)。
-
我已经尝试探索zookeeper来完成同样的工作,但我不确定是否可以使用zookeeper。
谁能帮我在正确的方向移动这个?
使用后端存储作业并没有错,但如果事务数量非常高,它可能成为瓶颈(>1 k tps) .
对于分布式调度,您需要解决两个问题:
- 一个且只有一个工人应该接受一个任务(你的相关问题)。
- 如果一个worker运行任务失败(例如内存不足,重新启动,…),任务应该返回到持久化。
要在典型的后端中轻松解决这两个问题,可以添加两个字段:
State :: { PENDING, WORKING, DONE }
LastUpdate :: DateTime
要解决前一个问题,您必须自动查询和更新一条PENDING
记录(即在同一事务中):
@Transactional
public Optional<Task> getTaskToWorkOnIfAny() {
Optional<Task> task = myBackend.getOnePendingTask();
if(task.isPresent())
// update task, I'm working on it!
myBackend.updateTask(task.get(), WORKING, new DateTime());
return task;
}
然后,像往常一样工作。
要解决后面的问题,只需检查任务是否WORKING
太长时间(如果您的任务很长,您可以添加ping
更新字段)。
如果我的任务失败,我应该写try/catch
来移动WORKING
到PENDING
吗?好吧,你可以,但如果系统真的崩溃了,你的任务将是WORKING
,所以,你应该遵循我之前的策略。
注意:考虑到整个问题,你只需要遵循前面的代码,其中getTaskToWorkOnIfAny
类似于SELECT * FROM task WHERE state = 'PENDING' OR (state = 'WORKING' AND lastUpdate < yesterday()
。仅此而已。