Rails Go 写个小程序,替换掉 Sidekiq

moliliang · 发布于 2017年5月15日 · 最后由 IChou 回复于 2017年5月20日 · 1304 次阅读
10594

ruby真是内存大户啊~

我的小破站运行在阿里云 1g内存上,mysql+sidekiq+puma 这三个内存大户吃了大部分的内存。

时不时从swap交换数据的时候,整个系统的负载就飙升,top NO.1只看到个 kswap[xxx]的进程。

所以我决定将MEM NO.1 的sidekiq去掉。

  • 同时不需要对rails中的队列业务做任何改动
  • 也不需要对一直的定时任务做任何修改

于是就用写了这个程序 https://github.com/molisoft/rkejob/

除了是一个队列(感觉这样改进后,更像是将请求异步化了),还是一个定时任务工具(因为我之前用Sidetiq作为定时任务工具)

之前的rails的队列流程如下:

rails  -> redis -> sidekiq(Runing)

现在的流程:

rails -> redis -> rkejob ---(POST)---> rails

什么?! 最后请求又请求回来了?没错啊,最后又是rails web端来执行队列任务了。所以相当于将“请求异步化”了~~~

这样的设计,其实就注定不适合做那种分分钟耗时数分钟,数十分钟的任务了~~~ 请酌情使用~~~ 保平安

rkejob 如何配置使用

在rkejob程序的同目录下新建配置 config.yml

redis:
  host: localhost
  port: 6379

queue:
  pool: 30
  concurrency: 3
  namespace: "namespace_sidekiq"
  database: 0
  queues:
    - default

job:
  url: "https://www.xxx.com/myjobs"

crons:
  -
    name: "CheckServerStatus"
    url: "https://www.xxx.com/mycrons"
    spec: "0 0 3 * * *"   # 每天凌晨3点运行
  -
    name: "CheckXXXXXStatus"
    url: "https://www.xxx.com/mycrons"
    spec: "@every 10m"

这样每一个队列任务都会被请求到 job.url 这个配置中,顺便会sidekiq请求过去的参数也一并请求到 rails-web 中了,下面会介绍rails-web端怎么处理这些数据。

crons 就是配置定时任务了~

rails-web端要做的事

rails这边需要做呢?几乎不需要做任何任务改动,只需要加2个action,分别处理job和cron。

require 'yaml'

# 这个比较重要哦~ 因为写入到redis中的数据是可以直接被yaml解析成ruby代码的,会出问题,所以这个加个猴子补丁~~
Psych::Visitors::ToRuby.prepend Module.new {
  def resolve_class(klass_name)
    klass_name && klass_name.safe_constantize || super
  end
}

class MyJobsController < ApplicationController

  skip_before_action :verify_authenticity_token, :only => [:myjobs, :mycron]

  before_action :only_local

  # 队列
  #
  def myjobs
    request_body = request.raw_post
    (target, method_name, args) = YAML.load(request_body)

    if target.to_s == 'Mailer'
      eval("#{target}.#{method_name}(*args)").deliver_now()
    else
      eval("#{target}.new.#{method_name}(*args)")
    end
    render :text => 'success'
  end

  # 定时任务
  #
  def mycron
    target = request.raw_post
    eval("#{target}.new.perform()")
    render :text => 'success'
  end

  private

  # 这里最好加验证,禁止外网请求 :)你懂的
  def only_local
    # if request.remote_ip != '127.0.0.1'
    #   redirect_to root_path
    # end
  end
end

后话

已在我的小破站上跑了几天了~~ 看日志来看~~~ 还可以~~~

golang真是不错啊~~~

重要的事,只说一遍…… 这玩意不适合跑非常耗时的任务哦~~~

共收到 37 条回复
1

1G 内存,节约了 5 刀

718

这。。。

sidekiq 本来就已经异步+并发了啊。

你这么做,最终任务还是落在 ruby 上处理,并不会使用更少内存。

不如直接用 golang 把后台任务重写了。https://github.com/jrallison/go-workers

但是既然只是个 1G 内存的机器,说明你的业务并没有太值钱到需要重写的地步。多花 $5 看来比较划算。

3

可以在需要执行后台任务的地方 Thread.new 然后在内部用 perform_now 的方式执行,或者直接用 https://rubygems.org/gems/sucker_punch 这样的库直接帮你把异步任务在一个线程池内搞定,这你连 Redis 依赖都不需要了。

2456

我可以认为你只是找个借口写 Golang.

10594
1Rei 回复

主要是折腾·· 日积月累也不少,哈哈哈

10594
718ch3n 回复

完全不一样啊,sidekiq会在后台一直驻留吃内存啊。 就是不想重写业务才这么整。

10594
2456zlx_star 回复

