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
相关产品推荐
相关产品推荐

