【问题标题】:ActionCable - Failed to upgrade to WebSocket in productionActionCable - 无法在生产中升级到 WebSocket
【发布时间】:2017-04-09 23:11:10
【问题描述】:

ActionCable 在生产中不起作用。在开发中运行良好,但在生产中运行不佳。

在 Ubuntu 14.04 上使用 Puma 运行 Nginx。我已检查 redis-server 是否已启动并正在运行。

Rails -v 5.0.0.1

production.log:

INFO -- : Started GET "/cable/"[non-WebSocket] for 178.213.184.193 at 2016-11-25 14:55:39 +0100
ERROR -- : Failed to upgrade to WebSocket (REQUEST_METHOD: GET, HTTP_CONNECTION: close, HTTP_UPGRADE: )
INFO -- : Finished "/cable/"[non-WebSocket] for 178.213.184.193 at 2016-11-25 14:55:39 +0100

来自客户的请求:

GET ws://mityakoval.com/cable HTTP/1.1
Host: mityakoval.com
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://mityakoval.com
Sec-WebSocket-Version: 13
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.6,en;q=0.4,uk;q=0.2,nb;q=0.2
Cookie: _vaktdagboka_session=******
Sec-WebSocket-Key: *******
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Protocol: actioncable-v1-json, actioncable-unsupported

回复:

HTTP/1.1 404 Not Found
Server: nginx/1.4.6 (Ubuntu)
Date: Fri, 25 Nov 2016 13:52:21 GMT
Content-Type: text/plain
Transfer-Encoding: chunked
Connection: keep-alive
Cache-Control: no-cache
X-Request-Id: d6374238-69ef-476e-8fc5-e2f8bbb663de
X-Runtime: 0.002500

nginx.conf:

upstream puma {
  server unix:///home/mityakoval/apps/vaktdagboka/shared/tmp/sockets/vaktdagboka-puma.sock;
}

server {
  listen 80 default_server deferred;
  # server_name example.com;

  root /home/mityakoval/apps/vaktdagboka/current/public;
  access_log /home/mityakoval/apps/vaktdagboka/current/log/nginx.access.log;
  error_log /home/mityakoval/apps/vaktdagboka/current/log/nginx.error.log info;

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

  try_files $uri/index.html $uri @puma;
  location @puma {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect off;

    proxy_pass http://puma;
  }

  location /cable {
    proxy_pass http://puma;
    proxy_http_version 1.1;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "Upgrade";
  }

  error_page 500 502 503 504 /500.html;
  client_max_body_size 10M;
  keepalive_timeout 10;
}

cable.yml:

redis: &redis
  adapter: redis
  url: redis://127.0.0.1:6379

production: *redis

development:
  adapter: async

test:
  adapter: async

production.rb:

config.action_cable.allowed_request_origins = ["http://mityakoval.com"]

routes.rb:

mount ActionCable.server, at: '/cable'

更新:

别忘了重启 nginx :) 这就是我的问题。

【问题讨论】:

  • 你找到解决办法了吗?
  • @RameshKumarThiyagarajan 你重启了 nginx 吗?
  • 提醒其他人:如果您的 nginx 设置为多站点服务器,您的配置文件可能是 /etc/nginx/sites-enabled/sitename 而不是 /etc/nginx/网站可用/默认
  • 你是怎么解决这个问题的?
  • @KrupaSuthar 抱歉回复晚了。就我而言,我只需要重新启动 nginx。

标签: ruby-on-rails ruby nginx ruby-on-rails-5 actioncable


【解决方案1】:

您应该将proxy_pass 属性的值从http://puma 更改为http://puma/cable

因此,/cable 的正确 location 部分将是:

location /cable {
  proxy_pass http://puma/cable;
  proxy_http_version 1.1;
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  proxy_set_header Host $http_host;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "Upgrade";
}

【讨论】:

  • 这也是我的问题。
【解决方案2】:

虽然其他帖子已经正确发布了解决方案,但我想我会发布更多关于如何确定问题出在哪里/在哪里为其他 nginx 新手解决问题的信息。

