流式响应Nginx配置踩坑记

3,359 阅读15分钟

后端流式响应服务上线后,https请求页面响应卡顿

前端vue, 后端springboot SSE(Server-Sent Events,服务器发送事件)。后端调用Dify第三方的API接口将流式响应推送给前端vue页面, 同样的链路http请求正常,https忽略SSL证书请求响应卡顿,起初怀疑是证书的问题,更换证书后依旧卡顿。 经过curl命令排查, https请求时,后端响应明显变慢,但是http正常, 排除代码本身的原因,求助gpt4, 和链路上nginx配置有关:

location /streaming_path {
    proxy_pass http://backend_service;
    proxy_buffering off;  # 关闭代理缓冲,确保流式传输
}

但是,我有个疑问: 相同的nginx配置,为什么 http访问正常, https访问好像被nginx 截流了呢? 为什么 http请求 没有proxy_buffering off; 设置也正常响应呢?

这个问题的核心在于 HTTP 和 HTTPS 在 Nginx 代理中的处理方式不同。以下几点解释了为什么 HTTP 请求可以正常响应,而 HTTPS 请求在没有关闭 proxy_buffering 时表现出卡顿的现象。

1. Nginx 代理行为差异

  • HTTP:在 HTTP 请求中,Nginx 默认会使用缓冲机制,但由于 HTTP 请求本身并不涉及加密和解密,数据的传输和处理通常比较快,即使启用了 proxy_buffering,只要后端响应速度快,客户端也不会明显感受到延迟。Nginx 能够较快速地将缓冲的数据传递给客户端,因此你感受不到 HTTP 请求的卡顿。
  • HTTPS:HTTPS 请求涉及加密、解密过程,Nginx 需要处理 SSL/TLS 协议,导致响应的数据经过更多处理环节。在这种情况下,proxy_buffering 缓冲机制可能会导致 Nginx 等待完整的响应数据块,再将其推送到客户端,增加了数据传输的延迟感,尤其在流式数据的场景中更明显。

2. 缓冲的作用

  • proxy_buffering 的默认行为:Nginx 默认会缓冲来自后端服务器的响应数据,缓冲满了之后再一次性发送到客户端。对于 HTTP 请求,数据包小而且无需解密,Nginx 处理较为快速,通常不会影响用户体验。
  • HTTPS 的复杂性:在 HTTPS 情况下,由于涉及加密和较大的数据包,Nginx 可能需要更长的时间来处理响应。而当启用缓冲时,Nginx 会先缓冲较大的数据块,再推送到客户端,这使得 HTTPS 的流式响应延迟更为明显。关闭 proxy_buffering 后,Nginx 可以直接将流式数据传递给客户端,无需等待缓冲区填满。

3. 流式传输的需求

  • 流式响应要求数据能够即时从后端传递给前端,而不是经过代理服务器的缓冲。对于流式传输来说,proxy_buffering 可能会阻碍这种即时时性。HTTP 由于传输开销小,能承受一定的缓冲延迟,而 HTTPS 的加密过程放大了这个问题。

4. 数据包大小和网络开销

  • HTTPS 请求的每个数据包相对更大,因为它包含了加密后的数据。Nginx 可能需要缓冲这些较大的数据包,而一旦 Nginx 使用 proxy_buffering 进行缓冲处理,这种额外的等待时间会被放大。而 HTTP 请求没有这种加密开销,响应更快。

总结:

简单来说,HTTPS 请求的流式响应之所以卡顿,主要是因为:

  • 数据包更大:HTTPS 请求的数据包经过加密后确实比 HTTP 大一些,加密增加了开销。
  • 缓冲机制的影响:在 HTTPS 请求中,Nginx 的缓冲机制需要更多时间来处理这些加密的数据包,导致延迟。相比之下,HTTP 没有加密开销,即使启用缓冲,数据传输也相对流畅。

因此,同样的请求,HTTPS 因为加密、数据包较大,加上 Nginx 的缓冲,导致流式响应出现卡顿。关闭 proxy_buffering 后,Nginx 不再等待缓冲区填满,而是直接传输数据,HTTPS 响应也就变得流畅了。

