漫画之前使用了 12 台 8 核 16G 机器 跑 sidekiq 的任务
每次重启 sidekiq 都会造成大量积压 而且要很久才能跑完,所以一般都不太敢重启 sidekiq
7 月初@tency 带头用 golang 重写了大部分 worker 逻辑 用了这个库 https://github.com/jrallison/go-workers
最终还没有重构完的 ruby worker 占两台,go worker 只用了一台 而且基本上没什么负载
有些事情确实不是 ruby 的强项。
大家关注点有点歪啊,第一时间怀疑我们业务设计有问题么。 其实主要问题还是量大 每天跑 800 万任务
每秒
抛个问题,像这样的重型 worker 系统,大家怎么选方案?
最后最后,有人想来一起维护这个嘛?[email protected]
评论区几个有价值的观点:
@luikore :worker 一次取多个任务,然后把 io 读写都 batch。https://github.com/gzigzigzeo/sidekiq-grouping
@quakewang :重度网络 IO 试试 异步的 http client
#12 楼 @steven_yue 不能保证使用的 gem 性能很好,比如说如果 gem 没有单独用 java 写用到 java.nio 包的话即使用了 jruby 在 io 方面性能也好不到哪去
同问,
为啥不考虑 jruby?
如果用 golang 重写的话,所有的业务逻辑是不是都要重新实现一遍?而且要和 Rails 中的业务逻辑始终保持一致,维护的工作量是不是有点大?
我还是习惯性的想为 ruby 辩解几句,sidekiq_worker(重构前的 worker) = sidekiq + 暴走 rails app, go_worker(重构后的 worker) = go_workers + 精简的暴走业务代码,看到区别了没有,一个是拖家带口臃肿不堪,一个是轻装上阵如履平地。按楼主提供的信息:8 核 16G 机器 跑 sidekiq 的任务,我保守猜测一下,一台机器启动了 8 * 2 = 16 个 workers, 每个 worker 消耗 500M 内存 (我现在有一个中等规模的 rails 项目,每个 woker 平均消耗 400 M 内存,以暴走的规模来看应该比我们消耗的内存多些), 总共 8 G 内存,什么任务都没有跑就已经吃掉 8G 内存了,但这不是 ruby 的错,是 rails 的错。其实如果脱离 rails, 单纯用 ruby 来写这些后台业务代码,效果也会不错的。
#25 楼 @kayakjiang 是的 ruby 本身不慢,我们 api 接口,响应时间 80ms 吞吐量 70kpm, 单就 worker 系统,在暴走漫画 ruby 已经不适合了。
#25 楼 @kayakjiang 另外再说下 sidekiq 的情况,硬件资源只能用到 50%,如果在提高并发量,由于 GIL 的存在 每个任务都会更慢 我们试验过多次。现在比较理想的是 150 并发
这个量级的任务,我猜是要按照订阅生成 feeds timeline? worker 一次取多个任务,然后把 io 读写都 batch 就嗷嗷快了 如果是做矩阵计算做个性化推荐或者聚类,都用的 C 实现,换语言也不会快
800 万一天是一台服务器要处理的量?平均每秒要处理 100 个不到的任务,估算到高峰是 700~800 每秒的任务处理。 根据经验,对 8 核 16G 的服务器,可以开 100 个左右 worker,处于 20%~30% 的负载水平。 也就是一个 worker 在高峰时间每秒要处理 7~8 个,就算用了 persistent client,对第 3 方服务的响应时间也要在 100ms 以下。 超过这个时间,就会积压,可以考虑换异步的 http client 试试看,或者类似你们干脆换语言。
#43 楼 @quakewang 我们实际的情况是,高峰时期远远不止每秒处理 800 个,用户一个操作会触发 2~3 个任务,并且这些任务还需要尽快产生结果,所以我们原有的 12 台机器,是为了应对峰值,而非平均的处理一天的量来搭建的。 另外一个情况就是,sidekiq 的实际处理速度,单机是有瓶颈的,纯粹的数据库+redis 操作,一般也要 50ms 左右,并且还存在大量消耗数据库连接的问题。 最后就是,ruby 的执行速度,确实不是很快。
所以干脆换语言重新实现。上面说的数据库+redis 操作的 task,在新系统下只有 10ms 左右,提升非常明显。
回答一下为啥不考虑 jruby 的问题
@shawnyu @tency 以下引用自 sidekiq FAQ 页面,所以数据可能会有水分。
The largest customers I'm aware of are processing ~500,000 jobs/min with Sidekiq with one customer reporting a peak of ~50,000 jobs/sec. Note that on dedicated hardware, Redis should be able to handle about 7000-8000 jobs/sec. After that, you'll need to shard your application to use multiple Redis instances or use multiple independent applications.
我不是特别能理解,后台任务多是 IO 相关,但是为什么使用sidekiq
和go-works
会有这么大的差距。
两者的 queue 都是利用了redis
的BRPOPLPUSH
,这方面应该差距不大。
假设单台机器 8x25 个 thread,200 个 sidekiq 的 worker,并发上应该问题也不大。
有没有可能是某些任务特别慢(比如网络连接有问题,单纯在等 timeout 时间),占住了 worker,或者其他什么原因呢?
如果两位有时间,能不能介绍下相关情况,让我们选型时可以参考一下。
#54 楼 @serco 先介绍下我们实际项目的情况,暴走漫画采用的是 RoR 框架,sidekiq 也是集成在项目内,也就是说 web 和 sidekiq 共用一套代码。我们的 sidekiq 系统无法满足我们的处理需求的原因,我分析下面几个原因:
可能还有别的原因造成整个系统有处理瓶颈,你说的网络 io、timeout 的问题,都会导致单个 sidekiq 线程暂停,不过这个问题在 go 系统中也存在,所以在比较两个系统差异上,这个不是核心。
简单来说,两方对比,语言执行效率、内存占用、并发模型、业务核心库的线程安全处理上,让新的 go 系统的处理效率远远高于 sidekiq。
学习了,之前公司在实际过程中也发现 sidekiq 占用 cpu 过高,负载比较大的情况
部分同意观点,主要还是 sidekiq 和 rails 业务代码相关联,带了很多不必要的库,而且 go 的并发性能有很大提升
但是用 go 重写推送部分的业务逻辑的话,整体的性能也会有所提高吧?对业务代码进行改造,会不会也在一定程度上提高了 worker 的性能呢?也就是大概多少比例是语言本身的优势,哪些是业务简化,代码优化带来的提升?
还有个问题,这个改造的成本会有点高吧?比如之前用 sidekiq 的 proxy 写的代码必须转换成全部都是传递数据的形似,比如 ruby-china 社区的源码就大量采用了 dalayed 这种写法,会把 ruby 的调用转成 yaml 序列化,go 用来做 worker 的话这块就完全没法用了吧?
第一点有影响但问题没那么严重,如果你指的是 GIL,那么 IO 并发还是没有太大影响的。 之前没有了解仔细看过 redis-rb 的代码,我觉得你说的第二点应该是最致命的。
timeout 的问题在 go 系统中应该没有太大影响,sidekiq 一般一开始就初始化了 25 个左右的 worker,卡住一个就会损失一部分处理能力。Go 的话,轻量级进程卡住就卡住了,完全不影响其他请求的处理。
#25 楼 @kayakjiang 看这个说法,应该最大的问题是 Ruby Sidekiq 启动起来带了 Rails 应用的内存暂用,内存费太多,workers 数量无法提升上去
挺好的。用 sidekiq 搭配 redis 实现了 mq 作用的东西。 其实 sidekiq 的任务是可以和主项目分离的,用 sidekiq 推任务。 另写一个轻量级的 ruby 项目来完成任务。
主要原因还是有点不明确,是 redis-rb 的问题还是 ruby GIL 的问题。是不是升级 ruby 版本可以解决 GIL 的问题? 我们现在把涉及外部的都用 rabbitmq 做成异步的调用,有时候是调用其他项目的 api 有时候是推入某个队列。
#60 楼 @tency https://github.com/mperham/sidekiq/blob/2fc1d81cc9691c9232a088f1410c5a27fae5e11f/lib/sidekiq/extensions/generic_proxy.rb#L19
如果用 proxy 的话就会有 yaml。。。go-worker 好像不能处理吧?
猜测一下:是不是 GO 使用的事件驱动模型,ruby 用的是线程模型,大量慢速 IO(网络请求)下,ruby 只能增加线程数量,增加线程数量又会带来线程切换开销。这也是楼主说增加线程数量,处理能力反而下降的原因。 BTW:这种场景 Go 确实很不错,曾经让小弟用 ruby 实现了一个百万连接的消息推送服务,连接是可以,但是大并发推送消息就挂了。用 Go 重写了,问题解决。Go 在这方面还是很有优势的。
新手问下,如果用 go worker 的话,CRUD 是不是都得用 golang 的库了?ActiveRecord 就没法用了? 楼主公司 golang 用的什么 ORM?或者直接是 SQL?
Ruby 在这里是有两个弱点 1、内存占用多。 2、如果涉及大量运算,会稍慢一些。 关于第一点,内存占用多式 Rails 的问题。所以剥离开就好了。 关于第二点,貌似你没有提到你们有大量的运算。 所以你们的问题,用 Ruby 完全可以解决(也许 Sidekiq 不适合)。 当然你们用 Go 也是没有问题,因为恰好你们有人会用,而且解决了你们的问题。
所以目前我能看到的就是你们还是没有找到根本原因,没有找到本质问题,你当然解决不了。 而且你们用的 Go,也是恰巧解决了你们的问题,但是你们还是不知道为啥 Go 可以解决。
哈哈,所以这是个悬案!
今天忽然看到这个帖子。看了一下我们也是用 sidekiq,一天 800w 任务左右。一部 15G 2vCPU 的虚拟服务器就搞定了。开 12 个线程。