【问题标题】:HTTP2 with node.js behind nginx proxyHTTP2 与 node.js 在 nginx 代理后面
【发布时间】:2017-05-28 23:18:15
【问题描述】:

我有一个在 nginx 代理后面运行的 node.js 服务器。 node.js 在端口 3000 上运行 HTTP 1.1(无 SSL)服务器。两者都在同一台服务器上运行。

我最近将 nginx 设置为使用带有 SSL (h2) 的 HTTP2。看来 HTTP2 确实已启用并且可以正常工作。

但是,我想知道代理连接(nginx node.js)使用 HTTP 1.1 的事实是否会影响性能。也就是说,我是否因为我的内部连接是 HTTP 1.1 而错过了 HTTP2 在速度方面的优势?

【问题讨论】:

  • 好问题,也可以应用于像 Docker Swarm 这样的容器化
  • 您好,只是好奇,您能分享一下您的 nginx 配置吗?我在 Elastic Beanstalk 环境中复制相同的行为时遇到了一些问题。

标签: node.js nginx http2


【解决方案1】:

一般来说,HTTP/2 最大的直接好处是 multiplexing 为浏览器连接提供的速度提升,这些连接通常受到高延迟(即缓慢的往返速度)的阻碍。这些还减少了多个连接的需求(和费用),这是一种尝试在 HTTP/1.1 中实现类似性能优势的解决方法。

对于内部连接(例如充当反向代理的网络服务器和后端应用服务器之间),延迟通常非常非常低,因此 HTTP/2 的速度优势可以忽略不计。此外,每个应用服务器通常已经是一个单独的连接,因此这里也没有任何好处。

因此,仅在边缘支持 HTTP/2,您将获得大部分的性能优势。这是一个相当常见的设置 - 类似于 HTTPS 经常在反向代理/负载平衡器上终止而不是一直通过的方式。

但是,从始至终支持 HTTP/2 有潜在的好处。例如,它可以允许服务器从应用程序一路推送。由于 HTTP/2 的二进制性质和标头压缩,减少最后一跳的数据包大小也有潜在的好处。尽管与延迟一样,带宽对于内部连接而言通常不是问题,因此其重要性是值得商榷的。最后,有些人认为反向代理连接 HTTP/2 连接到 HTTP/2 连接的工作比连接到 HTTP/1.1 连接的工作少,因为不需要将一种协议转换为另一种协议,尽管我对此表示怀疑。很明显,因为它们是单独的连接(除非它只是充当 TCP 直通代理)。所以,对我来说,端到端 HTTP/2 的主要原因是允许端到端服务器推送,但是even that is probably better handled with HTTP Link Headers and 103-Early Hints due to the complications in managing push across multiple connections 我不知道有任何 HTTP 代理服务器支持这个(支持 HTTP/2在后端不要介意像这样链接 HTTP/2 连接),所以你需要一个第 4 层负载均衡器来转发 TCP 打包程序,而不是链接 HTTP 请求——这会带来其他复杂性。

目前,虽然服务器仍在添加支持并且服务器推送使用率很低(并且仍在试验以定义最佳实践),但我建议仅在端点使用 HTTP/2。在撰写本文时,Nginx 还不支持 HTTP/2 用于 ProxyPass 连接(尽管 Apache 支持),并且有 no plans to add this,他们提出了一个有趣的观点,即单个 HTTP/2 连接是否会引入缓慢(强调我的):

是否计划在不久的将来支持 HTTP/2 代理?

简答:

不,没有计划。

长答案:

实现它几乎没有意义,因为它是 HTTP/2 的主要好处 是它允许在单个请求中多路复用多个请求 连接,因此 [几乎] 取消了对数量的限制 simalteneous requests - 与交谈时没有这样的限制 你自己的后端。 此外,使用时情况甚至可能变得更糟 HTTP/2 到后端,因为使用了单个 TCP 连接 多个

另一方面,实现 HTTP/2 协议和请求 在上游模块中的单个连接内多路复用将 需要对上游模块进行重大更改。

由于上述原因,没有计划在中实现 HTTP/2 支持 上游模块,至少在可预见的未来。如果你 仍然认为需要通过 HTTP/2 与后端​​通信 - 随时提供补丁。

最后,还应该注意的是,虽然浏览器需要 HTTP/2 (h2) 的 HTTPS,但大多数服务器不需要,因此可以支持 HTTP 上的最后一跳 (h2c)。因此,如果节点部分不存在端到端加密(通常不存在),则不需要端到端加密。但是,根据后端服务器相对于前端服务器的位置,如果流量将通过不安全的网络传输(例如,CDN 到 Internet 上的源服务器),那么即使对于这种连接也应该考虑使用 HTTPS。

2021 年 8 月编辑

HTTP/1.1 基于文本而不是 binary 确实使其容易受到各种请求走私攻击。在 Defcon 2021 PortSwigger demonstrated a number of real-life attacks 中,主要与将前端 HTTP/2 请求降级为后端 HTTP/1.1 请求时的问题有关。这些可能主要通过一直使用 HTTP/2 来避免,但鉴于当前支持前端服务器和 CDN 对后端使用 HTTP/2,以及支持 HTTP/2 的后端,这似乎需要很长时间因为这很常见,前端 HTTP/2 服务器确保这些攻击不可被利用似乎是更现实的解决方案。