如果 rails 错误包含 HTTP_UPGRADE:,您将知道您的 nginx 配置在您安装动作电缆的路径上需要 proxy_set_header Upgrade。 (意味着没有任何东西传递给 HTTP_UPGRADE)。解决问题后,我的日志显示HTTP_UPGRADE: websocket

  • Gotchya 1:正如操作所提到的,请确保在进行更改后重新启动 nginx(我这样做不正确)。

  • Gotchya 2:还要在 nginx 配置中查找包含语句,因为您的配置可能会拆分到多个文件中。 location /cable { 部分应该在 server { 内部,在我的情况下它是缺失的,因为它与包含语句不同的配置文件,我有一段时间没有注意到。

  • 类似的错误但不同的问题:您的 rails 日志将在 OP 提到的错误之前的日志中包含一个额外的错误,即当您的 rails 配置需要更新为另一个答案时提到更新 config.action_cable.allowed_request_origins。

日志可能会随着 rails 的变化而变化,但希望这有助于澄清问题出在哪里,以及我作为一个对 nginx 一无所知的人遇到的一些问题。

【讨论】:

  • 感谢在我的情况下有很多 server { 块,我已将其放置在端口 443 上列出的一个,它开始工作。
【解决方案3】:

但是,对于使用 Rails5、Action Cable 等和 DEVISE 遇到相同错误消息的任何人来说,这次对话已经很晚了,您只需按照建议的here 解决它。这一切都归结为 Web 套接字服务器没有会话,因此出现错误消息。

app/channels/application_cable/connection.rb

module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
      logger.add_tags 'ActionCable', current_user.name
    end

    protected
      def find_verified_user
        verified_user = User.find_by(id: cookies.signed['user.id'])
        if verified_user && cookies.signed['user.expires_at'] > Time.now
          verified_user
        else
          reject_unauthorized_connection
        end
      end
  end
end

app/config/initializers/warden_hooks.rb

Warden::Manager.after_set_user do |user,auth,opts|
  scope = opts[:scope]
  auth.cookies.signed["#{scope}.id"] = user.id
  auth.cookies.signed["#{scope}.expires_at"] = 30.minutes.from_now
end
Warden::Manager.before_logout do |user, auth, opts|
  scope = opts[:scope]
  auth.cookies.signed["#{scope}.id"] = nil
  auth.cookies.signed["#{scope}.expires_at"] = nil
end

解决方案由 Greg Molnar 开发

【讨论】:

  • 您可以只使用 env['warden'].user 来检索用户对象 :)
  • 确实如此。 @desheikh
  • 客户端是 Safari 时好像没有设置 cookie。有什么想法吗?
  • @Micah,我在 safari 上遇到了类似的问题,您是否设法找到有关它的更多信息?
【解决方案4】:

需要更改 NGINX 配置以接受此操作电缆请求的分辨率

location / {
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
}

将以上行添加到 nginx 站点配置中的 location 块中,然后重新启动 nginx。

【讨论】:

    【解决方案5】:

    我的解决方案是将这些行添加到我的 production.rb 文件中:

      config.action_cable.url = 'ws://your_site.com/your_action_cable'
      config.action_cable.allowed_request_origins = [ 'http://your_site.com' ]
    

    【讨论】:

      【解决方案6】:

      您可以更改有关/cable 的 nginx 配置

      proxy_set_header X-Forwarded-Proto http;

      我使用了你的 nginx 配置并在 myu 服务器上添加了这个更改,它工作正常。

      【讨论】:

        【解决方案7】:

        合作过:

        location ^~ /cable {
          ...
        }
        

        位置需要^~

        【讨论】:

          【解决方案8】:

          您的 cable.yml 文件应如下所示:

          production:
              adapter: redis
              url: <%=ENV['REDIS_URL']%>
          

          那么你应该在环境中设置这个键,应该是这样的:

          REDIS_URL: 'redis://redistogo:keyblahblahblhblah'
          

          另外,你应该在 production.rb 中有这个:

          config.web_socket_server_url = "wss://YOUR_URL.com/cable"
          

          【讨论】:

          • 感谢您的回答。我在哪里可以得到这个 redis 的密钥?
          • 啊,好的,我明白了。我不使用 RedisToGo。 Redis 服务器正在我的生产服务器上运行。我猜应该是ws://... 而不是wss://...,因为我没有使用 SSL。
          猜你喜欢
          • 1970-01-01
          • 2017-01-19
          • 1970-01-01
          • 2019-05-03
          • 1970-01-01
          • 2016-06-15
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多