打通 Sidekiq 的任督二脉 Ruby 和 Crystal

栏目: Ruby · 发布时间: 7年前

内容简介:打通 Sidekiq 的任督二脉 Ruby 和 Crystal

自从开始研究 Crystal 这门语言(之前也有介绍过),基本上每隔一段时间都会看看它的近况,去年 sidekiq 的作者用该语言重新实现了 sidekiq 项目而且给出了特别竟然的 对比数据

打通 Sidekiq 的任督二脉  <a href='https://www.codercto.com/topics/20648.html'>Ruby</a>  和 Crystal

相对比 Gitlab 采用 go 语言重新 gitlab_ci_runner 而学习一门新的语言达到高效率低内存的方法之外 Crystal 就像是新的希望。使用 Crystal 重新的 sidekiq 的代码也非常的简单但已经实现了核心功能和 Web UI。

本篇就给大家介绍下如果在 Ruby on Rails 的框架下调度和执行 Crystal 写的 Workers。测试环境是在 macOS 下,其他版本信息如下:

  • Ruby 2.0+
    • Rails 5.0
    • Sidekiq 5.0
  • Crystal 0.22.0+
    • Sidekiq.cr 0.7.0

配置 Rails 环境

如何配置 Ruby、Rails、Bundler、 Redis 就不在赘述,只讲核心,首先新建一个最基础的 rails 项目,不用额外的第三方辅助工具,数据库用 sqlite 减少外部依赖:

$ rails new ruby_on_rails -B -T -S -C -M  -d sqlite3

进入项目 ruby_on_rails 编辑 Gemfile :

# 修改源地址
source 'https://gems.ruby-china.org'

# 新增 sidekiq 的支持
gem 'redis-rails'
gem 'sidekiq'

# 其余的不用修改
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '~> 5.0.2'
# Use sqlite3 as the database for Active Record
gem 'sqlite3'
# Use Puma as the app server
gem 'puma', '~> 3.0'

gem 'redis-rails'
gem 'sidekiq'

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platform: :mri
end

group :development do
  # Access an IRB console on exception pages or by using <%= console %> anywhere in the code.
  gem 'web-console', '>= 3.3.0'
  gem 'listen', '~> 3.0.5'
  # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
  gem 'spring'
  gem 'spring-watcher-listen', '~> 2.0.0'
end

# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

配置好之后执行 bundle insall 安装好 Gem 的依赖,再创建文件 config/initializers/sidekiq.rb :

redis_config = { url: 'redis://localhost:6379/8' }

Sidekiq.configure_server do |config|
  config.redis = redis_config
end

Sidekiq.configure_client do |config|
  config.redis = redis_config
end

Sidekiq 的配置就完事了。

配置 Crystal 环境

Crystal 是基于 LLVM 开发,除了 Windows 以外其他绝大数系统基本上都支持(最新支持的 ARM 架构,可在树莓派上安装), 安装步骤 同样不再赘述。

安装好之后首先创建一个新项目:

$ crystal init app crystal

进入项目 crystal 编辑 Shard.yml 这是一个类似于 Gemfile 的功能,但实现的去中心化,在文件末尾新增如下依赖:

dependencies:
  sidekiq:
    github: mperham/sidekiq.cr
    branch: master

执行 shards updatecrystal deps 安装依赖即可。

编写 Workers

Worker 的功能很简单,就是做一个类似 Redis ping 的功能,Worker 在日志输出 PONG。

Ruby 版本

安装 sidekiq 后会在 rails 内置命令可生成基础模板,切换到 ruby_on_rails 目录:

$ rails g sidekiq:worker ping1
      create  app/workers/ping1_worker.rb
$ rails g sidekiq:worker ping2
      create  app/workers/ping2_worker.rb

worker 的内容也很简单,我在日志输出增加了 [Ruby] 作为 Ruby 版本的标识便于后面的辨识

# app/workers/ping1_worker.rb
class Ping1Worker
  include Sidekiq::Worker

  def perform(*args)
    logger.info "[Ruby] PONG !"
  end
end

# app/workers/ping2_worker.rb
class Ping2Worker
  include Sidekiq::Worker

  def perform(*args)
    logger.info "[Ruby] PONG PONG !!"
  end
end

Crystal 版本

切换到 crystal 目录下和 Ruby 不同的是它的源码是存放在 src 目录下面,我们稍微调整下结构:

