队列路由规则

当 Sidekiq 作业数量增加到一定规模时,系统会面临一些可扩展性问题。其中之一是队列的长度趋于变长。高度紧急的作业必须等待更长时间,直到其他不太紧急的作业完成。这种行列头部阻塞情况最终可能会影响系统的响应能力,尤其是关键操作。在另一种情况下,某些作业的性能会因同一台机器中的其他长时间运行或 CPU 密集型作业(计算或渲染作业)而降低。

为了解决上述问题,一种有效的解决方案是将 Sidekiq 作业拆分为不同的队列,并分配专门处理每个队列的机器。例如,所有 CPU 密集型作业都可以路由到 cpu-bound 队列并由一组 CPU 优化实例处理。不同公司的队列拓扑因工作负载和使用模式而异。因此,极狐GitLab 支持一种灵活的机制,管理员可以根据作业的特征来路由作业。

作为队列选择器的替代方案,它将 Sidekiq 集群配置为侦听一组特定的 worker 或队列,极狐GitLab 还支持在调度时将作业从 worker 路由到所需的队列。Sidekiq 客户端尝试将作业与配置的路由规则列表进行匹配。规则从头到尾进行处理,一旦我们找到给定 worker 的匹配项,我们就会停止处理该 worker(第一次匹配优先)。 如果 worker 不匹配任何规则,它将回退到从 worker 名称生成的队列名称。

默认情况下,如果未配置路由规则(或用空数组表示),则所有作业都将路由到从 worker 名称生成的队列中。

配置示例

/etc/gitlab/gitlab.rb

sidekiq['routing_rules'] = [
  # Do not re-route workers that require their own queue
  ['tags=needs_own_queue', nil],
  # Route all non-CPU-bound workers that are high urgency to `high-urgency` queue
  ['resource_boundary!=cpu&urgency=high', 'high-urgency'],
  # Route all database, gitaly and global search workers that are throttled to `throttled` queue
  ['feature_category=database,gitaly,global_search&urgency=throttled', 'throttled'],
  # Route all workers having contact with outside work to a `network-intenstive` queue
  ['has_external_dependencies=true|feature_category=hooks|tags=network', 'network-intensive'],
  # Route all import workers to the queues generated by the worker name, for
  # example, JiraImportWorker to `jira_import`, SVNWorker to `svn_worker`
  ['feature_category=import', nil],
  # Wildcard matching, route the rest to `default` queue
  ['*', 'default']
]

路由规则列表是查询元组和相应队列的顺序问题数组:

  • 查询遵循 worker 匹配查询 语法。
  • <queue_name> 必须是有效的 Sidekiq 队列名称。如果队列名称为 nil 或空字符串,则将 worker 路由到由 worker 名称生成的队列。

该查询支持通配符匹配 *,它匹配所有 worker。因此,通配符查询必须留在列表的末尾,否则后面的规则将被忽略。

note混合队列路由规则和队列选择器需要注意确保所有作业都由适当的 Sidekiq worker 安排和拾取。

Worker 匹配查询

极狐GitLab 提供了一种查询语法来根据其属性匹配 worker。队列路由规则队列选择器都使用此查询语法。查询包括两个部分:

  • 可以选择的属性。
  • 用于构造查询的运算符。

可用属性

引入于 13.1 版本 (tags) 。

队列匹配查询适用于 worker 属性。我们支持基于 worker 属性子集的查询:

  • feature_category - 队列所属的极狐GitLab 功能类别。例如,merge 队列属于 source_code_management 类别。
  • has_external_dependencies - 队列是否连接到外部服务。例如,所有导入器都将此设置为 true
  • urgency - 该队列的作业快速运行是多么重要。可以是 highlowthrottled。例如,authorized_projects 队列用于刷新用户权限,是 high 紧急的。
  • worker_name - worker 名称。使用此属性来选择特定的 worker。
  • name - 从 worker 名称生成的队列名称。使用此属性选择特定队列。因为这是从 worker 名称生成的,所以不会根据其他路由规则的结果而改变。
  • resource_boundary - 队列被 cpumemoryunknown 绑定。例如,ProjectExportWorker 是内存绑定的,因为它必须在保存数据以供导出之前将数据加载到内存中。
  • tags - 队列的短期注释。这些预计会在不同版本之间频繁更改,并且可能会被完全删除。

has_external_dependencies 是一个 boolean 属性:只有确切的字符串 true 被认定为 true,其他的都被认定为 false。

tags 是一个集合,这意味着 = 检查相交集,而 != 检查不相交集。例如,tags=a,b 选择具有标签 ab 或两者兼有的队列。tags!=a,b 选择没有这些标签的队列。

每个 worker 的属性都在源代码中硬编码。为了方便,生成了一个适用于社区版和付费版的所有可用属性列表,和一个仅适用于付费版的所有可用属性列表

可用的运算符

queue_selector 支持以下运算符,按优先级从高到低列出:

  • | - 逻辑 OR 运算符。例如,query_a|query_b(其中 query_aquery_b 是由此处的其他运算符组成的查询)包括匹配任一查询的队列。
  • & - 逻辑 AND 运算符。例如,query_a&query_b(其中 query_aquery_b 是由此处的其他运算符组成的查询)将仅包括与这两个查询匹配的队列。
  • != - NOT IN 运算符。例如,feature_category!=issue_trackingissue_tracking 特征类别中排除所有队列。
  • = - IN 运算符。例如,resource_boundary=cpu 包括所有受 CPU 限制的队列。
  • , - the concatenate set 运算符。例如,feature_category=continuous_integration,pages 包括来自 continuous_integration 类别或 pages 类别的所有队列。此示例也可以使用 OR 运算符,但允许更简洁,并且优先级更低。

此语法的运算符优先级是固定的:不可能使 AND 具有比 OR 更高的优先级。

在 12.9 及更高版本中,与上面的标准队列组语法一样,单个 * 作为整个队列组选择所有队列。

迁移

在 Sidekiq 路由规则更改后,管理员必须小心迁移以避免完全丢失作业,尤其是在作业队列很长的系统中。可以按照 Sidekiq 作业迁移中提到的迁移步骤完成迁移。

无法被迁移的 Workers

一些 workers 不能与其他 workers 共享队列,通常是因为他们检查自己队列的大小,因此必须从这个过程中排除。我们建议通过添加规则将它们从任何进一步的 worker 路由中排除,将它们保留在自己的队列中,例如:

sidekiq['routing_rules'] = [
  ['tags=needs_own_queue', nil],
  # ...
]

这些队列还必须包含在至少一个 Sidekiq 队列组中。