记一次部署 SSE 消息推送到 Cloudflare 遇到的问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
pzhyy
V2EX    程序员

记一次部署 SSE 消息推送到 Cloudflare 遇到的问题

  •  
  •   pzhyy 2023-08-17 10:33:11 +08:00 2158 次点击
    这是一个创建于 845 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前言

    情况是这样的,在开发 MagicBee 时,需要支持实时消息通知和任务分发功能。

    在做了一番技术方案的对比之后,决定使用 SSE (Server Send Events) 来实现。

    历经一个星期加班加点地开发之后,在本地测试一切都很完美,忍不住想来一波自夸:我真是个天才!

    终于到了部署上线的环节,跟往常一样,这个项目依然采用单机服务,然后使用 Cloudflare DNS 解析,再通过它的边缘网络分发到世界各地。

    为了安全,我决定先上到预发布环境测一测,再发布,以确保万无一失。

    拷贝了一份 Nginx 的配置,改改,-t 测试通过,然后重启 Nginx 使配置生效。

    最后,在 Cloudflare 的配置面板,将域名解析指向自己的服务器 IP 。

    这样,后端的部署就完成了。

    遇到的问题

    我迫不及待地打开 MagicBee 插件,心里有点慌,又有点期待...

    打开控制台,切换到 Network 面板,映入眼帘的是一秒闪一下的 401 错误,心想 完了。

    愣住 3 秒,突然回过来神来的我,这是访问受限,嗷 ~~ ,我还没登录呀!

    于是,不慌不忙地打开登录页,噼里啪啦地输入账号&密码,回车。

    回到 Network 面板,F5 刷新一下,没有错误了,暗自欣喜,继续观察中...

    在 1.1min 后,SSE 连接断开了,可能是网络波动,更何况还有重试机制兜底,不慌,继续观察...

    在下一个 1.1min 后,连接又断开了,看来这不是一个意外!

    首先想到的是 Nginx 的配置不对,赶紧检查了下配置。

    发现 proxy_read_timeout 1m; 设置为 1 分钟,肯定是它在搞鬼,于是改为 8h ,一天断开 3 次连接,还可以接受吧!

    继续观察,在 1.7min 后,又有序的断开连接了。

    啊 ~~ 这次又是什么原因呢???

    疯狂检索后,试了一遍又一遍,各种解决方案,可惜对我都不管用!

    比如,更改 Nginx 配置:

    proxy_http_version 1.1; proxy_set_header Connection ''; chunked_transfer_encoding off; proxy_buffering off; proxy_cache off; 

    在源(上游)服务的响应头添加:

    X-Accel-Buffering: no 

    简直要疯了,为了确定这是 Cloudflare 导致的问题,我不得不在本地装了个 Nginx ,用同样的配置测试却没问题。

    好,既然知道问题出在哪里,那就有解决的方法!

    又一波疯狂地检索后,得知 Cloudflare 对 SSE 支持并不好,但能很好的支持 WebSocket 。

    啊 ~~ 这?难道要我更换技术方案?

    • 使用传统的轮询?
    • 还是长连接?
    • 还是 WebSocket 呢?
    • 还是不用 Cloudflare 了?

    想想那漫漫重构路,我头大了!

    解决方案

    就在即将放弃 SSE 方案之际,决定再好好翻阅 Cloudflare 官方文档,终于功夫不负有心人,发现了这么一个描述。

    意思是 Cloudflare 已成功连接到源服务器,但在 100 秒内没有响应,所以就发生超时了。

    100 秒?换算起来不就是 1.666... 分钟,四舍五入恰好就是 1.7 分钟嘛?这与我遇到的问题十分吻合啊!

    继续往下看,对于这种耗时任务,Cloudflare 也提供了几种解决方案:

    • 使用短轮询来避免发生这个错误
    • 企业用户可以设置 proxy_read_timeout 到 6000 秒
    • 关闭 Cloudflare 的边缘网络分发,请求直接打到源服务器上

    这些都不是我想要的解决方案,仔细想想,是不是在 100 秒内有响应就好了?

    于是,我赶紧改造消息推送的代码,启动一个时钟来每隔 30 秒发送一个 keepalive 的空消息

    部署上去后,继续观察,嗯 ~ 这一次,稳了!

    连接在 1.5 小时后才断开,还是由于电脑休眠了的原因,不过这已经足够了。

    只要能达到小时级别,再加上重连机制,这是可接受的结果。

    总结

    这是一次部署 SSE 消息推送到 Cloudflare 的经历,中途遇到很多很多问题,做了很多测试,再一一排除。

    每一次都到了濒临崩溃,更改技术方案的局面,好在最终克服了重重困难,得偿所愿。

    如果你的应用场景与我相似,请求链路如下:

    Client -> Cloudflare -> Nginx -> Server 
    1. 请在 Server 设置响应头 X-Accel-Buffering: no 来关闭 Nginx 响应缓存
    2. 每隔 30 秒做一次响应来避免 Cloudflare 100 秒未响应的超时错误

    好啦!今天就分享到这里,有任何问题,欢迎在评论区交流。

    4 条回复    2023-08-17 21:14:58 +08:00
    yemoluo
        1
    yemoluo  
       2023-08-17 13:46:03 +08:00
    所以,看到最后,你没搞心跳就是了
    xieqiqiang00
        2
    xieqiqiang00  
       2023-08-17 17:29:51 +08:00 via Android
    cloudflare 免费的压根不支持 sse 吧?踩过坑
    pzhyy
        3
    pzhyy  
    OP
       2023-08-17 17:51:09 +08:00
    @GTim 跳了,每 30s 跳一下,要不然 Cloudflare 就以为链接死了

    @xieqiqiang00 支持!只是之前没搞过,这一次踩坑,记录一下
    RangerWolf
        4
    angerWolf  
       2023-08-17 21:14:58 +08:00
    感觉很干货,很多我没见过的东西!感谢楼主分享
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5283 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 06:51 PVG 14:51 LAX 22:51 JFK 01:51
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86