【问题标题】:Concurrent requests to ruby REST API not possible对 ruby​​ REST API 的并发请求是不可能的
【发布时间】:2015-11-20 19:31:14
【问题描述】:

我正在尝试使用 ruby 创建一些小型 REST API,并在 thin 服务器上运行 Sinatra gem。关键是要了解构建这种由微 Web 服务组成的 REST API 的难易程度,并将其与亚马逊 AWS 上可用的其他编程语言/技术进行比较。我已经很容易地创建了一个,这是代码(只是最小的工作项目,尚未考虑任何类型的优化):

require 'sinatra'
require 'mysql'
require 'json'

set :environment, :development

db_host = 'HOST_URL'
db_user = 'USER'
db_pass = 'PASSWORD'
db_name = 'DB_NAME'
db_enc  = 'utf8'

select = 'SELECT * FROM table1 LIMIT 30'

db = Mysql.init
db.options Mysql::SET_CHARSET_NAME, db_enc
db = db.real_connect db_host, db_user, db_pass, db_name

get '/brands' do
    rs = db.query select
    #db.close
    result = []
    rs.each_hash do |row|
        result.push row
    end
    result.to_json
end

使用ruby my_ws.rb 运行它会启动 Sinatrathin 上运行,没问题

在我的终端上使用curl,如curl --get localhost:4567/brands,返回所需的JSON响应也不是问题。

我现在已经解决了几个小时的真正问题(当然在 Google 上搜索,在 SO 上也阅读了大量资源)是当我尝试使用 围攻与更多并发用户:

sudo 围攻 -b http://localhost:4567/brands -c2 -r2

这应该在 benchnark 模式下运行,发出 2 个并发请求(-c2 开关)2 次(-r2 开关)。在这种情况下,我总是在控制台中收到一条错误消息,指出 Mysql::ProtocolError - invalid packet: sequence number mismatch(102 != 2(expected)):,而每次运行的数字 102 总是不同的。如果我只为一个用户运行基准测试(一个并发请求,即根本没有并发),我甚至可以运行 1000 次而没有错误 (sudo siege -b http://localhost:4567/brands -c1 -r1000)。

我尝试在我的代码中添加手动线程,例如:

get '/brands' do
    th = Thread.new do
        rs = db.query select
        #db.close
        result = []
        rs.each_hash do |row|
            result.push row
        end
        result.to_json
    end
    th.join
    th.value
end

但没有任何帮助。

根据我的发现:

  • Sinatra 默认是多线程的
  • 如果从 Sinatra 运行,如果由 ruby script.rb 运行,thin 也是多线程的
  • 多线程似乎对 DB 查询没有影响 - 这里看起来不可能有并发

我正在使用ruby-mysql gem,因为我发现它比(只是)mysql gem 更新,但最终不知道使用哪一个(找到旧文章使用 mysql 和其他一些请改用ruby-mysql)。

知道如何对我的 REST API 运行并发请求吗?我需要对其进行基准测试并将其与其他语言(PHP、Python、Scala 等)进行比较。

【问题讨论】:

  • 我会尝试不同的 mysql 适配器,也许它不是线程安全的。你使用什么 ruby​​ 运行时?
  • 应该是最新的,ruby -v 给我ruby 2.1.2p95 (2014-05-08) [x86_64-linux-gnu]
  • 我应该尝试哪些其他适配器?纯mysql 在设置字符集和编码转换方面存在问题。这就是我用 2.9.13 版本中的 ruby-mysql 替换它的原因。
  • 我一直使用mysql2,这是一个非常活跃的项目。不过有些东西不见了(比如没有准备语句)
  • 是的,直接看GH上的mysql2...我试试看。

标签: mysql ruby concurrency sinatra


【解决方案1】:

通过两次修复解决了这个问题。

第一个是将mysql适配器替换为mysql2

第二个是问题的真正原因:在我什至可以潜入线程之前(即在执行路由代码之前)为运行时创建了一次 MySQL 连接,从而导致(逻辑上)连接锁。

现在mysql2 和到 DB 的连接在路由执行下移动,即使对于 250 个并发请求,它也能正常工作!最终代码:

require 'sinatra'
require 'mysql2'
require 'json'

set :environment, :development

db_host = 'HOST_URL'
db_user = 'USER'
db_pass = 'PASSWORD'
db_name = 'DB_NAME'
db_enc  = 'utf8'

select = 'SELECT * FROM table1 LIMIT 30'

get '/brands' do
    result = []
    Mysql2::Client.new(:host => db_host, :username => db_user, :password => db_pass, :database => db_name, :encoding => db_enc).query(select).each do |row|
      result.push row
    end
    result.to_json
end

运行 sudo siege -b http://localhost:4567/brands -c250 -r4 现在给我:

Transactions:             1000 hits
Availability:             100.00 %
Elapsed time:             1.54 secs
Data transferred:         2.40 MB
Response time:            0.27 secs
Transaction rate:         649.35 trans/sec
Throughput:               1.56 MB/sec
Concurrency:              175.15
Successful transactions:  1000
Failed transactions:      0
Longest transaction:      1.23
Shortest transaction:     0.03

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-06-25
    • 2012-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-30
    • 1970-01-01
    • 2021-08-11
    相关资源
    最近更新 更多