Ruby WebSocket和EventMachine超时和错误恢复
使用puma、faye websocket ruby和eventmachine,我试图实现一个websocket服务器,该服务器被扩展以支持使用redis.rb的频道。每个客户机将使用当前正在开发的路径提供一个通道:“/C#{random number}”。所有这些逻辑都需要驻留在服务器中,因为客户端将是基于微处理器的Python系统,不支持更高级别的库 作为起点,我的代码基于。一个主要的变化是将其配置为在WebSocket“打开”期间支持多个通道 代码在正常运行时工作。但是,通常当一个客户端断开时,服务器会挂起,直到重新启动。我正在努力解决这个问题,但到目前为止还未能解决。最初,Heroku会抛出一个H12超时。我已经实现了机架超时。我尝试过在服务器内挽救超时,但这些超时从未触发。我在服务器中实现了一个“on error”事件,但它从未触发。最常见的情况是,服务器在重新启动之前一直处于关闭状态。客户端应该自己保护自己,但我需要服务器恢复并继续 config.ru:Ruby WebSocket和EventMachine超时和错误恢复,ruby,heroku,websocket,eventmachine,faye,Ruby,Heroku,Websocket,Eventmachine,Faye,使用puma、faye websocket ruby和eventmachine,我试图实现一个websocket服务器,该服务器被扩展以支持使用redis.rb的频道。每个客户机将使用当前正在开发的路径提供一个通道:“/C#{random number}”。所有这些逻辑都需要驻留在服务器中,因为客户端将是基于微处理器的Python系统,不支持更高级别的库 作为起点,我的代码基于。一个主要的变化是将其配置为在WebSocket“打开”期间支持多个通道 代码在正常运行时工作。但是,通常当一个客户端断
require './app'
require './middlewares/myserver_backend'
require 'rack-timeout'
use Rack::Timeout, service_timeout: 20, wait_timeout: 30, wait_overtime: 60, service_past_wait: false
use Myserver::MyserverBackend
run Myserver::App
机架中间件“后端”:
Gemfile.lock文件:
GEM
remote: https://rubygems.org/
specs:
activesupport (4.2.5.1)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
eventmachine (1.2.0.1-x86-mingw32)
faye-websocket (0.10.4)
eventmachine (>= 0.12.0)
websocket-driver (>= 0.5.1)
i18n (0.7.0)
json (1.8.3)
json_pure (1.8.3)
minitest (5.9.0)
multi_json (1.12.1)
oj (2.16.1)
permessage_deflate (0.1.3)
progressbar (0.21.0)
puma (3.4.0)
rack (1.6.4)
rack-protection (1.5.3)
rack
rack-timeout (0.4.2)
rake (11.2.2)
redis (3.3.0)
rollbar (2.11.5)
multi_json
sinatra (1.4.7)
rack (~> 1.5)
rack-protection (~> 1.4)
tilt (>= 1.3, < 3)
thread_safe (0.3.5)
tilt (2.0.5)
tzinfo (1.2.2)
thread_safe (~> 0.1)
websocket-driver (0.6.4)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
PLATFORMS
x86-mingw32
DEPENDENCIES
activesupport (= 4.2.5.1)
bundler
faye-websocket
json_pure
oj (~> 2.16.0)
permessage_deflate
progressbar
puma
rack
rack-timeout
rake
redis (>= 3.2.0)
rollbar
sinatra
RUBY VERSION
ruby 2.2.4p230
BUNDLED WITH
1.12.5
config/puma.rb:
env = ENV['OS'] == 'Windows_NT' ? 'development' : ENV['RACK_ENV']
if env.nil? || env == 'development' || env == 'test'
concurrency = 0 # Set to zero to ensure single mode, not clustered mode
max_threads = 1
end
# WEB_CONCURRENCY and RAILS_MAX_THREADS == 1 in Heroku for now.
concurrency ||= (ENV['WEB_CONCURRENCY'] || 2)
max_threads ||= (ENV['RAILS_MAX_THREADS'] || 5)
worker_timeout 15
workers Integer(concurrency)
threads_count = Integer(max_threads)
threads threads_count, threads_count
#preload_app!
rackup DefaultRackup
port ENV['PORT'] || 3000
environment ENV['RACK_ENV'] || 'development'
我需要做的是完成服务器的“关闭”事件。它需要清理一切,然后重新启动自己,但它没有这样做 然而,我不喜欢这个作为最终答案。问题是,为什么服务器会关闭商店,仅仅因为客户机被丢弃而终止并重新启动?难道没有更干净的方法来清除失败客户的碎屑吗?跟进:此修复确实回答了这个特定的问题,在任何情况下,完成onclose都解决了所述的问题。除了Redis事件之外,还进一步增强了客户端WebSocket事件的线程化,例如onclose只关闭客户端而不关闭服务器 新活动是:
ws.on :close do |event|
if @debug
puts "MyserverBackend>> Close entered. Last error:#{$!.class}:#{$!.to_s};Module:#{$0};Line:#{$.};"
$@.each { |backtrace| puts backtrace }
exit
end
@clients.each do |clients_ws, clients_channel|
begin
@redis.unsubscribe(clients_channel)
rescue RuntimeError => exception
unless exception.to_s == "Can't unsubscribe if not subscribed."
raise
end
false
end
end
@clients.delete_if { |clients_ws, clients_channel| clients_ws == ws }
channel = URI.parse(event.target.url).path[1..URI.parse(event.target.url).path.length]
puts "MyserverBackend>> Websocket closure for:#{channel}; Event code:#{event.code} Event reason:#{event.reason};"
ws = nil
app = Myserver::App
myserver = MyserverBackend.new(app)
myserver
end
GEM
remote: https://rubygems.org/
specs:
activesupport (4.2.5.1)
i18n (~> 0.7)
json (~> 1.7, >= 1.7.7)
minitest (~> 5.1)
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
eventmachine (1.2.0.1-x86-mingw32)
faye-websocket (0.10.4)
eventmachine (>= 0.12.0)
websocket-driver (>= 0.5.1)
i18n (0.7.0)
json (1.8.3)
json_pure (1.8.3)
minitest (5.9.0)
multi_json (1.12.1)
oj (2.16.1)
permessage_deflate (0.1.3)
progressbar (0.21.0)
puma (3.4.0)
rack (1.6.4)
rack-protection (1.5.3)
rack
rack-timeout (0.4.2)
rake (11.2.2)
redis (3.3.0)
rollbar (2.11.5)
multi_json
sinatra (1.4.7)
rack (~> 1.5)
rack-protection (~> 1.4)
tilt (>= 1.3, < 3)
thread_safe (0.3.5)
tilt (2.0.5)
tzinfo (1.2.2)
thread_safe (~> 0.1)
websocket-driver (0.6.4)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
PLATFORMS
x86-mingw32
DEPENDENCIES
activesupport (= 4.2.5.1)
bundler
faye-websocket
json_pure
oj (~> 2.16.0)
permessage_deflate
progressbar
puma
rack
rack-timeout
rake
redis (>= 3.2.0)
rollbar
sinatra
RUBY VERSION
ruby 2.2.4p230
BUNDLED WITH
1.12.5
ruby client.rb
20.098119 seconds;
[:close, 1002, "Error during WebSocket handshake: Unexpected response code: 500"]
20.07921 seconds;
[:close, 1002, "Error during WebSocket handshake: Unexpected response code: 500"]
20.075731 seconds;
[:close, 1002, "Error during WebSocket handshake: Unexpected response code: 500"]
env = ENV['OS'] == 'Windows_NT' ? 'development' : ENV['RACK_ENV']
if env.nil? || env == 'development' || env == 'test'
concurrency = 0 # Set to zero to ensure single mode, not clustered mode
max_threads = 1
end
# WEB_CONCURRENCY and RAILS_MAX_THREADS == 1 in Heroku for now.
concurrency ||= (ENV['WEB_CONCURRENCY'] || 2)
max_threads ||= (ENV['RAILS_MAX_THREADS'] || 5)
worker_timeout 15
workers Integer(concurrency)
threads_count = Integer(max_threads)
threads threads_count, threads_count
#preload_app!
rackup DefaultRackup
port ENV['PORT'] || 3000
environment ENV['RACK_ENV'] || 'development'
ws.on :close do |event|
if @debug
puts "MyserverBackend>> Close entered. Last error:#{$!.class}:#{$!.to_s};Module:#{$0};Line:#{$.};"
$@.each { |backtrace| puts backtrace }
exit
end
@clients.each do |clients_ws, clients_channel|
begin
@redis.unsubscribe(clients_channel)
rescue RuntimeError => exception
unless exception.to_s == "Can't unsubscribe if not subscribed."
raise
end
false
end
end
@clients.delete_if { |clients_ws, clients_channel| clients_ws == ws }
channel = URI.parse(event.target.url).path[1..URI.parse(event.target.url).path.length]
puts "MyserverBackend>> Websocket closure for:#{channel}; Event code:#{event.code} Event reason:#{event.reason};"
ws = nil
app = Myserver::App
myserver = MyserverBackend.new(app)
myserver
end