proxy_buffering 默认多久向下传一次呢

Nginx 的 proxy_buffering 默认行为是根据缓冲区大小和内容量来决定何时将数据传递给客户端,并没有一个固定的时间间隔。具体的传输时间主要取决于以下几个因素:

  1. 缓冲区大小

    • Nginx 在接收到后端服务器的响应后,会将数据写入缓冲区,只有当缓冲区填满后,才会将数据传递给客户端。缓冲区大小可以通过以下几个配置项调整:

      • proxy_buffer_size:控制第一个缓冲区的大小,通常用于存储响应头。
      • proxy_buffers:指定用于存储响应主体的缓冲区大小和数量。
      • proxy_busy_buffers_size:控制在传输过程中可以同时用于传输的缓冲区大小。
  2. 数据量和响应速率

    • 如果后端服务器响应的数据量较大,并且 Nginx 缓冲区被迅速填满,数据会更快地传递给客户端。
    • 如果数据量较小或者响应速率较慢,Nginx 可能会等待更多数据来填满缓冲区,导致客户端需要更长时间才能接收到数据。
  3. 网络延迟和性能

    • Nginx 会根据客户端的网络条件来决定何时传递缓冲的数据。如果客户端的网络状况较差,Nginx 可能会等待更长时间,直到数据完全缓冲完毕再传递。

默认行为总结:

  • Nginx 没有一个固定的时间间隔来传递缓冲的数据,而是根据缓冲区大小和数据的填充情况来动态决定。如果缓冲区没有填满,Nginx 可能会等待更多的数据积累后再发送给客户端,这在流式传输场景中会导致延迟和卡顿。

通过关闭 proxy_buffering,Nginx 可以跳过这个缓冲机制,直接将后端的响应数据实时传递给客户端,特别适合需要即时传输的流式响应。

如果数据量较小,缓冲区没有填满,nginx就不将数据下发了吗

是的,默认情况下,如果缓冲区没有填满,Nginx 可能会等待更多数据填充缓冲区,然后再将数据发送给客户端。Nginx 的 proxy_buffering 机制是为了提高传输效率,避免频繁发送小数据块。当缓冲区未满时,Nginx 会尽量等待更多的数据来减少发送的次数。

但是在某些情况下,Nginx 可能仍会在超时之前将数据传递给客户端,比如:

  1. 响应结束:如果后端服务器已经发送了完整的响应,且缓冲区没有填满,Nginx 会在接收到完整响应后将数据下发给客户端。

  2. 超时设置:Nginx 有一些超时配置项,控制在没有收到新数据时应等待多长时间。例如:

    • proxy_read_timeout:指定 Nginx 等待后端服务器响应数据的时间。如果在这个时间内后端没有发送足够的数据,Nginx 会终止连接或发送已缓冲的数据。
  3. 响应头的触发:在流式响应的场景中,某些响应头(例如 Transfer-Encoding: chunkedContent-Length)可能会促使 Nginx 立即下发数据,而无需等待缓冲区填满。

适用于流式传输的优化:

对于流式传输或实时性要求较高的场景,默认的 proxy_buffering 可能导致延迟。关闭 proxy_buffering(即 proxy_buffering off;)可以确保 Nginx 不会等待缓冲区填满,而是实时将后端发送的数据流传递给客户端。

解决缓冲区等待的问题:

  • 关闭缓冲proxy_buffering off; 会禁用缓冲机制,Nginx 会立即将后端服务器发送的每一小段数据直接转发给客户端,而不管缓冲区是否满。
  • 调整缓冲区大小:如果你不想关闭缓冲,可以调整 proxy_buffer_sizeproxy_buffers 的大小,使得 Nginx 缓冲的量变得更小,以便更快将数据下发。

在实时应用中,关闭 proxy_buffering 是常见的优化方案,可以有效避免缓冲区等待问题,确保数据即时传输。

proxy_buffer_sizeproxy_buffers 的大小 默认是多少,应该怎么优化设置?

