如何修复 Discourse 论坛的 /message-bus 或长轮询错误

1. 問題

当 Discourse 配置了 CDN 时,会发生一个常见错误:

/message-bus/204234de907442e8b77e153786a58e5b/poll
连接失败 / 超时 / 状态码异常

影响:

  • 通知失败:红点(新私信/回复)不再实时显示;更新可能延迟或根本不出现。

  • 实时更新中断:新帖子/点赞/投票不会自动刷新;需要手动刷新页面。

  • 用户体验下降:“连接丢失”消息出现;交互延迟。

  • 服务器负载增加:前端不断重试轮询,给源站增加压力。

原因:Discourse 使用长轮询来维护实时通信。许多 CDN 会强制执行默认缓存、缩短超时时间、进行挑战/防火墙检查或对长连接进行缓冲,从而导致中断或缓存响应,破坏实时行为。


2. 通用方法(稳定性优先)

  • 域名分离:通过专用域名将 MessageBus 路由到源站。

  • 反向代理(Nginx)层

    • 为 /message-bus 启用 CORS。
    • 禁用代理缓冲、放宽超时时间、明确禁止缓存。
  • CDN 层(如果仍在使用)

    • 将 /message-bus/* 配置为无缓存、延长超时时间、无 JS 挑战/验证码/速率限制,以及 cookie/授权直通
    • 或者完全绕过 CDN。

3. 实现步骤

1) 配置 Discourse 环境变量

编辑 app.yml(通常位于 /var/discourse/containers/app.yml),在 env:: 下添加/修改:

env:
  DISCOURSE_MESSAGE_BUS_REDIS_ENABLED: true
  DISCOURSE_LONG_POLLING_BASE_URL: "https://messagebus.example.com"

应用更改(官方部署):

cd /var/discourse
./launcher rebuild app

说明:

  • DISCOURSE_LONG_POLLING_BASE_URL 告诉前端使用 MessageBus 域名。
  • REDIS_ENABLED 应保持启用。

2) DNS 和证书

  • messagebus.example.com 直接指向源站,绕过 CDN(最佳实践)。
  • 为该域名设置有效的 HTTPS 证书。

3) Nginx(MessageBus 域名)反向代理和 CORS

messagebus.example.com 的 server 块中添加或更新以下内容:

location ^~ /message-bus {

    # (1) 处理 CORS 预检 (OPTIONS)
    if ($request_method = OPTIONS) {
        add_header 'Access-Control-Allow-Origin' 'https://bbs.example.com' always;
        add_header 'Access-Control-Allow-Credentials' 'true' always;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
        add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With,discourse-deferred-track-view-topic-id,discourse-present,discourse-track-view,discourse-deferred-track-view,x-silence-logger,dont-chunk,x-shared-session-key' always;
        add_header 'Access-Control-Max-Age' 1728000 always;
        add_header 'Content-Type' 'text/plain; charset=UTF-8' always;
        add_header 'Content-Length' 0 always;
        return 204;
    }

    # (2) 反向代理到 Discourse
    proxy_pass http://unix:/var/discourse/shared/standalone/nginx.http.sock:;
    # 或者,如果是独立部署:
    # proxy_pass http://127.0.0.1:3000;

    # (3) 防止重复的 CORS 标头
    proxy_hide_header Access-Control-Allow-Origin;
    proxy_hide_header Access-Control-Allow-Credentials;
    proxy_hide_header Access-Control-Allow-Methods;
    proxy_hide_header Access-Control-Allow-Headers;
    proxy_hide_header Access-Control-Max-Age;

    # (4) 普通请求的 CORS
    add_header 'Access-Control-Allow-Origin' 'https://bbs.example.com' always;
    add_header 'Access-Control-Allow-Credentials' 'true' always;
    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
    add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With,discourse-deferred-track-view-topic-id,discourse-present,discourse-track-view,discourse-deferred-track-view,x-silence-logger,dont-chunk,x-shared-session-key' always;

    # (5) 长轮询稳定性设置
    proxy_read_timeout    120s;
    proxy_send_timeout    120s;
    proxy_connect_timeout 60s;

    proxy_buffering off;
    add_header X-Accel-Buffering no always;

    # (6) 明确禁止缓存
    add_header Cache-Control "no-store, no-cache, must-revalidate" always;
}

:warning: 安全提示:如果使用 Access-Control-Allow-Credentials: true,Origin 不能是 *;它必须与论坛的确切域名匹配。


4) CDN 规则(如果论坛仍通过 CDN)

推荐:为以下路径设置无缓存 + 延长超时时间 + 绕过 WAF/速率限制

正则表达式示例:

^/(session|login|message-bus|admin|u|users)(/|$)

策略:

  • 无浏览器/节点缓存(no-store/no-cache)。
  • 上游/读取/空闲超时时间 ≥ 60–120 秒。
  • 禁用 JS 挑战/验证码/机器人管理。
  • 传递 cookie 和 Authorization 标头(不要剥离)。

4. 如何验证成功

1) 浏览器开发者工具 → 网络

在论坛页面上:

  • 观察 /message-bus/…/poll。
  • 请求应“挂起”约 20–60 秒,然后返回 200(可能为空)。
  • 下一个轮询请求会自动触发。

检查响应标头:

  • Access-Control-Allow-Origin: https://bbs.example.com
  • Cache-Control: no-store
  • 无 Age、X-Cache: HIT 或 CF-Cache-Status: HIT(表示未缓存)。

常见问题:

  • 固定的 10 秒/30 秒错误 → 边缘/源站超时。
  • 504/524:超时。
  • 499:中间层断开连接。
  • 403/401:WAF/身份验证阻止。

2) 命令行快速探测(可选)

检查连接性和标头(非完整轮询):

curl -I "https://messagebus.example.com/message-bus/health-check" \
  -H "Origin: https://bbs.example.com"

注意:实际轮询需要会话上下文;这仅验证 CORS 和连接性。

1 个赞