You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rails应用WebSocket连接建立后直接终止问题求助

Rails WebSocket连接异常终止问题

在Rails应用中遇到WebSocket连接问题:通过Postman建立连接后,连接无错误提示直接终止,nginx.error.log与生产日志均未记录错误信息。怀疑问题出在Nginx配置,Redis服务运行正常。

Nginx配置

upstream puma_smarthome {
server unix:///home/rails/apps/smarthome/shared/tmp/sockets/smarthome-puma.sock;
}

server {
server_name smarthome.this-co.de www.smarthome.this-co.de;

root /home/rails/apps/smarthome/current/public;
access_log /home/rails/apps/smarthome/current/log/nginx.access.log;
error_log /home/rails/apps/smarthome/current/log/nginx.error.log info;

location ^~ /assets/ {
    gzip_static on;
    expires max;
    add_header Cache-Control public;
}

try_files $uri @puma_smarthome;

location @puma_smarthome {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Port $server_port;
    proxy_redirect off;
    proxy_pass http://puma_smarthome;
}

location /cable {
    #proxy_pass http://89.58.12.188:80;
    proxy_pass http://puma_smarthome;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    #proxy_redirect off;
    #proxy_set_header X-Real-IP $remote_addr;
    #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $host;
}

#if ($Scheme = http) {
#     return 301 https://$server_name$request_uri;
#}
error_page 500 502 503 504 /500.html;
client_max_body_size 100M;
keepalive_timeout 10;
listen 80;
listen 443 ssl; # managed by Certbot
ssl_certificate /etc/letsencrypt/live/smarthome.this-co.de/fullchain.pem; # managed by Certbot
ssl_certificate_key /etc/letsencrypt/live/smarthome.this-co.de/privkey.pem; # managed by Certbot
include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

生产日志

I, [2023-08-16T21:06:20.550633 #1207931]  INFO -- : [d53bce34-cbb6-4491-abb7-2e76e8af39e1] Started GET "/cable?token=[FILTERED]" for 127.0.0.1 at 2023-08-16      21:06:20 +0200
I, [2023-08-16T21:06:20.555670 #1207931]  INFO -- : [d53bce34-cbb6-4491-abb7-2e76e8af39e1] Started GET "/cable?token=[FILTERED]" [WebSocket] for 127.0.0.1 at 2023-08-16 21:06:20 +0200
I, [2023-08-16T21:06:20.555745 #1207931]  INFO -- : [d53bce34-cbb6-4491-abb7-2e76e8af39e1] Successfully upgraded to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: upgrade, HTTP_UPGRADE: websocket)
I, [2023-08-16T21:06:20.596028 #1207931]  INFO -- : Registered connection (Z2lkOi8vc21hcnRob21lL1N5c3RlbS80)

Postman日志

Disconnected from ws://smarthome.this-co.de/cable?token=token
21:06:20
1006 Abnormal Closure:No close frame was received.
{"type":"welcome"}    21:06:20
Connected to ws://smarthome.this-co.de/cable?token=token

puma.rb配置

# Puma can serve each request in a thread from an internal thread pool.
# The `threads` method setting takes two numbers: a minimum and maximum.
# Any libraries that use thread pools should be configured to match
# the maximum value specified for Puma. Default is set to 5 threads for minimum
# and maximum; this matches the default thread size of Active Record.
#
max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
threads min_threads_count, max_threads_count
#bind 'tcp://172.21.12.76:3000'
# Specifies the `worker_timeout` threshold that Puma will use to wait before
# terminating a worker in development environments.
worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development"

# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
workers 2
daemonize true if ENV.fetch("RAILS_ENV", "development") == "production"
port ENV.fetch("PORT") { 3000 }

# Specifies the `environment` that Puma will run in.

environment ENV.fetch("RAILS_ENV") { "production" }

# Specifies the `pidfile` that Puma will use.
pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }

# Specifies the number of `workers` to boot in clustered mode.
# Workers are forked web server processes. If using threads and workers together
# the concurrency of the application would be max `threads` * `workers`.
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
# workers ENV.fetch("WEB_CONCURRENCY") { 2 }

# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
# before forking the application. This takes advantage of Copy On Write
# process behavior so workers use less memory.
#
# preload_app!

# Allow puma to be restarted by `bin/rails restart` command.
plugin :tmp_restart

问题排查与解决思路

从日志来看,Rails已成功完成WebSocket升级并注册连接,但Postman很快收到1006异常关闭,核心问题大概率是Nginx连接超时配置不足、WebSocket头部配置错误,或Puma多worker模式下的状态共享问题。

1. 修复Nginx的WebSocket头部与超时配置

在location /cable块中更新配置:

proxy_pass http://puma_smarthome;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade"; # 修正HTML转义的引号问题
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 延长长连接超时时间,避免Nginx主动断开
proxy_connect_timeout 7d;
proxy_send_timeout 7d;
proxy_read_timeout 7d;

2. 启用Puma的preload_app!

由于使用了2个Puma worker,未启用preload_app!会导致Action Cable连接在worker间无法共享状态,需在puma.rb中取消注释:

preload_app!

3. 启用HTTP到HTTPS的跳转

当前Nginx中HTTP转HTTPS的规则被注释,若实际使用HTTPS访问(wss://),但Postman用ws://会引发跳转冲突,建议启用规则:

if ($scheme = http) {
     return 301 https://$server_name$request_uri;
}

4. 验证配置并重启服务

修改后执行以下命令:

# 验证Nginx配置正确性
nginx -t
# 重启Nginx
systemctl restart nginx
# 重启Puma
bundle exec pumactl restart

内容的提问来源于stack exchange,提问作者Seppello

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 02:45:00