【问题标题】:Design a generic job scheduler [closed]设计一个通用的作业调度程序[关闭]
【发布时间】:2014-11-23 13:19:10
【问题描述】:

我正在尝试设计一个通用的作业调度程序,以扩展我的架构知识和在面试中思考系统设计问题的能力。到目前为止,我想出的内容如下。您能否指出我在解决此类问题的方法中应该在哪些方面进行全面的工作?

我已经阅读了很多在线资源,但需要一些具体的指导才能继续前进。

为 X 公司设计一个通用的作业调度程序(这是最大的 当今的科技公司)。

用例

创建/读取/更新/删除作业

调查已运行的作业 过去(工作类型、花费时间、详细信息)

约束

每秒将在系统上运行多少作业?

= # 个作业/小时归因于用户 + # 个作业/小时归因于机器

= 1m * 0.5 /day/24/3600 + 1m/50*20/24/3600

~= 12 个作业/秒

系统需要存储多少数据?

推理:我只存储作业执行细节,实际 工作(脚本执行)在其他机器上完成 收集的数据是结束时间、成功/失败状态等。这些是 > 都可能只是文本,可能带有用于说明目的的图形。一世 将存储 >> 所有在系统中执行的作业的数据,通过 作业调度程序(即过去 10 年)

=(设置作业详细信息的页面大小 + 收集的有关作业的数据大小)* 作业数 * 365 > 天 * 10 年 = 1 MB * 900 000 * 365 * 10

~= 3600 000 000 MB

= 3600 000 GB

=3600 TB =3.6 PB

抽象设计

根据以上信息,我们不需要太多 机器来保存数据。我会将设计分解为 以下:

应用层:服务于请求,显示 UI 细节。

数据存储层: 像一个大哈希表:存储映射 键值(键是按它们运行的​​日期时间组织的作业, 而这些值将显示这些工作的详细信息)。这是为了启用 轻松搜索历史和/或计划的工作。

瓶颈:

流量:12 个作业/秒并不太具有挑战性。如果这个峰值,我们可以 使用负载均衡器将作业分配到不同的服务器 执行。

数据:在 3.6 TB 时,我们需要一个哈希表,可以很容易地 查询快速访问已在 应用。

扩展抽象设计

这个作业调度器的本质是每个作业都拥有一个 几个状态:待处理、失败、成功、终止。没有业务逻辑 返回少量数据。

为了处理流量,我们可以有一个应用服务器 每秒处理 12 个请求,并在此请求失败时进行备份。在 未来,我们可以使用负载均衡器来减少请求的数量 访问每台服务器(假设 >1 台服务器正在生产中) 优势 这将是减少请求/服务器的数量,增加 可用性(如果一台服务器出现故障,并处理峰值流量 好)。

对于数据存储,要存储 3.6 TB 的数据,我们需要几台机器 将其保存在数据库中。我们可以使用 noSQL 数据库或 SQL 数据库。鉴于如何 后者有更广泛的使用和社区支持,这将有助于 在解决问题并被大公司使用时,我 会选择 mySQL 数据库。

随着数据的增长,我会采用以下策略来处理 它:

1) 在哈希上创建唯一索引

2) 通过添加更多内存垂直扩展 mySQL 数据库

3) 通过分片对数据进行分区

4) 使用主从复制策略与主-主 复制以确保数据的冗余

结论

因此,这将是我对作业调度程序组件的设计。

【问题讨论】:

标签: architecture job-scheduling n-tier-architecture system-design


【解决方案1】:

我建议你研究一下这个工作的消息总线。或者,如果您想了解此类总线允许的架构,请查看 NServiceBus。

如果您使用公共汽车,您可以轻松地限制您的队列。它可能会减慢您的处理速度,这意味着您需要研究并发性。

人们通常认为编写这样的服务很容易。不是。

需要考虑的其他一些事情..

消息失败时会发生什么。它会丢失吗?你回滚吗? 你如何扩展你的架构。您可以轻松添加新客户/消费者吗?

