如何使用 Sinatra、Sequel 和 Rack 设置基于会话的数据库连接

How to setup session based database connections with Sinatra, Sequel and Rack

我在 Puma / Rack 服务器中使用 Sequel 和 Sinatra。

我希望将我的 Sequel 数据库连接对象与会话一起存储 - 而不是全局 - 这样我就可以为每个登录用户提供一个单独的数据库连接池。目的是让每个 Web 服务器登录都登录一个数据库服务器。

我不知道该怎么做,因为 Sequel 数据库对象似乎是全局单例。例如,如果我尝试序列化数据库对象并存储在会话中,我将收到一条错误消息:TypeError - can't dump anonymous class: 我不想在每次路由请求时都连接到数据库。

我该怎么做?下面是一些示例代码,希望能表明我正在努力实现的目标:

require 'sinatra/base'
require 'pp'
require 'sequel'
require 'json'
require 'java'            
require 'sqljdbc4.jar'
require 'yaml'

class MyApp < Sinatra::Base

  set :sessions, true

  use Rack::Session::Cookie, :key => 'rack.session',
      :expire_after => 2592000,
      :secret => 'sydasx'

  get '/' do
    db = session[:db]
    DB = YAML::Load(db)

    response = ''
    DB['select * from SEC_USER'].each do |row|
      response += row.to_s
    end

    response.to_json
  end

  get '/login/:username' do

    username = params['username']

    puts "username: #{username}"

    conn_str = "jdbc:sqlserver://localhost:1434;databaseName=#{username};integratedSecurity=true;"    
    DB = Sequel.connect(conn_str)
    puts "DB: #{DB.pretty_inspect}"
    db = YAML::dump(DB)
    puts "db: #{db}"

    session[:db] = db

    "logged in"
  end

end

您不能序列化 Sequel::Database 对象。你有几个不错的选择:

  1. 使用机架中间件为每个请求创建一个 Sequel::Database 对象,只为请求使用该对象。在这种情况下,您不会将 Sequel.connect 的结果分配给常量,您会传递一个块并调用该块内的下一个变量。

  2. 在顶层创建单个 Sequel::Database 对象并将其存储在数据库常量中。 arbitrary_servers 和 server_block 扩展到 Sequel::Database 对象中。然后使用机架中间件在块的持续时间内检查到适当服务器的连接。

  3. 如果您的客户很少,可以只使用 Sequel 的分片支持,只使用 server_block 扩展而不使用 arbitrary_servers。这样做的一个好处是可以缓存连接,这样您就不会为每个请求建立单独的数据库连接(1. 和 2. 都是这种情况)。

  4. 使用您提到的全局哈希,键是用户名,值是 Sequel::Database 对象。如果你这样做,你需要确保你有足够的内存来存储你想要跟踪的所有对象。