差别还是很大的。如果都是轻量级的任务,比如发邮件,发短信什么的,几秒钟的任务用这个来做还是很方便的。耗时的任务当然还是用golang重写业务啦。这个文中已阐述~~~ 😅

10594
3lgn21st 回复

噗,很有道理,突然觉得 @zlx_star 说的也有道理。。hoho,但是看起来不屌啊~~~ 😭 定时任务也可以直接写个ruby脚本放crontab中,然后post吗~~~ = =!

10594
3lgn21st 回复

我再一想,不对啊,这样搞我得重写多少代码啊~~~ 😂

96

话说需要缓冲的请求,通常都怎么处理?都用队列?

10594
32jetspeed 回复

看实际需求嘛,发邮件,发短信这种耗时几秒几十秒的,可以用这些啊

1

看看 active job 的后端,有的后端是不用开新进程的。

3
10594moliliang 回复

你试试看 sucker_punch 你就会发现比现在要少写很多代码。

10594
3lgn21st 回复

看了下这玩意确实不错,基于线程的。 那定时任务呢~

De6df3

理论上来说,你这么做没有意义

  1. Sidekiq 耗内存,原因是因为 Sidekiq 的进程载入了整个 Rails 项目的代码,用于执行 Rails 以及应用的代码。
  2. 你当然你这么做当然能省内存,原因是因为 Go 的进程不再有 Rails 项目的代码,但是... 你的业务逻辑得重新实现,或像你现在这样调用 Rails 的接口来实现。如果是这样, 那用 Go 来写 Sidekiq 要做的事情没有任何意义,你 Sidekiq 也可以独立跑,不加载 Rails 的东西,内存就少了。
  3. Sidekiq 也是并发的实现,而大量耗费资源的地方是处理逻辑,不是 Sidekiq 本身,单纯改实现来说,提升的效果几乎可以忽略(除非所有业务实现用 Go 来写)
3
10594moliliang 回复

定时任务你自己不是已经有答案了么?写一个 rake task,用 cron job 去调用呀。

10594
3lgn21st 回复

很久之前就这样做过了,每次rake也会载入整个rails,其实依然慢

3
10594moliliang 回复

5 美刀一个月,其实就是一杯星巴克的事,看你纠结成这个样子......

10594
De6df3huacnlee 回复

1、这个我当然知道呀~ 2、嗯 3、整个sidekiq的cpu的占用是非常低的,完全就是 占内存。我的初衷是减少内存占用,而不是降低cpu资源。

sidekiq只所以占内存是因为会载入rails,这个大家都知道。 可是你说这样做没意义,我不认同。

之前sidekiq+puma服务器,就要载入2份rails,也就是rails会吃2份内存(一份几百M)。现在只有puma会载入rails,也就是少了一份内存占用。puma本身也会有更的内存来提高效能。

而执行相关任务时的内存占用,几乎可以忽略不计。

所以,我不觉得这样做没意义:)

10594
3lgn21st 回复

我擦,钱能解决的事当然都是小事。但是你说一个宅男宅家里,除了撸代码还能干啥

3
10594moliliang 回复

我的答案是玩《赛尔达传说荒野之息》

96

用 ActiveJob 的 async adapter 多好

10594
3lgn21st 回复

😂 家里的xbox都懒得玩~~~

8744

happy hacking😂

语法高亮可以指定 ruby 的

21568

666,还是第一次看到有把异步任务再post回来处理的。。。

4209

折腾折腾是对的,哈哈哈。最近我也在研究用go重写Ruby

4257

宅会玩。

2650

@moliliang 我觉得对于你的问题来说,你的解决方法很新颖,而且很简洁,需要赞一个!

11562
3lgn21st 回复

你这个回复让我笑了一路

10594
2650blueplanet 回复

感觉你在黑我

10594
8744lithium4010 回复

谢谢提醒,哈哈

10594
11562hging 回复

很幽默的

10594
4209hanluner 回复

折腾并学习嘛

53826f

Crystal版的sidekiq,同一个作者:sidekiq.cr

10594
53826fwmzsonic 回复

1、你的头像好赞哦 2、这玩意能无缝使用sidekiq中的业务代码?应该不能载入rails吧? 3、看了下源码,真是跟ruby一模一样啊~~

9821

用 crystal 写 worker 不是更合适吗,可参见:https://ruby-china.org/topics/32893

3035

赞 挺有意思的小东西,虽然很少有应用场景,哈哈

机器内存不够,又不想多花钱的痛我懂,当年阿里云 1G 内存的机器上 cap 内存不够,逼得我去用 shell 写了个部署脚本

不过我的话,估计会把 sidekiq 的消费者抽出来用纯ruby重写,然后你就发现它不是辣么吃内存了😅

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册