【讨论】:

  • @json45,谢谢。我很欣赏这些领域的研究。还有,你觉得上面的答案怎么样?
【解决方案2】:

您所描述的大部分内容已由用于调度作业和执行它们的不同框架实现。我知道的一个 - Quartz。虽然我会在 Quartz 中实现一些不同的东西,但它有据可查,并且会给你很多关于工作和他们通常面临的障碍的想法。

您描述的方法很好,但我会从中消除特定领域的问题(例如并行处理、分片、缩放)。如果要在不同的机器上运行作业,那是因为具体案例(例如为金融银行运行的作业)不能适合一台机器。我认为您作为作业引擎的开发人员不应该担心这一点。原因是您正在开发一个框架,而不是一个产品化的应用程序。

如果您要为作业引擎本身引入分片,我认为您高估了作业引擎本身的复杂性。作业执行(框架)部分本身不会有很大的偶然性。然而,具体的实现,比如银行软件作业,可能需要处理相同的数据,但不同的数据集,然后你就有了分片。因此,简而言之,引入扩展机制超出了您的工作范围。

另外,我没有看到作业执行和消息总线之间存在平行关系,所以我不评论这个方向。

【讨论】:

    【解决方案3】:

    大多数大型作业调度程序会考虑您的文档中未涵盖的方面。

    一些关键问题是:(不分先后)

    • 取消 - 您经常想终止一项长时间运行的作业,或阻止其运行。
    • 优先级 - 您通常希望高优先级作业优先于低优先级作业运行。但是以一种低优先级作业不会在生成大量作业的系统中永远等待的方式实现这一点是“不平凡的”
    • 资源 - 某些作业可能只能在具有特定资源的系统上调度。例如。有些需要大量内存、快速的本地磁盘或快速的网络访问。有效地分配这些是很棘手的。
    • 依赖关系 - 某些作业可能只有在其他作业完成后才能运行,因此无法在给定时间之前安排。
    • 截止日期 - 某些工作需要在给定时间内完成。 (或至少在给定时间开始。)
    • 权限 - 某些用户可能只能将作业提交到某些资源组,或具有某些属性,或一定数量的作业等。
    • 配额 - 某些系统为用户提供指定的系统时间量,而运行作业会从中减去。这可能会对您示例中的数字产生重大影响。
    • 暂停 - 某些系统允许检查点和暂停作业,然后再恢复。

    我确信还有更多 - 尝试查看 slurmgrid-engine 上的文档以获取更多想法。

    需要考虑的其他事项:

    1. 您的抽象设计可能需要更多细节来支持这些高级概念。
    2. 您不需要频繁访问大部分 3.6TB 数据 - 将其拆分为最近和旧数据,如果您允许对旧数据的访问速度变慢(并且命中磁盘)。
    3. 您可能有不同类别的用户,至少是“管理员”和“用户”。这对应用程序的结构意味着什么。
    4. 一个真正的作业调度应用程序每秒能够处理更多的请求 - slurm 表明每秒 33 个持续的速度和更高的突发,但我的理解是它可能会比这高得多。
    5. 通常需要通过网页以外的界面提交作业或查询作业状态 - 这对您的应用程序结构意味着什么。 (我要么对核心引擎使用更简单的提交 API,并将 Web UI 作为一个愚蠢的翻译器,并且所有其他方法都使用相同的 API,或者使用带有简单 Web 前端的 REST API))
    6. 如何检测服务器故障?两台服务器是否足以可靠地确定这一点?为此,通常使用基于仲裁的措施,或者对第三台服务器进行连接测试。如果出现故障的服务器重新上线,您会怎么做?

    【讨论】:

    • 你的 cmets 很有见地。
    • 谢谢,刚看到这个。真有见地。
    • 超级有用的答案!
    猜你喜欢
    • 2012-03-29
    • 1970-01-01
    • 1970-01-01
    • 2015-01-09
    • 1970-01-01
    • 1970-01-01
    • 2020-12-01
    • 2010-09-07
    • 2017-11-08
    相关资源
    最近更新 更多