加载中

Version 1.3.13 of nginx is out and with it comes support for Connection: upgrade and Upgrade header, meaning proxying of WebSockets! Many people have been waiting for this and “are websockets in nginx supported?” is one of the most frequent questions in #nginx on freenode. With that out of the way, time to have a look at the nginx WebSocket implementation.

The New Nginx Websockets Configuration Directives

The documentation provided states the configuration as follows:

    location /chat/ {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    }

This is indeed fairly simple and configuring the HTTP version used isn’t even new at all. We can go ahead and improve the versatility a bit by creating a connection variable thus allowing us to move these proxy_set_headers into a generic included file.

    map $http_connection $upgrade_requested {
    default upgrade;
    '' close;
    }

This makes the variable $upgrade_requested available to proxy_set_header Connection and if connection upgrade wasn’t requested then it will default back to “” to not interfere with normal requests. The idea here being that if you always proxy HTTP/1.1 then you don’t need a location specifically to handle WebSockets.

Nginx 1.3.13 已经发布了,该版本支持 Connection: upgrade 和 Upgrade 头,这就意味着支持WebSocket代理了.很多人都在等这个新特性以至于 “Nginx 支持 websockets 吗?” 成为了 freenode上的#nginx频道最常问的问题. 有了这种方式,让我们来看看Nginx的WebSocket实现.

Nginx新添加的Websockets配置指令

文档中提到的配置如下:(译者注:原文中的链接其实不是文档的链接.现在nginx的官方文档中已经有websocket的说明了http://nginx.org/en/docs/http/websocket.html)

    location /chat/ {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    }

这配置的确是相当简单连配置HTTP的版本都是以前版本就支持的. 我们还可以稍微优化一下配置,通过创建一个链接的变量把proxy_set_headers放到一个公共的包含文件中,以此来提高通用性.

    map $http_connection $upgrade_requested {
    default upgrade;
    '' close;
    }

这样使得变量 $upgrade_requested 可以在 proxy_set_header Connection 里面使用,而且如果没有 upgrade 的链接请求,则Conection这个头会被设置为"",这样就不会干扰普通的请求了.这样做的好处是,如果你只使用HTTP/1.1代理,那么你不需要再另外配置一个location来专门处理WebSockets.

Connection upgrading seems unlikely to be back ported into the stable branch so if you wish to use this you will have to use the development branch. Thankfully development in nginx does not mean that it’s not run-time stable, it just means that the API might change and as such it only affects module authors. Don’t be afraid to install the development version to play around with this new feature.

Limitations

So far a few limitations are known in the websocket implementation.

Client Must Specify Connection Upgrade
The client need to have requested the connection upgrade, otherwise nginx will fail. Currently that part is listed as a todo in the code so I can’t say how it will fail, but I’m sure it will be implemented eventually so don’t rely on it. This limitation should not be an issue to anyone and the only issue with it is a theoretical situation where a module author might want to use connection upgrade to a backend and then process the response itself.

链接升级似乎没有往后移植到稳定的版本中,因此如果你想要使用链接升级功能,你必须使用开发版本。感激的是nginx的开发版本并不意味着它不是运行稳定版,只是意味着API会变,在这种情况下只会影响模块的编写者。不要害怕安装开发版本来尝试这个新特征。

局限性:

到目前为止,在websocket的执行部署中有一些局限性:

客户端必须制定链接升级

客户端必须请求链接升级,否则nginx将会失败。当前这个要求在代码里列为要做的部分,因此我需要说它是怎么失败的,但是我可以肯定的是它最后也会被执行,因此我不需要依赖它。这个约束对任何人来说不是个问题,只会在当模块的编写者或许想要应用连接升级到后端且本身处理响应的情况下才会是问题。

WebSockets Time Out
WebSockets are still affected by proxy_read_timeout which defaults to 60 seconds. This means that if you have an application using WebSockets but not sending any data more than once per 60 seconds you either need to increase the timeout or implement a ping message to keep the connection alive. The ping solution has the added benefit of discovering if the connection was closed unexpectedly.

Keep-Alive & WebSockets
Keep-alive pings aren’t usable for working around the above mentioned timeout as they work on the TCP level are are just empty packets. They aren’t reported to the application and thus the application will never respond to them meaning that proxy_read_timeout will still trigger.

WebSockets 超时
WebSockets 仍然受到缺省为60秒的 proxy_read_timeout 的影响。这意味着,如果你有一个程序使用了 WebSockets,但又可能超过60秒不发送任何数据的话,那你要么需要增加超时时间,要么实现一个 ping 的消息以保持联系。使用 ping 的解决方法有额外的好处,可以发现连接是否被意外关闭。

Keep-Alive & WebSockets
Keep-alive pings 对上述超时问题无效,因为它们只是在 TCP 级别上发送空包。它们不向应用程序报告,所以应用程序也不响应它们,因此 proxy_read_timeout 仍然会触发。

WebSockets Supports SSL
Since WebSockets tie into the normal proxy module SSL works the exact same way it normally would.

Proxy Buffers
WebSockets utilize two memory buffers the size of proxy_buffer_size, one for upstream data and another for downstream data. The difference between WebSockets and a normal proxy request is that WebSockets will never buffer to disk.

Case Sensitive Upgrade Header
Some backends do a case sensitive check on the upgrade header and will require the header to be either “upgrade” or “Upgrade”, failing on one and working on the other. If things seem to be right but doesn’t work then try changing the case of the header.

WebSockets支持SSL
由于WebSockets绑定在正常的代理模块中,所以SSL照常的工作运行。

代理缓存
WebSockets利用两个内存缓冲区来实现代理缓存配置(proxy_buffer_size)的大小,一个用于上行数据,另一个用于下行数据。WebSockets和普通的代理请求所不同的是,WebSockets不会将数据缓存到磁盘。

大小写敏感的Upgrade头部
有一些后端程序对Upgrade头做大小写检查,同时支持“upgrade”和“Upgrade”两种写法,否则的话,只有“upgrade”写法能正常工作。如果配置看上去都正确,就是不能正常工作,就要尝试修改头部的大小写了。

返回顶部
顶部