iT邦幫忙

2026 iThome 鐵人賽

DAY 19
0
Modern Web

重寫一套比我還老的系統:21 歲的校園文字廣播系統系列 第 19 篇

Day 19|我去,感覺做不完了。等等,你是鬼吧!

  • 分享至 

  • xImage
  •  

今天第 19 天了,我們剩下 11 天的時間了。

有點可惜,但似乎是沒有機會把所有的思考過程全部寫出來了呢。

真可惜,時間真的過得好快。
話說回來,我們真的能做完嗎?

不知道囉,盡人事,聽天命吧。

做不完不就證明我這次時間規劃可能有問題嗎?

小問題啦,就算做不完這也會是一個很難得的體驗。

不對阿,廢話怎麼這麼多,進正題。

頭好痛,真複雜。

挫屎,加了新的即時通,整個系統的複雜度對我而言有點高了。

我決定了,先砍掉,留到以後再做。

你的以後是多久以後?留著明年繼續打鐵人賽嗎?

超好笑,那也要我有空再說。

希望明年這個時候還有精力吧,寫鐵人賽真的好累。

先回到主題吧。

新的廣播怎麼被「廣播」?

蛤?何意味?

你知道的吧。

原本的廣播就像一間吃到飽自助吧,你的目標是把全部的食物都吃一遍。

並出去跟大家分享都有哪些餐點,且餐點會臭酸。

當你想看新廣播時,就去跟老闆說:「老闆,給我來一份我目前能點到的所有餐點!」(這邊假設會隨機刷新新餐點)

雖然做法十分簡單暴力,但你浪費的食材是真的多。

會員制餐廳(喜)。

惡臭(悲)。

總之,要處理這問題也不難,最直覺的解法就是請一位服務員來,在有新餐點時讓服務員給你上一份就好了。

但如此一來又會衍生出許多問題如:

  • 哪些菜已經上過了?
  • 出餐是由廚師記錄還是服務員紀錄?
  • 服務員怎麼知道每桌客人已經吃到哪裡?
  • 客人中途跑出去再回來,漏掉的菜怎麼辦?
  • 同一個會員開了兩桌,又該怎麼處理?

怎麼吃個飯這麼麻煩?這些聽起來都是奧客(惱)

那這樣好了,讓客人自己跟櫃台說他最後吃的那道菜是什麼不就好了嗎?

哇靠!你是天才阿!

這算一種自誇嗎?
我怎麼感覺不到天才在哪?

有,你把整個問題難度降低了不只一丁半點。

有差很多嗎?

就這麼說吧,我幾乎已經想好整個實作流程了。

太扯了吧?

你應該知道兩種上餐方式分別對應什麼更新方式吧?

輪詢跟推播?

y,這兩個方式在設計和實作上的複雜度完全不是一個等級的。

用推播我本來要搬出一整個 class 寫一個 Manager 來管理所有有的推播連線。

用輪詢我不用搞出一整套設計,只要用一點點小巧思就能完成了。

再利用資料庫查詢的優化,每次只撈上次之後的新廣播,也不用一直把整桌菜重新端上來。

沒有會員制餐廳了(悲)。

何異味???但新設計還是可以叫他會員制餐廳啦(惱)。

(喜)

那一點點小巧思是什麼?

所以到底要怎麼做?

總不能真的讓使用者每隔幾秒填一次:「我看到第 114514 則廣播了」吧?

當然不是。

還記得我們前面說的嗎?

每一位客人只需要記住一件事情:

「我最後吃到哪一道菜?」

假設現在伺服器裡有五則廣播:

101
102
103
104
105

而我的瀏覽器已經拿到了:

101
102
103

那下一次輪詢的時候,我根本不用再跟伺服器要一次 101 ~ 105。

只需要告訴它:

我最後拿到 103,還有新的嗎?

後端收到之後,就可以去資料庫裡找:

id > 103

最後只需要把:

104
105

丟回來。

如果沒有呢?

那就什麼都不要給我。

過幾秒再問一次就好了。

這就是我打算使用的 Incremental Polling(增量輪詢)。

與其每次都重新同步所有資料,不如讓 Client(客戶端)告訴 Server(伺服器):

「我已經同步到哪裡了?」

Server 再把後面的資料補給它。