Nginx 中 proxy_buffer_sizeproxy_buffers 是配置代理服务器缓冲响应数据的参数,它们控制缓冲区的大小和数量。优化这些设置取决于你的具体使用场景。

1. proxy_buffer_size

  • 默认值proxy_buffer_size 的默认值是 4k8k,这取决于操作系统的页面大小。通常在 32 位系统上是 4k,在 64 位系统上是 8k
  • 作用:该配置指定用于存储后端服务器响应头的缓冲区大小。如果响应头大于这个值,Nginx 将临时使用磁盘来存储超出的部分,可能会导致性能下降。

优化建议

  • 如果你知道后端服务器会返回较大的响应头,建议增加这个值。例如,返回带有大量 Set-Cookie 头部或大 HTTP 响应头的应用(如 REST API、复杂的 Web 应用等)可以适当增大该值。

  • 常见优化值:8k16k32k,具体取决于应用的响应头大小。

    proxy_buffer_size 16k;
    

2. proxy_buffers

  • 默认值:默认设置为 8 4k8 8k,这表示 Nginx 会使用 8 个缓冲区,每个缓冲区大小为 4k8k,依赖于页面大小。
  • 作用proxy_buffers 定义了用于存储后端服务器响应体的缓冲区数量和大小。如果响应体大于所有缓冲区的总容量,Nginx 会将数据写入临时文件(磁盘)中,从而降低性能。

优化建议

  • 对于大响应体(例如文件传输、图片、视频等大文件),可以增大缓冲区的大小和数量,以减少对磁盘的依赖,提升性能。

  • 对于流式传输或需要即时传输响应的小数据块应用(如聊天、日志输出等),可以设置较小的缓冲区,甚至关闭缓冲。

  • 常见优化值proxy_buffers 的大小和数量通常可以根据响应体大小调整。例如:

    proxy_buffers 16 16k;
    

    这表示使用 16 个缓冲区,每个缓冲区大小为 16k,总共 256k 的内存缓冲区来存储响应体。如果应用响应体较大,你可以进一步增加缓冲区数量或大小。

3. 如何选择合适的大小?

优化 proxy_buffer_sizeproxy_buffers 的设置时,应考虑以下几点:

  1. 响应头大小:如果响应头通常较大,可以增加 proxy_buffer_size 来避免频繁写入磁盘。你可以监控响应头的大小,观察是否超过默认的 4k8k
  2. 响应体大小:对于较大响应体的场景(例如文件下载或视频流),增加 proxy_buffers 的缓冲区数量和大小可以提高性能,减少对磁盘的依赖。你可以根据应用的典型响应体大小进行调整。
  3. 硬件资源:确保增加缓冲区大小不会导致内存消耗过多,尤其是在高并发场景下,每个连接都会使用这些缓冲区,因此需要平衡内存使用与性能。
  4. 磁盘 I/O 性能:在缓冲区不够时,Nginx 会写入临时文件。如果磁盘 I/O 性能较差,增大缓冲区以避免频繁的磁盘写入会显著提升性能。

示例优化配置

以下是一个优化的配置示例,适用于响应头和响应体较大的应用场景:

proxy_buffer_size 16k;      # 适当增加响应头的缓冲区大小
proxy_buffers 8 32k;        # 增加响应体的缓冲区数量和大小
proxy_busy_buffers_size 64k; # 优化繁忙缓冲区大小,适用于高负载场景
  • 这里 proxy_buffer_size 设置为 16k,适合较大响应头。
  • proxy_buffers 使用 8 个 32k 缓冲区来缓存响应体。
  • proxy_busy_buffers_size 设为 64k,确保繁忙时 Nginx 有足够的缓冲空间。

总结

  • proxy_buffer_size 主要优化响应头的处理,通常推荐从 8k16k
  • proxy_buffers 控制响应体的缓存,优化时根据应用的响应体大小调整,常见设置为 816 个缓冲区,每个缓冲区 16k32k

