利用 Shoryuken and SQS 快速处理 API 请求

3,229 阅读6分钟
原文链接: github.com

Rails 为后台工作提供了相当多的解决方案。其中一个就是被称为 Sidkiq 的智能 Gem, 我们之前在 Sitepoint 上提到过。

Sidekiq 相当棒,能解决大多数开发中的问题。尤其是 Rails 繁重问题上相当有用。然而,它也有一些不足。

  • 如果你不是专业版用户(每年支付750美元),而你的进程崩溃的话,将会丢失你的工作
  • 如果你的工作量增加,你需要更强大版本的 Redis ,而它耗费更多的成本和资源。
  • 为了监控你工作上发生的一切,你需要把握它的控制面板。

想要解决排队任务的你需要考虑的另一种方法是 Shoryuken ,它和亚马逊的 SQS (Simple Queue Service)协同工作。这是一个基本消息存储,之后你可以通过 Shoryukeσn workers 进行处理。这些 workers 之后在主 Rails 进程外部工作。有了 SQS 和 Shoryuken,你可以为 workers 创建队列利用 workers 循环任务直到队列空闲。

使用 Shoryuken 有以下好处:

  • 他是以 Amazon SQS 构建的,难以置信的便宜(每一百万次 Amazon SQS Requests 只要0.5美元)。
  • SQS 是用来规模化作业的。利用亚马逊这个令人惊喜的基础配置,你可以轻松地简化你的 workers 。
  • 亚马逊提供了一个简单的控制台来查看队列以及配置死消息的情况。
  • 亚马逊的 Ruby SDK 在创建队列的时候十分灵活。如果你愿意可以创建很多队列。

本文档中,我会带你来配置带有 Flickr API 的 Shoryuken 。你将见证 Shoryuken 如何在后台光速处理任务。

为了开始这个教程,我们将用 Flickr API 来创建一个简单的搜索框,这样就可以根据 id 输入来生成照片。

  1. 首先,我们需要设置雅虎账户,因为这是我们可以访问 Flickr API 唯一的方式。配好雅虎账户之后,简单地查看一下Flickr 文档页面。

  2. 在 Flickr 文档 页面单击创建一个应用 链接。

  3. 在 Flickr.com 申请一个非商业密钥。

  4. 下一个页面当中,你会被要求输入项目的具体信息,简单地填入项目名称和事项等即可。

  5. 你会收到应用的密钥和密码。把他们写在某个地方,因为这个教程当中需要用到。

接下来,搭建一个单控制器行为的 Rails app 。要生成新的 Rails app,利用如下命令行生成:

rails new shoryuken_flickr

下一步,配置控制器。带有index行为的控制器行为是完美的:

rails g controller Search index

在config/routes.rb 里添加一个根路径到这个操作上:

root  'search#index'

在索引页,设置一个简单的搜索框:

<%= form_tag("/", method: "get") do %>
  <%= text_field_tag(:flickr_id) %>
  <%= submit_tag("Search") %>
<% end %>

我们必须配置 Flickr 模块来返回用户提交 id 的照片:

  1. 首先,我们得安装[ flickr_fu ] (github.com/commonthrea…),这样更容易抓取我们需要的数据。
  2. 利用相关凭证配置 flickr.yml 文件。这个文件在 config 文件夹里作业,看起来是这样的:

    key: <%= ENV["flickr<em>key"] %>
    secret: <%= ENV["flickr</em>secret"] %>
    token_cache: "token_cache.yml
    
  3. 现在我们可以为目录页创建一个 helper 方法来返回照片。在 app/helpers/search_helper.rb 添加如下内容:

    module SearchHelper
      def user_photos(user_id, photo_count = 5)
        flickr = Flickr.new(File.join(Rails.root, 'config','flickr.yml'))
        flickr.photos.search(:user_id => user_id).values_at(0..(photo_count - 1))
      end
    end
    

基于提供的用户 id 这种方法可以返回照片。在 app/controllers/search_controller.rb ,需要一个操作来抓取数据:

  class SearchController < ApplicationController
    def index
      if params[:flickr_id]
        @photos = user_photos(params[:flickr_id],10).in_groups_of(2)
        @id = params[:flickr_id]
      end
    end
  end

现在,只要创建一个小的片段来生成照片。在 app/views/search 里通过如下代码添加一个 photos.html.erb 文件:

  <ul>
    <% @photos.each do |photo| %>
      <li> <% photo.each do |p| %>
      <%= link_to(image_tag(p.url(:square), :title => p.title, :border => 0, :size => '375x375'), p.url_photopage) %>
      </li>
    <% end %>
  <% end %>
  </ul>

