16

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

我在网上阅读了很多资源,但在前进时需要一些具体的指导。

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

用例

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

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

约束

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

= 用户数/小时的作业数 + 机器数/小时的作业数

= 1m * 0.5 /天/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 db。

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

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

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

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

4)采用主从复制策略,主从复制,保证数据冗余

结论

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

4

3 回答 3

38

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

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

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

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

需要考虑的其他事项:

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

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

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

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

还有一个,我看不到作业执行和消息总线之间的平行关系,所以我不评论这个方向。

于 2014-10-13T16:01:46.470 回答
2

我建议您查看此工作的消息总线。或者,如果您想了解这种总线允许的架构,请查看 NServiceBus。

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

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

其他一些事情要考虑..

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

于 2014-09-30T11:58:54.340 回答