【问题标题】:Coordinating the execution of a single periodic task between servers in a cluster协调集群中服务器之间单个周期性任务的执行
【发布时间】:2012-10-31 05:46:17
【问题描述】:

(我会尽量让这个问题尽可能简短,同时清楚地描述情况。如果有任何遗漏,请评论。)

情况

  • 我在同一个数据中心运行一个包含三台服务器的集群
  • 为简化部署,每台服务器运行完全相同的应用程序代码

目标

  • 由单个服务器每分钟运行一个任务(称为 Task X)。

在这些条件下

  • 集群保持分布式且高度可用
  • 每台服务器都在运行相同的应用程序代码。换句话说,不存在“将代码 A 部署到 主服务器 并将代码 B 部署到所有 辅助服务器

我不想区分服务器类型的原因是为了保持高可用性(避免所谓的主服务器宕机时出现问题)、冗余(分配负载)以及避免在我需要的地方创建复杂的部署过程将不同的应用程序部署到不同类型的服务器。

为什么这么难?如果我要添加每 5 分钟执行一次此任务的代码,那么每个服务器都会执行它,因为每个服务器运行相同的应用程序代码。因此,他们需要能够协调在每个滴答期间哪个服务器将运行相同。

我能够使用分布式消息传递机制,例如 Apache KafkaRedis。如果使用这样的机制来协调这样的任务,那么这样的“算法”将如何工作?

我向其他人提出了这个问题,他的回答是使用任务队列。然而,这似乎并没有解决问题,因为问题仍然存在:哪个服务器将任务添加到任务队列?如果所有服务器都将任务添加到队列中,则会导致重复条目。此外,哪个服务器将执行队列中的下一个任务?这一切都需要通过集群内的协调来决定,而不需要区分不同类型的服务器。

【问题讨论】:

  • @Charles,这不是协调问题吗?
  • 协调作为一般的事情是非常非常模糊的,并且会成为一个坏标签。现有标签可以很好地分类您的问题。
  • 感谢您添加此内容,它很好地描述了我要写的问题:)
  • @Steven,太好了。你用什么语言工作?
  • @Tom,Python/Django 环境,但问题几乎相同。我有几台机器,我希望其中一台执行任务 x,但不是全部,但我不想要主机。我希望一台机器能够退出存在而不会产生任何不良影响(节省负载/冗余等)

标签: cluster-computing distribution high-availability task-queue


【解决方案1】:

听起来您正在寻找分布式锁。 Redis 用setnx 很好地做到了这一点。如果将其与expire 结合使用,则可以创建每 N 秒释放一次的全局锁。

setnx 只会在键不存在时写入值并返回 true。 Redis 操作是原子的,因此只有在密钥过期后调用setnx 的第一个服务器才能继续运行任务。

这是一个 ruby​​ 的例子:

# Attempt to get the lock for 'Task X' by setting the current server's hostname
if redis.setnx("lock:task:x", `hostname`.chomp)
  # Got the lock, now I set it to expire after 5 minutes
  redis.expire("lock:task:x", 60 * 5)
  # This server has the go ahead to execute the task
  execute_task_x
else
  # Failed to get the lock. Another server is doing the work this time around
end

有了这个你仍然依赖于调用一台服务器Redis Master,除非你利用redis-sentinel。查看the redis-sentinel docs 以获取有关如何配置自动故障转移的信息。

【讨论】:

  • 有趣,我没想到数据库可以帮助我。我确实提到了 Redis,但更多的是因为它是 pubsub。你知道 MongoDB 是否也可以做到这一点?数据库方面,我更喜欢它而不是 Redis。
  • 我相信你可以,虽然我没有亲自尝试过。您必须为键和到期前设置唯一索引。 db.jobs.ensureIndex( { "job_x": 1 }, { expireAfterSeconds: 300 } )
  • 我忘记添加 'unique: true'。 mongo ensure index 调用应如下所示:db.jobs.ensureIndex( { "job_x": 1 }, { unique: true, expireAfterSeconds: 300 } )。请记住,Redis 在空时仅使用 1MB 内存,并且除了上述用途之外还有很多用途。
  • 谢谢,我会试一试并报告。请注意,它需要 MongoDB 2.2+ (docs.mongodb.org/manual/tutorial/expire-data)。
  • +50 可以,但值必须是Date 类型。因此,唯一字段需要另一个索引,因为 Date 永远不是唯一的。
【解决方案2】:

您也可以使用JGroups 来实现此目的。示例实现见here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-30
    • 1970-01-01
    • 2012-06-20
    • 1970-01-01
    相关资源
    最近更新 更多