Flickr id 就呈现在 URL 的用户配置里。以 138578671@N04 这个 ID 为例,如果你在表单里提交了有效值,就会返回一系列照片。

现在我们有了一个从 Flickr 获取新照片的应用。这很棒,但是对用户来说这还很慢,而且每次搜需要刷新整个页面。

我认为加上一点 AJAX 这个应用会更完善,在 app/views/search 创建 index.js.erb 视图,并添加一些 Javascript 的内容:

$('ul').remove();
$('#flickr').append("<%= j render 'photos'%>").html_safe();

控制器当中,要保证我们对代码阻塞有 响应

class SearchController < ApplicationController
  def index
    if params[:flickr_id]
      @photos = user_photos(params[:flickr_id],10).in_groups_of(2)
      @id = params[:flickr_id]
    end
    respond_to |format|
      format.html
      format.js
    end
  end
end

最后,每个搜索表单中,设置 remote 为 ture:

<br/>
  <%= form_tag("/", method: "get", :remote => true) do %>
  <%= text_field_tag(:flickr_id) %>
  <%= submit_tag("Search") %> <% end %>
<br/>

好,这很酷,但是我们还没有用到 Shroyuken 。进程还是单线程的。

配置 Shoryuken

如果你还没有 Amazon Web Services (AWS) 账户,你需要创建一个。按照:

  1. 点击“我的账户”下拉菜单,然后单击“AWS 控制台”。
  2. 登录之后就进入了控制台。
  3. 在右上方,单击菜单栏中的用户名,然后单击“安全验证”。
  4. 现在你被带到一个页面里,你可以获取访问密钥的 id 和密码。
  5. 点击“创建新的访问密钥”得到你的访问密钥 ID 和密码。你需要这些来运行 Shoryuken 和 SQS。

这样我们有了 AWS 的访问密钥,下一步就是安装和配置相关的 gem 。添加如下代码到你的 Gemfile里来安装带有相关细节的 AWS SDK 。

gem 'aws-sdk', '~> 2'

之后,bundle install。

我们需要利用相关证书配置 AWS SDK。完成通常创建一个叫aws.rb 的文件,放在 config/initializers 文件夹里面。

touch config/initializers/aws.rb

在文件中添加以下代码:

Aws.config.update({ 
  region:      "eu-west-1",
  credentials: Aws::Credentials.new(your_access_key, your_secret_key)
})

sqs = Aws::SQS::Client.new(
  region:      "eu-west-1",
  credentials: Aws::Credentials.new(your_access_key, your_secret_key)
)
sqs.create_queue({queue_name: 'default'})

确保用你的实际证书替代原证书

如果我们查看 SQS 控制台,会发现重启 Rails 服务器之后会出现新的队列。

SQS QUEUE

最后,到了安装 Shoryuken gem 的时候了。在我们的 Gemfile 里:

gem 'shoryuken'

创建 Shoryuken worker 和其他中间件。我只是创建了在 apps 下创建了一个新的名叫 workers 的目录:

mkdir app/workers
touch app/workers/flickr_worker.rb
touch app/workers/flickr_middleware.rb
touch config/shoryuken.yml

配置 Flickr 中间件:

class FlickrMiddleware
  def call(worker_instance, queue, sqs_msg, body)
    puts 'Before work'
    yield
    puts 'After work'
  end
end

配置 worker :

class MyWorker
  include Shoryuken::Worker
  shoryuken_options queue: QUEUE_NAME, auto_delete: true, body_parser: JSON

  def perform(sqs_msg, body)
    id = body.fetch('id')
    flickr = Flickr.new({
      key:"your_key",
      secret:"your_secret",
      token_cache:"token_cache.yml"
    })
    flickr.photos.search(:user_id => id).values_at(0..(5 - 1))
  end
 end

同时也需要按如下方式配置我们的 config/shoryuken.yml 文件:


aws:
  access_key_id: 'AWS_KEY'
  receive_message:
    attribute_names:
    - ApproximateReceiveCount
    - SentTimestamp
  region: eu-west-1
  secret_access_key: 'AWS Secret Key'
  concurrency: 25
  delay: 0
  queues:
    - [default, 6]

完美! 我们差不多配置好了所有的东西准备开始了。只剩下了给队列发送消息了。在搜索控制器上,写入如下代码:

“`
class SearchController < ApplicationController
include SearchHelper

def index
  # 138578671@N04 submit this in the form
  if params[:flickr_id]
    FlickrWorker.perform_async("id" => params[:flickr_id])
    sleep 0.1