.
├── LICENSE
├── README.md
├── lib
├── shard.lock
├── shard.yml
├── spec
└── src
    ├── crystal_server.cr
    └── workers
        ├── ping1_worker.cr
        └── ping2_worker.cr

worker 的内容如下,并设置 queue 为 crystal 用于指派使用:

# src/workers/ping1_worker.cr
class Ping1Worker
  include Sidekiq::Worker
  sidekiq_options do |job|
    job.queue = "crystal"
  end

  def perform()
    logger.info "[Crystal] PONG !"
  end
end

# src/workers/ping2_worker.cr
class Ping2Worker
  include Sidekiq::Worker
  sidekiq_options do |job|
    job.queue = "crystal"
  end

  def perform()
    logger.info "[Crystal] PONG PONG !!"
  end
end

通过代码可以看出 Ruby 和 Crystal 的代码基本上是完全一样的。

任督二脉

对于使用 Sidekiq 的童鞋都知道,如果我想执行一个队列任务,只需要调用下 Worker 本身的 perform_* 方法,这样的话根本无法调用一个不同语言版本的 Worker 否则 sidekiq 会报类似如下错误:

2017-04-26T06:19:14.187Z 50690 TID-ox4qa1k8o WARN: {"context":"Job raised exception","job":{"class":"Crystal::Ping1Worker","args":[],"retry":true,"queue":"default","jid":"42ce106d79
01a274f3db2d54","created_at":1493187554.181674,"enqueued_at":1493187554.1820428},"jobstr":"{\"class\":\"Crystal::Ping1Worker\",\"args\":[],\"retry\":true,\"queue\":\"default\",\"jid
\":\"42ce106d7901a274f3db2d54\",\"created_at\":1493187554.181674,\"enqueued_at\":1493187554.1820428}"}
2017-04-26T06:19:14.187Z 50690 TID-ox4qa1k8o WARN: NameError: uninitialized constant Crystal

打通任督二脉的关键在于两个版本都提供一个 low-level 的 API 可用于定制化调用:

Ruby 版本

job_id = Sidekiq::Client.push(
  'queue' => '',  # 指派特定的队列名,默认是 default
  'class' => '',  # Worker 的类名,可以是实例化类型或字符串类型
  'args' =>[]     # Worker 接收的参数,以数组形式传递
)

Crystal 版本

job = Sidekiq::Job.new
job.queue = "default"   # 指派特定的队列名,默认是 default
job.klass = ""          # Worker 的类名,可以是实例化类型或字符串类型
job.args = [].to_json   # Worker 接收的参数,以数组形式传递

client = Sidekiq::Client.new
job_id = client.push(job)

打通任督二脉

准备工作就绪,打通任督二脉的关键就只差一个了!那就是对于 redis 数据共享,细心的童鞋可能留意了上面只配置了 Ruby 版本的 redis 连接,但对于 Crystal 我故意留白没有说明。因为这个是最关键的一步,对于当前 sidekiq.cr 版本来说。

sidekiq.cr 对于作者来说是一次试水并没有话特别大的精力,Crystal 本身还处在开发阶段在未到达 1.0 之前会有各种 Break Changes。而且作者是非常照顾 Heroku 的开发者,默认仅支持该服务平台 Redis-to-Go 服务,因此想设置 Redis 连接信息必须通过系统的环境变量:

REDISTOGO_URL=redis://localhost:6379/8
REDIS_PROVIDER=$REDISTOGO_URL

上面的配置是不可省略的,因为我个人不懂 Heroku 给作者乱提了 PL 还被作者狠批了一顿 :(

回到话题本身,我们来继续写上 src/crystal_server.cr 关键的代码:

注意:redis 连接信息无比保证和 rails 配置的是一致的!

require "sidekiq"
require "sidekiq/cli"
require "./workers/*"

ENV["LOCAL_REDIS"] = "redis://localhost:6379/8"
ENV["REDIS_PROVIDER"] = "LOCAL_REDIS"

cli = Sidekiq::CLI.new
server = cli.configure do |config|
  # 支持中间件,默认留空即可
end

cli.run(server)

代码需要编译执行,因为不编译是无法给 sidekiq cli 传递它接受的参数(当然也有方法,我放在末尾范例源码中自己寻找)

$ crystal build src/crystal_server.cr -o crystal_server

通过上面命令把源码编译成可执行文件到项目根目录的 crystal_server 文件。

打开终端一:启动 ruby 的 sidekiq server

$ cd ruby_on_rails
$ sidekiq -q default
2017-04-26T06:47:19.299Z 76282 TID-owewdljsc INFO: Booting Sidekiq 4.2.10 with redis options {:url=>"redis://localhost:6379/8"}


         m,
         `$b
    .ss,  $$:         .,d$
    `$$P,d$P'    .,md$P"'
     ,$$$$$bmmd$$$P^'
   .d$$$$$$$$$$P'
   $$^' `"^$$$'       ____  _     _      _    _
   $:     ,$$:       / ___|(_) __| | ___| | _(_) __ _
   `b     :$$        \___ \| |/ _` |/ _ \ |/ / |/ _` |
          $$:         ___) | | (_| |  __/   <| | (_| |
          $$         |____/|_|\__,_|\___|_|\_\_|\__, |
        .d$$                                       |_|

2017-04-26T06:47:19.433Z 76282 TID-owewdljsc INFO: Running in ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-darwin16]
2017-04-26T06:47:19.433Z 76282 TID-owewdljsc INFO: See LICENSE and the LGPL-3.0 for licensing details.
2017-04-26T06:47:19.433Z 76282 TID-owewdljsc INFO: Upgrade to Sidekiq Pro for more features and support: http://sidekiq.org
2017-04-26T06:47:19.442Z 76282 TID-owewdljsc INFO: Starting processing, hit Ctrl-C to stop

打开终端二:启动 crystal 的 sidekiq server

$ cd crystal
$ ./crystal_server -q crystal

         m,
         `$b
    .ss,  $$:         .,d$
    `$$P,d$P'    .,md$P"'
     ,$$$$$bmmd$$$P^'
   .d$$$$$$$$$$P'
   $$^' `"^$$$'       ____  _     _      _    _
   $:     ,$$:       / ___|(_) __| | ___| | _(_) __ _
   `b     :$$        \___ \| |/ _` |/ _ \ |/ / |/ _` |
          $$:         ___) | | (_| |  __/   <| | (_| |
          $$         |____/|_|\__,_|\___|_|\_\_|\__, |
        .d$$                                       |_|

2017-04-26T06:48:42.755Z 83552 TID-21ybwjk  INFO: Sidekiq v0.7.0 in Crystal 0.22.0
2017-04-26T06:48:42.755Z 83552 TID-21ybwjk  INFO: Licensed for use under the terms of the GNU LGPL-3.0 license.
2017-04-26T06:48:42.755Z 83552 TID-21ybwjk  INFO: Upgrade to Sidekiq Enterprise for more features and support: http://sidekiq.org
2017-04-26T06:48:42.755Z 83552 TID-21ybwjk  INFO: Starting processing with 25 workers
2017-04-26T06:48:42.756Z 83552 TID-21ybwjk  INFO: Press Ctrl-C to stop

验证功力效果

两边的 sidekiq server 都已经跑起来了,我们先从 rails 启动 console 验证,注意留意两个 sidekiq 终端日志的输出:

$ rails console
Loading development environment (Rails 5.0.2)

# 调用 rails 本身的 ping1 和 ping2 worker
2.4.0 :001 > Sidekiq::Client.push('class' => 'Ping1Worker', 'args' =>[])
 => "961500753aa127b73ac50851"
2.4.0 :002 > Sidekiq::Client.push('class' => 'Ping2Worker', 'args' =>[])
 => "be366d2e5f44adf367853d82"

对应 rails 的 sidekiq server 会同时输出:

2017-04-26T06:53:01.722Z 76282 TID-owex58ag8 Ping1Worker JID-961500753aa127b73ac50851 INFO: start
2017-04-26T06:53:01.722Z 76282 TID-owex58ag8 Ping1Worker JID-961500753aa127b73ac50851 INFO: [Ruby] PONG !
2017-04-26T06:53:01.722Z 76282 TID-owex58ag8 Ping1Worker JID-961500753aa127b73ac50851 INFO: done: 0.0 sec
2017-04-26T06:53:52.681Z 76282 TID-owex58bs0 Ping2Worker JID-be366d2e5f44adf367853d82 INFO: start
2017-04-26T06:53:52.681Z 76282 TID-owex58bs0 Ping2Worker JID-be366d2e5f44adf367853d82 INFO: [Ruby] PONG PONG !!
2017-04-26T06:53:52.681Z 76282 TID-owex58bs0 Ping2Worker JID-be366d2e5f44adf367853d82 INFO: done: 0.0 sec

自身一脉本来就是通的没什么好稀奇的,验证另外一脉:

$ rails console
Loading development environment (Rails 5.0.2)

# 调用 crystal 的 ping1 和 ping2 worker
2.4.0 :001 > Sidekiq::Client.push('class' => 'Ping1Worker', 'args' =>[], 'queue' => 'crystal')
 => "324cf5e07b5e2999b0a45565"
2.4.0 :002 > Sidekiq::Client.push('class' => 'Ping2Worker', 'args' =>[], 'queue' => 'crystal')
 => "06c60bb9d52d9a31d48d2fdc"

看看 crystal 的 sidekiq server 的日志:

2017-04-26T06:57:11.846Z 21253 TID-1z8q4cg  JID=324cf5e07b5e2999b0a45565 INFO: Start
2017-04-26T06:57:11.846Z 21253 TID-1z8q4cg  JID=324cf5e07b5e2999b0a45565 INFO: [Crystal] PONG !
2017-04-26T06:57:11.846Z 21253 TID-1z8q4cg  JID=324cf5e07b5e2999b0a45565 INFO: Done: 0.000046 sec
2017-04-26T06:57:20.785Z 21253 TID-1z8q3y8  JID=06c60bb9d52d9a31d48d2fdc INFO: Start
2017-04-26T06:57:20.785Z 21253 TID-1z8q3y8  JID=06c60bb9d52d9a31d48d2fdc INFO: [Crystal] PONG PONG !!
2017-04-26T06:57:20.785Z 21253 TID-1z8q3y8  JID=06c60bb9d52d9a31d48d2fdc INFO: Done: 0.000049 sec

验证通过!打通了!

Crystal 这边向 Ruby 调用也可行,但只有通过如下代码,有个别时候自身调用也没有日志输出,不过在 Web UI 却发现已处理的数字已正常更新,该问题我再调查下。

require "sidekiq"

ENV["LOCAL_REDIS"] = "redis://localhost:6379/8"
ENV["REDIS_PROVIDER"] = "LOCAL_REDIS"

workers = %w(Ping1Worker Ping2Worker)
workers.each do |wk_class|
  job = Sidekiq::Job.new
  job.queue = "default"
  job.klass = wk_class

  Sidekiq::Client.default_context = Sidekiq::Client::Context.new

  client = Sidekiq::Client.new
  job_id = client.push(job)
  puts "[#{wk_class}] job id: #{job_id}"
end
2017-04-26T07:20:58.754Z 62256 TID-oukzi7jck Ping1Worker JID-1fee81b35052cba1f6525de5 INFO: start
2017-04-26T07:20:58.754Z 62256 TID-oukzi7jck Ping1Worker JID-1fee81b35052cba1f6525de5 INFO: [Ruby] PONG !
2017-04-26T07:20:58.755Z 62256 TID-oukzi7jck Ping1Worker JID-1fee81b35052cba1f6525de5 INFO: done: 0.0 sec
2017-04-26T07:20:58.756Z 62256 TID-oul02vzfw Ping2Worker JID-0bb7eef097447784fb48d943 INFO: start
2017-04-26T07:20:58.756Z 62256 TID-oul02vzfw Ping2Worker JID-0bb7eef097447784fb48d943 INFO: [Ruby] PONG PONG !!
2017-04-26T07:20:58.756Z 62256 TID-oul02vzfw Ping2Worker JID-0bb7eef097447784fb48d943 INFO: done: 0.0 sec

结语

本篇只是通过一个最简单的例子让大家知道互通的方法,实际使用中对于数据交互等还有更多需要考虑的地方,这里就暂时不做展开。非常期待 Crystal 今年立的 1.0 的目标

对于 Crystal 语言本身的评价,大家也可看下 RubyChina 站长的心得 Crystal 说我最近关注 Crystal 的感受 ,编译语言有编译语言的坑,入坑需谨慎。

本文演示的代码已经整理并放到了 Github ,对于不明白的地方可配合代码更好服用。


以上所述就是小编给大家介绍的《打通 Sidekiq 的任督二脉 Ruby 和 Crystal》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Probabilistic Method Second Edition

The Probabilistic Method Second Edition

Noga Alon、Joel H. Spencer / Wiley-Blackwell / 2000 / $121.95

The leading reference on probabilistic methods in combinatorics-now expanded and updated When it was first published in 1991, The Probabilistic Method became instantly the standard reference on one......一起来看看 《The Probabilistic Method Second Edition》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具