找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

268

积分

0

好友

34

主题
发表于 昨天 23:41 | 查看: 1| 回复: 0

在现代Web应用中,实时通信已成标配。无论是使用Server-Sent Events(SSE) 实现服务端单向推送,还是通过WebSocket建立双向通道,一旦部署到生产环境,Nginx配置不当往往是导致“本地能跑、线上失效”的罪魁祸首。

一、SSE 与 WebSocket:核心区别与选型

特性 SSE (Server-Sent Events) WebSocket
通信方向 服务器 → 客户端(单向) 双向全双工
协议基础 基于 HTTP/1.1,MIME 类型为 text/event-stream 独立协议,需通过 Upgrade: websocket 升级建立
连接管理 浏览器自动重连 需应用层实现重连
适用场景 实时通知、日志流、行情推送 聊天室、协同编辑、在线游戏

选型建议:如果只需服务器向客户端推送数据,选用SSE;若需客户端与服务器双向实时交互,则使用WebSocket。

二、Nginx 代理长连接配置详解(带注释)

假设后端服务运行在 http://127.0.0.1:8000,前端访问路径约定如下:

  • 普通 API 请求:/
  • SSE 接口:/sse/...
  • WebSocket 接口:/ws/...
第一步:全局 WebSocket 协议升级映射

map指令必须放在 http {} 配置块内,用于动态设置Connection头部。

# 根据客户端是否发送 Upgrade 头,动态设置 Connection 值
# 若是 WebSocket 请求($http_upgrade = "websocket"),则 Connection 设为 "upgrade"
# 否则设为 "close",避免普通 HTTP 请求被误判为长连接
map $http_upgrade $connection_upgrade {
    default upgrade;   # 默认:升级连接(用于 WebSocket)
    ''      close;     # 空值:关闭连接(用于普通 HTTP)
}

⚠️ 注意:map 指令不能放在 serverlocation 块中。

Nginx map指令配置位置示意图

第二步:Server 块完整配置

以下配置包含逐行中文注释,清晰地展示了反向代理的核心要点。

server {
    listen 80;
    server_name your-domain.com;  # 替换为你的实际域名或 IP

    # 普通 API 请求代理(无需特殊处理)
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # SSE 长连接代理配置(关键!务必逐项检查)
    location ~ ^/sse/ {
        proxy_pass http://127.0.0.1:8000;

        # 必须使用 HTTP/1.1(SSE 依赖其长连接特性)
        proxy_http_version 1.1;

        # 【核心】禁用 Nginx 缓冲与缓存
        proxy_buffering off;  # 必须!否则Nginx会缓存整个响应体,导致事件流不实时
        proxy_cache off;

        # 关闭 gzip 压缩(SSE 不兼容压缩,浏览器无法解析压缩后的 event stream)
        gzip off;

        # 清空 Connection 头,防止 Nginx 自动添加 "Connection: close"
        proxy_set_header Connection '';

        # 设置长超时时间(根据业务需求调整,例如1小时)
        proxy_read_timeout 3600s;   # 等待后端发送数据的最大空闲时间
        proxy_send_timeout 3600s;   # 向客户端发送数据的超时
        proxy_connect_timeout 3600s;# 与后端建立连接的超时

        # 告诉上游代理(如CDN、多层Nginx)不要缓冲此响应
        proxy_set_header X-Accel-Buffering no;

        # 启用分块传输编码(SSE 依赖 chunked encoding 逐块发送数据)
        chunked_transfer_encoding on;

        # 可选:CORS 跨域支持
        add_header 'Access-Control-Allow-Origin' '*' always;
        add_header 'Access-Control-Allow-Methods' 'GET, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'Origin,Authorization,Accept,X-Requested-With' always;

        # 处理 CORS 预检请求(OPTIONS)
        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Max-Age' 1728000;
            add_header 'Content-Length' 0;
            return 204;
        }
    }

    # WebSocket 长连接代理配置(核心是协议升级)
    location ^~ /ws/ {
        proxy_pass http://127.0.0.1:8000;

        # 必须使用 HTTP/1.1 以支持协议升级
        proxy_http_version 1.1;

        # 【核心】正确传递 WebSocket 升级头
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade; # 使用前面定义的 map 变量

        # 常规代理头(用于获取真实IP、协议等)
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # 设置合理的超时时间
        proxy_read_timeout 300s;   # 5分钟无消息自动断开(可按需调整)
        proxy_send_timeout 300s;
        proxy_connect_timeout 3600s;
    }
}

替代写法(不使用map指令): 如果你不想使用全局的map,可以在WebSocket的location块中直接写死Connection头。

proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

三、高频问题排查清单

SSE 连接不工作?
  1. 检查缓冲:确认 proxy_buffering off; 是否已设置,这是SSE生效的关键。
  2. 检查响应头:确保后端返回正确的 Content-Type: text/event-stream
  3. 检查压缩:确认 gzip off; 已设置。
  4. 多层代理:如果有多层Nginx或CDN,每一层都需要配置 proxy_buffering off;
WebSocket 连接失败?
  1. 检查升级头:确认配置中正确传递了 UpgradeConnection 头部,并使用了 proxy_http_version 1.1;。这是HTTP协议升级的基础。
  2. 检查状态码:在浏览器开发者工具的Network面板中,查看WebSocket握手请求的状态码是否为 101 Switching Protocols。Nginx的access.log有时记录不准确,应以浏览器为准。
  3. 检查超时:默认的 proxy_read_timeout 为60秒,对于长连接可能过短,建议适当延长。

问题排查思维导图

四、最小可用配置(用于快速调试)

SSE 最小配置
location /sse {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_buffering off;        # 必须!
    proxy_cache off;
    gzip off;
    proxy_set_header Connection '';
    proxy_read_timeout 3600s;
}
WebSocket 最小配置
location /ws {
    proxy_pass http://backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 300s;
}

总结与核心要点

  • SSE 配置核心proxy_buffering off; + gzip off; + 足够长的 proxy_read_timeout
  • WebSocket 配置核心:正确透传 UpgradeConnection 头 + proxy_http_version 1.1 + 合理的超时设置。
  • 最后一步:配置修改后,务必使用以下命令测试并重载Nginx,使配置生效。
    
    sudo nginx -t && sudo nginx -s reload
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区(YunPan.Plus) ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-3 14:19 , Processed in 0.965400 second(s), 41 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 CloudStack.

快速回复 返回顶部 返回列表