根据应用场景和服务器资源的不同,可以灵活调整这些值,以达到最佳的性能优化。

proxy_buffering 的优点和缺点有哪些呢?

启用 proxy_buffering 是为了优化 Nginx 的性能,减少后端服务器与客户端之间的传输负担,特别是在处理大量请求时,缓冲机制可以显著提高资源利用率和吞吐量。但它也有缺点,尤其是在需要实时传输或流式响应的场景中。

proxy_buffering 的优点

  1. 提高传输效率

    • 缓冲机制允许 Nginx 在接收到一定量的数据后再一次性发送给客户端,而不是每接收一小块数据就立即传输。这样可以减少与客户端之间频繁的小数据传输,降低网络开销,尤其是在网络延迟较高或客户端连接较慢时。
  2. 减轻后端服务器负担

    • Nginx 会先将后端服务器的响应缓存在内存或硬盘中,然后以适合客户端接收的速度传递数据。这意味着即使客户端接收速度慢,后端服务器也能快速释放资源,处理更多的请求。
  3. 支持负载均衡和缓存优化

    • 在启用 proxy_buffering 的情况下,Nginx 可以利用它的缓存机制进行负载均衡和优化。例如,如果请求可以被缓存,Nginx 只需要一次从后端获取响应,之后可以直接从缓存中提供数据给多个客户端,减轻后端服务器的压力。
  4. 减少频繁的 IO 操作

    • 通过将小数据块汇总到较大的缓冲区再进行发送,Nginx 可以减少系统的 I/O 操作次数,特别是在处理大量小数据包时,这可以提高整体系统性能。

proxy_buffering 的缺点

  1. 增加延迟

    • 对于实时性要求高的场景(如实时视频流、WebSocket、SSE 流式传输等),Nginx 会等待缓冲区填满才下发数据,导致客户端无法立即收到响应,从而增加响应的延迟。这在流式传输或小数据包频繁发送的场景中表现得尤为明显。
  2. 缓冲区可能不满导致数据延迟发送

    • 如果响应的数据量较小,Nginx 可能会等待缓冲区填满才发送,这会导致一些数据长时间滞留在缓冲区中,延迟传递给客户端,尤其是在流式传输的场景中可能表现为“卡顿”。
  3. 额外的内存和硬盘消耗

    • 缓冲需要占用内存或者硬盘(如果超出内存缓冲限制),这可能导致 Nginx 在高负载的情况下需要大量内存或磁盘空间。如果缓冲区配置不当,可能导致内存占用过高或者磁盘空间不足。
  4. 不适合流式应用

    • 对于需要逐步发送数据的应用(如聊天消息、SSE、实时日志输出等),启用缓冲会影响用户体验,因为缓冲机制阻止了数据的即时下发。

什么时候使用 proxy_buffering

  • 适合的场景

    • 大文件传输:在传输大文件或较大响应时,proxy_buffering 能减少对网络带宽的占用,提高传输效率。
    • 后端负载较高:当后端服务器负载较高时,Nginx 缓冲可以帮助后端更快地释放资源,而不会被慢速的客户端阻塞。
    • 静态资源缓存:对于静态资源,Nginx 缓冲和缓存结合使用可以显著提高性能,减少后端服务器的负载。
  • 不适合的场景

    • 实时流式传输:如实时聊天、视频流、WebSocket、SSE 等场景,这些场景下需要即时将数据传递给客户端,缓冲会增加延迟。
    • 小数据块频繁传输:如果应用需要频繁发送小数据包,启用 proxy_buffering 可能导致这些数据滞留在缓冲区内,延迟传输给客户端。

总结:

  • 优点:提高传输效率、减轻后端负担、支持缓存优化、减少系统 I/O 操作。
  • 缺点:增加延迟、不适合实时应用、需要额外的内存或硬盘资源、可能导致小数据块的延迟发送。

如果应用场景中对实时性要求不高,并且传输的数据量较大,proxy_buffering 可以显著提高性能和资源利用效率。但对于需要即时响应或流式传输的应用,关闭 proxy_buffering 则是更好的选择。