記一次部署 SSE 消息推送到 Cloudflare 遇到的問(wèn)題
前言
情況是這樣的,在開(kāi)發(fā) MagicBee 時(shí),需要支持實(shí)時(shí)消息通知和任務(wù)分發(fā)功能。
在做了一番技術(shù)方案的對(duì)比之后,決定使用 SSE (Server Send Events) 來(lái)實(shí)現(xiàn)。
歷經(jīng)一個(gè)星期加班加點(diǎn)地開(kāi)發(fā)之后,在本地測(cè)試一切都很完美,忍不住想來(lái)一波自夸:我真是個(gè)天才!??
終于到了部署上線的環(huán)節(jié),跟往常一樣,這個(gè)項(xiàng)目依然采用單機(jī)服務(wù),然后使用 Cloudflare DNS 解析,再通過(guò)它的邊緣網(wǎng)絡(luò)分發(fā)到世界各地。
為了安全,我決定先上到預(yù)發(fā)布環(huán)境測(cè)一測(cè),再發(fā)布,以確保萬(wàn)無(wú)一失。
拷貝了一份 Nginx 的配方,改改,-t 測(cè)試通過(guò),然后重啟 Nginx 使配置生效。
最后,在 Cloudflare 的配置面板,將域名解析指向自己的服務(wù)器 IP。
這樣,后端的部署就完成了。
遇到的問(wèn)題
我迫不及待地打開(kāi) MagicBee 插件,心里有點(diǎn)慌,又有點(diǎn)期待…
打開(kāi)控制臺(tái),切換到 Network 面板,映入眼簾的是一秒閃一下的 401 錯(cuò)誤,心想 ?? 完了。
愣住 3 秒,突然回過(guò)來(lái)神來(lái)的我,這是訪問(wèn)受限,嗷 ~~ 嗐,我還沒(méi)登錄呀!
于是,不慌不忙地打開(kāi)登錄頁(yè),噼里啪啦地輸入賬號(hào)&密碼,回車。
回到 Network 面板,F(xiàn)5 刷新一下,沒(méi)有錯(cuò)誤了,暗自欣喜,繼續(xù)觀察中…
在 1.1min 后,SSE 連接斷開(kāi)了,可能是網(wǎng)絡(luò)波動(dòng),更何況還有重試機(jī)制兜底,不慌,繼續(xù)觀察…
在下一個(gè) 1.1min 后,連接又?jǐn)嚅_(kāi)了,看來(lái)這不是一個(gè)意外!

首先想到的是 Nginx 的配置不對(duì),趕緊檢查了下配置。
發(fā)現(xiàn)?proxy_read_timeout 1m;
?設(shè)置為 1 分鐘,肯定是它在搞鬼,于是改為 8h,一天斷開(kāi) 3 次連接,還可以接受吧!
繼續(xù)觀察,在 1.7min 后,又有序的斷開(kāi)連接了。

啊 ~~ ?? 這次又是什么原因呢???
瘋狂檢索后,試了一遍又一遍,各種解決方案,可惜對(duì)我都不管用!
比如,更改 Nginx 配置:
在源(上游)服務(wù)的響應(yīng)頭添加:
簡(jiǎn)直要瘋了,為了確定這是 Cloudflare 導(dǎo)致的問(wèn)題,我不得不在本地裝了個(gè) Nginx,用同樣的配置測(cè)試卻沒(méi)問(wèn)題。
好,既然知道問(wèn)題出在哪里,那就有解決的方法!
又一波瘋狂地檢索后,得知 Cloudflare 對(duì) SSE 支持并不好,但能很好的支持 WebSocket。
啊 ~~ 這?難道要我更換技術(shù)方案?
使用傳統(tǒng)的輪詢?
還是長(zhǎng)連接?
還是 WebSocket 呢?
還是不用 Cloudflare 了?
想想那漫漫重構(gòu)路,我頭大了!??
解決方案
就在即將放棄 SSE 方案之際,決定再好好翻閱 Cloudflare 官方文檔,終于功夫不負(fù)有心人,發(fā)現(xiàn)了這么一個(gè)描述。

意思是 Cloudflare 已成功連接到源服務(wù)器,但在 100 秒內(nèi)沒(méi)有響應(yīng),所以就發(fā)生超時(shí)了。
100 秒?換算起來(lái)不就是 1.666… 分鐘,四舍五入恰好就是 1.7 分鐘嘛?這與我遇到的問(wèn)題十分吻合??!
繼續(xù)往下看,對(duì)于這種耗時(shí)任務(wù),Cloudflare 也提供了幾種解決方案:
使用短輪詢來(lái)避免發(fā)生這個(gè)錯(cuò)誤
企業(yè)用戶可以設(shè)置?
proxy_read_timeout
?到 6000 秒關(guān)閉 Cloudflare 的邊緣網(wǎng)絡(luò)分發(fā),請(qǐng)求直接打到源服務(wù)器上
這些都不是我想要的解決方案,仔細(xì)想想,是不是在 100 秒內(nèi)有響應(yīng)就好了。
于是,我趕緊改造消息推送的代碼,啟動(dòng)一個(gè)時(shí)鐘來(lái)每隔 30 秒發(fā)送一個(gè) keepalive 的空消息。
部署上去后,繼續(xù)觀察,嗯 ~ 這一次,穩(wěn)了!
連接在 1.5 小時(shí)后才斷開(kāi),還是由于電腦休眠了的原因,不過(guò)這已經(jīng)足夠了。
只要能達(dá)到小時(shí)級(jí)別,再加上重連機(jī)制,這是可接受的結(jié)果。
總結(jié)
這是一次部署 SSE 消息推送到 Cloudflare 的經(jīng)歷,中途遇到很多很多問(wèn)題,做了很多測(cè)試,再一一排除。
每一次都到了瀕臨崩潰,更改技術(shù)方案的局面,好在最終克服了重重困難,得償所愿。
如果你的應(yīng)用場(chǎng)景與我相似,請(qǐng)求鏈路如下:
請(qǐng)?jiān)?Server 設(shè)置響應(yīng)頭?
X-Accel-Buffering: no
?來(lái)關(guān)閉 Nginx 響應(yīng)緩存每隔 30 秒做一次響應(yīng)來(lái)避免 Cloudflare 100 秒未響應(yīng)的超時(shí)錯(cuò)誤
好啦!今天就分享到這里,有任何問(wèn)題,歡迎在評(píng)論區(qū)交流。