等一下。

那第一次進來的人勒?

跟老闆說:

我一道都沒吃過。

好可憐。

……

每桌吃到哪裡都不一樣

這個做法還有另一個我很喜歡的地方。

假設今天我同時開了三台裝置:

手機:最後拿到 105
平板:最後拿到 102
電腦:第一次開啟

完全沒關係。

手機可以說:

我拿到 105 了。

平板可以說:

我只拿到 102。

電腦則是:

我什麼都沒有。

Server 根本不需要記住:

蘇某的手機 → 105
蘇某的平板 → 102
蘇某的電腦 → NULL

每個 Client 自己記住就好了。

等到下一次要資料時,再把自己的進度一起帶上來。

這個「目前同步到哪裡」的位置,我們就可以把它當成一個 Cursor(游標)。

所以整件事情最後其實變得非常單純:

Client 保存 Cursor
        ↓
詢問 Cursor 後有沒有新廣播
        ↓
Server 查詢新資料
        ↓
回傳
        ↓
Client 更新 Cursor
        ↓
過幾秒再來一次

所以你剛剛講得一副發現新大陸的樣子,結果就這樣?

對啊。

天才在哪?

我收回,你似乎真的是傻子。

。

關掉網頁也沒關係

而且既然 Cursor 是由 Client 自己保存的,那重新整理其實也沒什麼特別的。

例如我現在已經同步到:

105

那瀏覽器只要把這個 105 保存下來。

就算我現在直接把網頁關掉,這段時間 Server 又出現:

106
107
108

下次打開網站時,我還是可以直接告訴 Server:

我上次拿到 105。

然後把:

106
107
108

補回來。

也就是說,Server 根本不用特別維護:

「這個使用者現在看到哪裡?」

更不用管同一個使用者到底開了幾台裝置。

每台裝置管好自己就好了。

但這樣不會漏資料嗎?

等等,你這個設計是不是有洞?
假設我已經拿到 105 了,但後來 87 被修改了。
我之後不是永遠只會問 105 後面的東西?
那 87 的修改勒?

對。

如果今天我們處理的是一個資料可以隨意修改、刪除的系統,那單純記住最後一筆資料的 ID 確實不夠。

因為 Client 現在的 Cursor 如果是:

105

下一次它只會去找:

id > 105

那前面的 87 就算被改得面目全非,Client 也不會知道。

那你這個設計不是炸了嗎?

還好。

因為我們很幸運。

這是廣播。

廣播一旦送出去,就不應該偷偷修改,更不能偷偷撤回。

如果真的寫錯了呢?

那就再發一則更正。

#105
明天下午 3:00 於活動中心集合

#106
【更正】上一則廣播集合時間有誤,
應為明天下午 4:00。

這樣不只能讓收到原廣播的人知道內容有更正,也不會讓一則已經發出去的廣播莫名其妙變成另一個東西。

所以對我們來說,廣播的資料其實很接近 Append-only(只追加):

101
102
103
104
105
↓
新增
↓
106

新的廣播只會繼續往後加,前面的資料則不會突然被修改或消失。

等等,那你前面說餐點會臭酸又是什麼?

你不是說資料不會消失嗎?

這是兩件事情。

餐點臭酸,不代表餐點憑空消失了。

假設老師在下課時發了一則:

請資訊股長下節上課前來辦公室拿東西。

結果我們的系統直到下一節課都上完了,才把這則廣播送到資訊股長手上。

資料有被修改嗎?

沒有。

資料有被刪掉嗎?

也沒有。

那系統有正常完成工作嗎?

……

好像也沒有。

對。

因為這則廣播雖然還是完全正確的資料,但它已經沒有用了。

餐點臭酸了。

所以不能送太慢?

對。

這也是為什麼我們雖然決定不用 WebSocket,卻不能:

反正是輪詢,那一小時問一次好了。

Polling interval(輪詢間隔)還是得控制在合理範圍內。

我們真正需要的並不是:

「新廣播發布的瞬間就必須抵達。」

而是:

「新廣播必須在失去時效性以前抵達。」


上一篇
Day 18|即時通負責溝通,那廣播負責什麼?
系列文
重寫一套比我還老的系統:21 歲的校園文字廣播系統 共 19 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言