【讨论】:

  • 感谢您的广泛回复。您在协议之间“翻译”以及在我的设置中多路复用的整体有效性方面的 cmets 主要是我正在寻找的东西。
  • 您好,您能分享一下如何使用反向代理服务和后端服务实现服务器推送的想法吗?我用spdy 或本机http2 尝试了nodejs,它们都需要SSL 才能工作(看起来这是使用http2 的关键要求,无论是什么lib 或平台)。好吧,我没有想到将反向代理服务与后端服务结合起来,因为据我所知,我们总是只在反向代理服务中使用 SSL,但是,后端服务说他们现在也需要它。而且我完全同意端到端加密是一种浪费。
  • 首先 Nginx 不支持服务器推送,但如果使用 Apache 为例,那么可以有 HTTP/2 到客户端,然后 HTTP/1.1 到节点。然后要实现服务器推送,您只需在响应中从节点添加 link 标头。 Apache,将看到响应,看到链接头,并自动请求资源并将其推送到客户端。
  • NGINX 现在支持 HTTP2/Push!正如您提到的那样,我已经设置了它,NGINX 进行推送,NodeJS 坐在代理后面,它工作得很好!我正在推动的大件物品是一个很大的缩小的 .css 和缩小的 .js 文件。 `
  • 是的 NGINX 支持 Push,但仍然不支持 HTTP/2 到 Node 等后端。然而,就像你正在做的那样,更好的推送方式可能是使用链接头(所以让 Nginx 推送),所以 HTTP/2 缺少这个代理并不重要。
【解决方案2】:

NGINX 现在支持proxy_pass 的 HTTP2/Push,这太棒了...

在这里,我也在从我的静态子域推送 favicon.ico、minified.css、minified.js、register.svg、purchase_litecoin.svg。我花了一些时间才意识到我可以从子域推送。

location / {
            http2_push_preload              on;
            add_header                      Link "<//static.yourdomain.io/css/minified.css>; as=style; rel=preload";
            add_header                      Link "<//static.yourdomain.io/js/minified.js>; as=script; rel=preload";
            add_header                      Link "<//static.yourdomain.io/favicon.ico>; as=image; rel=preload";
            add_header                      Link "<//static.yourdomain.io/images/register.svg>; as=image; rel=preload";
            add_header                      Link "<//static.yourdomain.io/images/purchase_litecoin.svg>; as=image; rel=preload";
            proxy_hide_header               X-Frame-Options;
            proxy_http_version              1.1;
            proxy_redirect                  off;
            proxy_set_header                Upgrade $http_upgrade;
            proxy_set_header                Connection "upgrade";
            proxy_set_header                X-Real-IP $remote_addr;
            proxy_set_header                Host $http_host;
            proxy_set_header                X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header                X-Forwarded-Proto $scheme;
            proxy_pass                      http://app_service;
        }

【讨论】:

  • 我刚刚为这个问题添加了书签,并想在您的答案中添加一个官方公告链接 - Introducing HTTP/2 Server Push with NGINX 1.13.9 - 它包含几个有用的示例。
  • @IvanShatsky 您引用的页面说不应该推送可能缓存的资源。服务器无法知道客户端缓存了什么,最常见的资源,最有可能缓存的资源(因为它们在每个页面上),正是您想要推送的资源。 Push 不会绕过浏览器缓存 AFAIK。
【解决方案3】:

如果有人在不方便使您的服务与 HTTP2 兼容时正在寻找解决方案。这是您可以用来将 HTTP1 服务转换为 HTTP2 服务的基本 NGINX 配置。

server {
  listen [::]:443 ssl http2;
  listen 443 ssl http2;

  server_name localhost;
  ssl on;
  ssl_certificate /Users/xxx/ssl/myssl.crt;
  ssl_certificate_key /Users/xxx/ssl/myssl.key;

  location / {
    proxy_pass http://localhost:3001;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
  }
}

【讨论】:

    【解决方案4】:

    您通常不会损失性能,因为 nginx 通过向您的节点后端创建多个同时请求来匹配浏览器在 HTTP/2 上执行的多路复用请求。 (HTTP/2 的主要性能改进之一是允许浏览器在同一个连接上同时发出多个请求,而在 HTTP 1.1 中,每个连接只能同时发出一个请求。而且浏览器也限制了连接数。)

    【讨论】:

      【解决方案5】:

      NGINX 不支持 HTTP/2 作为客户端。由于它们在同一台服务器上运行并且没有延迟或带宽有限,我认为这两种方式都不会产生巨大的不同。我会确保你在 nginx 和 node.js 之间使用 keepalives。

      https://www.nginx.com/blog/tuning-nginx/#keepalive

      【讨论】:

      • NGINX 现在支持 HTTP/2 和 proxy_pass。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-01
      • 1970-01-01
      • 2021-07-09
      • 1970-01-01
      • 2015-10-20
      相关资源
      最近更新 更多