[分享] 极狐GitLab如何提升 sidekiq 服务性能

Sidekiq 是 Ruby-on-Rails 的后台作业处理器,并使用 Redis 作为作业队列的数据存储。后台作业处理对 GitLab 至关重要,这些作业或许不都是需要实时执行的,或许优先级并不高,又或许执行时间很长。因此这些作业不应该占用相对昂贵的 HTTP连接来执行长时间运行的操作。

对于大多数用户来说,Sidekiq 如何使用 Redis 并不重要,重要的是我们可以通过何种方式降低 Redis 的压力从而获得更高的性能。

Sidekiq 队列以 LIST 数据结构存储在 Redis 中 ,它实际上是一个有序的条目序列。对于 Sidekiq,每个条目都是一些 JSON,描述了要做的工作(Ruby class + arguments)和一些元数据。开箱即用的 Sidekiq 使用名为“default”的单个队列,但可以创建和使用任意数量的其他命名队列。作业使用 RPUSH 在列表的末尾排队,并使用 BRPOP 从列表的前面检索以执行。

本文将介绍如何通过配置在一定程度上面提升 Sidekiq 的性能。主要涉及以下几个方面:

  • 多进程
    Omnibus 方式的部署中,Sidekiq服务 默认以 GitLab 实例组件的形式存在,和其他组件共用一台服务器资源。通过 ps -ef | grep sidekiq 可以看到Sidekiq 进程默认启动一个,称之为 default 进程,用以处理所有队列,并可以通过启动多个 worker thread 的形式并发处理队列和减少队列阻塞。

    配置方式也非常简单,只要根据默认定义的队列名称创建新的 queue_group 即可。
    示例:

sidekiq['queue_groups'] = [
 "elastic_commit_indexer, elastic_association_indexer",
 "mailers",
 "*"
]
  • 队列选择器
    队列选择器可以理解为以上配置的进阶版本,此配置可以将众多队列更加方便快捷的进行分组,但是弱点也是依然存在的,目前测试看起来分组并不是很严格,例如队列 project_export 即使定义了新的 group,但是可能依旧被路由到 default 。

    以上两种方式对 redis 而言,并没有明显的区别。redis依然要同时处理多达 400+的队列,如果 GitLab 业务较为繁忙,依然会给 redis带来不小的压力。

  • 路由规则
    路由规则是比较推荐的方式。相较于以上两种方式有着较大的优势。以上两种方式仅是在默认的基础上启动了多个 Sidekiq process 去处理400+数量的队列,而路由规则则是在启动多个 Sidekiq process的基础上,将 job 重新路由到定义的新队列,因为新定义的队列数量较少,Sidekiq 处理起来也会性能更好。使用路由规则可以大大减少队列的数量,这样就可以减少 Sidekiq 扫描 Redis 队列的时间,并且可以根据需要将队列和 group 进行映射,减少了 Sidekiq 处理队列的复杂性。


  • 外部 Sidekiq 实例
    外部实例可以看做在路由规则的基础上的再一次扩展,因为多进程启动的进程都在 GitLab Server本台服务器上面,消耗的仍然是本机的资源,如果机器资源足够,这可能会提升性能,否则这可能造成对机器资源进一步争用。
    使用外部 Sidekiq 实例能很好的解决这个问题,可以隔离 Sidekiq 资源到外部的机器上面。不仅如此,在配置了多个 Sidekiq 实例后,可以使用路由规则将指定的队列路由到不同配置的 Sidekiq 实例上面。
    示例:
    高CPU需求队列: resource_boundary=cpu&urgency=high 的队列可以路由到 CPU 性能好的 Sidekiq实例
    高内存需求队列:resource_boundary=memory&urgency=high的队列可以路由到内存充足的 Sidekiq实例
    最终可以达到如下效果: