【问题标题】:How to stop schedulers to run parallel while the services are distributed?如何在服务分发时停止调度程序并行运行?
【发布时间】:2022-02-01 00:00:30
【问题描述】:

我们正在处理的应用程序有一些调度程序实际上是在特定时间运行的 cron 作业。这些服务是用 PHP 编写的,我们有这些调度程序,这些调度程序部署在具有多个实例的服务器中。假设我们有 3 个工作服务实例,其中我们有这些调度程序。因此,当调度程序运行时,它实际上在所有三个实例上运行,并且有几秒钟的间隔。这意味着调度程序正在执行相同的操作 3 次。那么如何停止在多个实例中运行相同的调度程序呢?我想在一个实例中运行调度程序。

【问题讨论】:

    标签: php microservices scheduler slim


    【解决方案1】:

    您将需要一些服务实例之间的协调方式。您选择的方法将需要权衡,因此请确定您不希望该操作发生 3 次的原因。更具体地说,您需要回答以下问题:

    哪个更糟:动作发生了两次还是根本没有发生?

    无论您选择哪种协调方式,都有一种失败模式会导致操作多次发生或不发生。没有涉及通过网络进行通信的协调方法,它可以保证恰好一次(如果动作是幂等的(执行两次与执行一次没有区别),则可能接近恰好一次),但如果动作是幂等的,则发生两次可能根本不坏*,因此根本不发生可能会更糟)。

    如果发生两次更可取,那么简单的事情就是为每个实例分配一个唯一的(甚至是唯一的)标识符,并让每次运行(当调度程序触发时)一个 DB 查询,如 INSERT INTO leases (id, holder, timestamp) VALUES ('scheduler', identifier, now())id 是首要的关键)。如果所有实例的时钟大致同步,则只有其中一个插入会成功:如果成功,则获取租约并执行计划的操作;在此之后,经过一段时间,你DELETE FROM leases WHERE id = 'scheduler' AND holder = identifier。在某个比调度器更频繁但比该时间段更频繁的时期,每个节点都会执行DELETE FROM leases WHERE id = 'scheduler' AND timestamp before now() - period 来取消太旧的租约。

    如果根本不发生更可取,可以使用诸如 etcd、consul 或 zookeeper 之类的东西来实现租约。对于这些系统来说,这是一个相当普遍的用途,因此它经过了实战测试并且有据可查。请注意,在最大安全情况下(租约在明确释放之前永远不会过期),您需要进行一些外部监控以确定计划的操作是否在应该发生的时候发生。如果监控显示没有,请提醒人工操作员(最终协调层),该操作员可以调查并手动使租约到期。

    *:我想像向随机目标发射足够多的核导弹以致地球变得无法居住的事情在所有意图和目的上都是幂等的,尽管我希望我们同意最好不要发生这种情况……

    【讨论】:

    • 感谢您分享您的想法。在我的情况下发生两次是不可取的。在搜索了很多博客并阅读了我正在使用的调度程序包lavary crunz的文档后,我找到了解决我问题的解决方案。我使用交响乐的lock componentRedisStore 来解决我的问题。
    • 如果你更喜欢 at-most-once 而不是 at-least-once,我会非常小心地使用 Symfony 的锁和 Redis 存储。 AFAIK,Redis 的各种锁实现,至少在常用的 Redis 配置中,会在“发生两次”方面犯错,而不是“根本不发生”方面。
    猜你喜欢
    • 2020-04-18
    • 1970-01-01
    • 1970-01-01
    • 2021-03-09
    • 1970-01-01
    • 2011-04-20
    • 2015-01-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多