我正在尝试使用 ruby 和在 thin 服务器上运行的 Sinatra gem 创建一些小型 REST API。重点是了解构建由微型 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 在 thin 上运行,没问题。
从我的终端使用 curl
,例如 curl --get localhost:4567/brands
返回所需的 JSON 响应也不是问题。
我现在已经处理了几个小时的真正问题(当然是在 Google 上搜索,也在 SO 上阅读了大量资源)是当我尝试使用基准微型 WS 时>围攻更多并发用户:
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
gem,因为我发现它比(只是)mysql
gem 更新,但最后不知道要使用哪个(发现旧文章使用 mysql
和其他一些使用 ruby-mysql
代替)。
知道如何对我的 REST API 运行并发请求吗?我需要对其进行基准测试并将其与其他语言(PHP、Python、Scala 等)进行比较。
最佳答案
通过两个修复程序解决了该问题。
第一个是用 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
关于mysql - 无法并发请求 ruby REST API,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33823618/