使用Sinatra模块化样式为每个实例而不是每个请求创建一个变量

时间:2014-01-13 18:27:00

标签: ruby heroku redis sinatra connection-pooling

我有一个以模块化风格编写的Sinatra应用程序,在Heroku上运行。它使用Redis,我的Redis连接数量有限(10)。我发现它经常会抛出错误,抱怨它已经用完了Redis连接。所以我开始使用connection_pool希望能解决问题;一个Redis连接池,应用程序每次都会选择其中一个,而不是尝试在每个请求上创建一个新连接。

但我仍然遇到同样的问题。我可以在单个查询中执行大量Redis查询而无需投诉。但是,如果我重新加载一个测试页面,它只是做了一些Redis查询,几次相当快速的连续,我得到“Redis :: CommandError - ERR最大客户端数量达到”错误。

所以我假设,也许,它在每个请求上创建一个新的connection_pool实例......我不知道。但它并没有像我期望的那样“汇集”。

我有这样的事情:

# myapp.rb
$LOAD_PATH.unshift(File.dirname(__FILE__))
$stdout.sync = true

require 'thin'
require 'myapp/frontend'

MyApp::Frontend.run!

Sinatra app:

# myapp/frontend.rb
require 'sinatra/base'
require 'redis'
require 'connection_pool'
require 'uuid'

module MyApp
  class Frontend < Sinatra::Base

    helpers do
      def redis_pool
        @redis_pool ||= ConnectionPool.new(:size => 8, :timeout => 5) do
          redis_uri = URI.parse(ENV['REDISCLOUD_URL'])
          client = ::Redis.new(:host => redis_uri.host,
                               :port => redis_uri.port,
                               :password => redis_uri.password)
        end
      end
    end

    get '/tester/'
      redis_pool.with do |r|
        id = UUID.generate
        r.hset(:user, id, "Some data")
        r.hget(:user, id)
        r.hdel(:user, id)
      end
      p "DONE"
    end
  end
end

Procfile看起来像:

web: ruby myapp.rb

有什么想法吗?当前站点的流量非常低,所以这应该是可能的。

1 个答案:

答案 0 :(得分:1)

每次处理@redis_pool的get请求时都会创建一个/tester/的新实例,因为每次都会调用帮助方法redis_pool

您可以使用sinatra的设置帮助程序仅初始化一次redis连接:

config do
  redis_uri = URI.parse(ENV['REDISCLOUD_URL'])
  set :redis, Redis.new(:host => redis_uri.host,
                        :port => redis_uri.port,
                        :password => redis_uri.password)
end

现在,应用的每个实例都有一个redis连接,该连接会持续存储所有请求。像这样访问设置

get '/tester/'
  id = UUID.generate
  settings.redis.hset(:user, id, "some data")
  settings.redis.hget(:user, id)
  settings.redis.hdel(:user, id)
  p "DONE"
end