jruby 9.3.6(因此为ruby 2.6.8(,rails 6.1.6.1。在生产中使用ssl(wss(与design、puma、nginx。
本地actioncable运行没有问题,但在外部服务器上actioncable建立了Websocket连接,导致nginx:GET/cable HTTP/1.1";101 210,用户从cookie中的扩展session_id得到正确验证;成功升级到WebSocket";,浏览器接收{"类型":"欢迎"}和ping。
因此,actioncable javascript发送了一个订阅频道的请求,但没有成功。
我尝试了很多关于nginx的配置,例如在nginx中切换到乘客的actioncable位置/电缆,5天后我甚至从简单的";新Websocket";在javascript中,客户端实现了actioncable-as(使用import-maps和actioncable.esm.js(,但它并没有解决主要问题。
日志文件:
production.log:
[ActionCable connect in app/channels/application_cable/connection.rb:] WebSocket error occurred: Broken pipe -
如果domain.com被调用一次,则每0.2秒就会发生一次此错误。如果页面已关闭,错误将继续。0.2秒与浏览器发送订阅频道请求的频率相同。我假设日志时间与ssl有关,但我没有发现问题。所以现在,我假设";"断管";是服务器端jruby应用程序和actioncable之间的问题。但我不确定,实际上我也不知道,如何解决那里的故障。
另外,puma.stderr.log中还有一条警告:
warning: thread "Ruby-0-Thread-27:
/home/my_app/.rbenv/versions/jruby-9.3.6.0/lib/ruby/gems/shared/gems/actioncable-6.1.6.1/lib/action_cable/connection/stream_event_loop.rb:75" terminated with exception (report_on_exception is true):
ArgumentError: mode not supported for this object: r
启动redis cli并执行"monitor":
1660711192.522723 [1 127.0.0.1:33630] "select" "1"
1660711192.523545 [1 127.0.0.1:33630] "client" "setname" "ActionCable-PID-199512"
发布到消息Channel_1作品:
1660711192.523831 [1 127.0.0.1:33630] "publish" "messages_1" "{"message":"message-text"}"
与本地开发配置相比,这看起来有所不同:
1660712957.712189 [1 127.0.0.1:46954] "select" "1"
1660712957.712871 [1 127.0.0.1:46954] "client" "setname" "ActionCable-PID-18600"
1660712957.713495 [1 127.0.0.1:46954] "subscribe" "_action_cable_internal"
1660712957.716100 [1 127.0.0.1:46954] "subscribe" "messages_1"
1660712957.974486 [1 127.0.0.1:46952] "publish" "messages_3" "{"message":"message-text"}"
那么什么是"_action_cable_internal";,为什么不在生产中进行呢?我找到了actioncablegem的代码,并在gems/actioncable-6.1.6.1/lib/action_cable/server/base.rb中添加了"p@pubsub",并将该信息与本地配置进行了比较。
本地有一个信息:
@thread=#<Thread:0x5326bff6@/home/me_the_user/.rbenv/versions/jruby-9.2.16.0/lib/ruby/gems/shared/gems/actioncable-6.1.6.1/lib/action_cable/connection/stream_event_loop.rb:75 sleep>
对应于服务器上的信息:
@thread=#<Thread:0x5f4462e1@/home/me_the_user/.rbenv/versions/jruby-9.3.6.0/lib/ruby/gems/shared/gems/actioncable-6.1.6.1/lib/action_cable/connection/stream_event_loop.rb:75 dead>
所以它看起来像";警告";是一个"错误"。
此外,我不确定wscat/curl的输出是否正常或报告错误:
root@server:~# wscat -c wss://domain.tld
error: Unexpected server response: 302
由于缺少"/电缆",这可能是正常的。但是:
root@server:~# wscat -c wss://domain.tld/cable
error: Unexpected server response: 404
root@server:~# curl -I https://domain.tld/cable
HTTP/1.1 404 Not Found
配置:nginx.conf:
http {
upstream app {
# Path to Puma SOCK file, as defined previously
server unix:///var/www/my_app/shared/sockets/puma.sock fail_timeout=0;
}
server {
listen 443 ssl default_server;
server_name domain.com www.domain.com;
include snippets/ssl-my_app.com.conf;
include snippets/ssl-params.conf;
root /var/www/my_app/public;
try_files $uri /index.html /index.htm;
location /cable {
proxy_pass http://app;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Ssl on;
proxy_set_header X-Forwarded-Proto https;
}
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_redirect off;
add_header X-Frame-Options SAMEORIGIN always;
proxy_pass http://app;
}
rails_env production;
} }
cable.yml:
production:
adapter: redis
url: redis://127.0.0.1:6379/1
ssl_params:
verify_mode: <%= OpenSSL::SSL::VERIFY_NONE %>
initializer/redis.rb:
$redis = Redis.new(:host => 'domain.com/cable', :port => 6379)
routes.rb:
mount ActionCable.server => '/cable'
config/environments/production.rb:
config.force_ssl = true
config.action_cable.allowed_request_origins = [/http://*/, /https://*/]
config.action_cable.allow_same_origin_as_host = true
config.action_cable.url = "wss://domain.com/cable"
我认为这与ssl有关,但我不是配置专家,所以非常感谢您的帮助。
问题是由硬件引起的:我在法国使用一个所谓的"airbox",这是一个提供WLAN的移动互联网接入。因此,由于actioncable websocket证书不是",websocket连接总是关闭的;"已知";移动互联网将更加严格。
因此,我创建了一个伪websocket:如果websocket连接立即关闭,用户的浏览器每2.5秒就会询问一次,是否有通过websocket发送的内容。