无法同时请求ruby REST API

时间:2015-11-20 09:51:09

标签: mysql ruby concurrency sinatra

我正在尝试使用 ruby​​ 创建一些小型REST API,并在服务器上运行 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运行此操作会在上运行 Sinatra 没问题

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

我现在已经解决了几个小时的真正问题(当然,在Google上搜索,在SO上也可以阅读大量资源)就是当我尝试使用 微型WS时> Siege 拥有更多并发用户:

  

sudo siege -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默认是多线程的
  • 如果从ruby script.rb 运行,则从Sinatra运行时,
  • thin也是多线程的
  • 多线程似乎对数据库查询没有影响 - 这里似乎没有可能的并发性

我正在使用ruby-mysql宝石,因为我发现它比那些(只是)mysql宝石更新,但最终还是不知道使用哪一块(找到旧的宝石)要使用mysql的文章和其他使用ruby-mysql的文章。

关于如何对我的REST API运行并发请求的任何想法?我需要对其进行基准测试并与其他语言(PHP,Python,Scala,...)进行比较。

1 个答案:

答案 0 :(得分:1)

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

第一个是用mysql2替换mysql适配器。

第二个是问题的真正原因:在我甚至可以潜入线程(即在执行路由的代码之前)导致(逻辑上)连接锁之前,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