2020年11月14日 星期六

[個人網站開發] 個人投資頁面 - 新增即時股價更新 (使用SignalR + finnhub.io資料源)

最近在研究SignalR這個微軟開發的通訊函式庫, 不知道的人可以參考微軟介紹: SignalR 簡介

簡單POC一下後感覺蠻好用的, 唯一可惜的地方是SignalR client端的程式語言支援的有點少, 官方支援的有JavaScript, .Net, Java, 非官方 or 實驗性質的則有C++跟Swift, 只考慮官方支援語言的話就只能寫.net或JAVA的用戶端程式或網頁上使用了。

既然學了一個新東西, 除了POC以外當然會想做些實際應用了, 目前想到自己需要用到SignalR的地方, 大概就是需要即時更新內容的網站, 以我自己網站來說, 大概就只有投資頁面的個股報價資訊, 要能即時更新股價資訊, 主要有兩個問題要處理:


1. 網頁前端需要即時跟後端取得股價資料, 如果用ajax polling不斷問後端server有無更新股價的方式會極度浪費資源, 應該要使用支援雙向傳輸的通訊方式(e.g. websocket), 只有在後端server發現真的有股價需要更新時, 在傳送最新的股價給前端。 

2. 後端必須即時取得股價資料, 在通知前端更新, 而後端的股價的數據源要從哪取得就是個問題了, 總不可能一直polling打request到YAHOO財經的網站上要資料, 這樣太浪費電腦資源得到的數據也太慢, 還有被ban ip的可能。

上述兩個問題的話, 1可以直接用SignalR解決, 2的話可以參考我之前介紹的finnhub.io網站:

[美股API推薦] finnhub.io介紹 - 美股即時股價API

這網站提供50個個股報價的websocket訂閱, 而且取得的報價是以tick為單位, 可以即時取得個股的報價資訊。

既然前後端通訊跟數據源的問題都解決了, 再來就是實作面了! 最簡單的做法其實可以寫個service裝在自己的Azure VM上, 在用websocket接finnhub.io的個股報價, 之後就把得到的報價用SignalR轉給架好的SignalR hub, 前端網頁的部分一樣也是連到這台SignalR hub, 就可以即時得到股價資料了。

整個網站架構大致如下, SignalR的部分是這次新增:


1. 網站本身架在github page上, 這樣所有靜態資源的流量都算在github身上, 比起自己的Azure最低規格VM能快上許多。

2. 需要後端資料的部分則由前端發ajax打給自己的Azure VM server, server回傳加上Access-Control-Allow-Origin  header 避免CORS瀏覽器阻擋問題。

3. 所有個股基本資料, 健康分析, 個股新聞跟SEC文件等等這些不需要即時而且要花費許多時間的工作透過Github Action每日觸發做批次工作, 然後在落地成檔案讓網站直接存取。

4. 個股即時報價部分就像上述提到的, 用websocket接finnhub.io數據源, 在透過SignalR傳到前端, 另外因為自己的VM規格已經夠爛了, 一直接finnhub.io怕會讓server loading更重, 畢竟finnhub.io傳過來的是tick資料, 所以這部分決定拉出來部屬到免費規格的Azure function, 讓Azure function去接finnhub.io數據源, 然後把Azure function接到的報價資料統整起來, 只送股價有更新的資料, 並且每1.5秒統一把所有有更新的個股報價一起送過來, 這樣可以省自己Azure VM的大量網路流量, 不用不斷去接tick單位等級的資料。

實作完後成果如下, 因為是透過SignalR統一通知所有連線的前端需要更新的股價, 可以看到跟client端主動ajax polling不一樣, 每個client端都是同時間收到更新後的股價:


實作網站(考慮到負載問題, 最後決定只做在持有清單上, 加上關注清單跟每日選股會接近finnhub.io免費版上限...):



雖然做完了, 不過在實際體驗成果時發現還有不少問題:

1. finnhub.io免費帳號的websocket很不穩:
自己測試的情況是, 如果是美股剛開盤的前兩小時左右, finnhub.io的websocket會有很大機率根本連不上, 看起來是流量大時他們就不管免費帳號了, 雖然合理不過還是頗無奈...。

而再來就是websocket要是運氣好真的連上了, 只要有更新股價finnhub.io就會主動送報價資訊過來, 可是維持連線很久之後, 會有一定機率finnhub.io只會送{"type":"ping"}, 而只有重新斷線在連線後才會恢復正常。

基本上上面這兩個問題我也只能針對連不上還有過多ping的情況加retry, 不過基本上出問題有時是以小時為單位, 就算有retry也沒用, 免費的數據源也沒辦法挑剔就是了QQ

2. 免費版Azure function掛載的Azure App Service會自動休眠:
基本上Azure function是觸發後工作, 算錢是算執行時間 * 資源使用量, 而我的作法是Azure function觸發後用websocket連線finnhub.io後就直接回傳結果, 之後就不斷在背景程式下繼續取得報價資料, 所以基本上Azure function立刻就跑完根本不用錢, 之後的工作都是在背景下讓Azure App Service跑, 不過Azure App Service只要偵測Azure function跑完後沒工作了, 過陣子就會自己shutdown (時間不一定, 目前觀察大概20分鐘左右), 這問題其實可以參考stackoverflow這篇解決:


透過www.freshworks.com每五分鐘都去ping Azure App Service, 這樣他就不會睡覺, 而且因為不是觸發Azure function所以不用算錢, 另外免費版的Azure App Service雖然有1天60分鐘CPU的使用量, 可是好像只有搭載web service程式才會有CPU使用量的計算, 只使用Azure function的情況則是沒這問題, 不過實際上用這種方式到底穩不穩還不明, 還要多觀察測試才能知道。

這次大概就寫到這, 公司開始忙起來了應該不太有餘裕繼續用下班搞新玩意, 真希望能早點財富自由哪~。

沒有留言:

張貼留言