2018年6月15日 星期五

簡易台股過濾器 - 初試 Google Cloud AppEngine、Datastore

起念是想在「不負擔任何費用」的前提之下,建立能有動態內容、提供可互動 WEB API 的網頁伺服環境,即 Server 端必需能支援 CGI。在各大雲端平台上一陣走馬看花之後,最後看上了 Google Cloud AppEngine、以及用作 Database 的 Google Cloud Datastore。兩者皆有常態性地低用量免費方案,只要用量不超過某限額,是不用付錢的,很適合作為個人練兵場、或驗證一些概念性的想法。

於是乎我把自己平時為了方便選擇存股目標而寫的台股過濾器,移植到 AppEngine 中,提供了簡易的網頁前端以方便操作。

台灣上市股票過濾器

原始碼部分
台股歷年股利、最近成交價爬蟲 (爬出數據塞進 Datastore 中)

置於 AppEngine 中的部分 (含網頁前端內容、後端 WEB API 實作)






Google Cloud AppEngine 說穿了就是一種 Micro Framework 的概念延伸。作為網頁動態內容的提供者,你不再需要煩惱怎麼架設 Web Server、設定 CGI 權限這些瑣碎的細節。唯一要設定的是「URL 中的哪個 Path 會對應到執行你的哪個函數」 (稱為設定 "Route" ),以及提供函數實作 -- 想辦法組出適當的網頁內容即可 (稱為 "Request Handler")。

幾乎每種語言都有成熟的 Micro Framework 實現 -- Crow (C++), Slim (PHP), Flask (Python), ASP.NET Core (C#)。分別點進它們的網頁中看幾個例子,再對照更早時期的 CGI 開發方式,就能理解 Micro Framework 的訴求了。

Google Cloud AppEngine 裡又細分為兩種運作環境: Standard、和 Flexible

Standard 環境中,真的只需要把 Route 與 Request Handler 上傳,之後一切都不需操心了。AppEngine 會依據 Request 的數量、回應時間來自動調配實際上運行 Request Handler 的機器數 (Auto Scaling)。但也因為要「在不屬於你的機器上執行你的程式」,所以 Standard 環境中必然有些限制,例如限制可用的第三方函式庫、不允許任意讀寫本機檔案、有限的對外連絡並且不可使用 Private IP 等等。採用的 Micro Framework 也是由 AppEngine 提供、由 Google 內部打造的版本,並不是前述提到的任何一種。

而 Flexible 環境則是自動透過 Google Compute Engine 開設 VM 來用為執行你的程式的宿主機器。因為是由你來替這些 VM 付費,所以自然就不像 Standard 環境有那麼多限制了。你可以指定使用前述的 Micro Framework、任意客制化環境中安裝的套件、透過 Private IP 訪問你其它架設的 VM 等等。具體的比較表在此

然而只有 Standard 環境有免費用量的方案。因此我是採用 Standard 環境 (Python 2.7)。

受限於 Standard 環境中可用的第三方函式庫,能用選擇的 Database 方案也不多,要嘛就是 MySQL (或 Google Cloud SQL)、不然就是 Google Cloud Datastore。因為我過去對 NoSQL 類 Database 的經驗較多,很自然就選擇了 Google Cloud Datastore。下面說說心得:

首先令人有點苦惱的是 Google Cloud Datastore 目前有兩種不同的 Python 函式庫。這是因為 Datastore 最早不是獨立的服務,而是特別為了 AppEngine Standard 環境量身打造的,曾經只有在 AppEngine 中能使用,後來才晉升為獨立的服務。時至今日就演變成:在 AppEngine 中使用的是 ndb 這個 Datastore 函式庫,而在 AppEngine 以外則要使用 google-cloud-datastore 這個函式庫 (可用 pip 安裝)。其它 Standard 環境中支援的程式語言可能也面臨類似的情況。

在我的台股過濾器內,透過爬蟲去取得最新股票資料的部分是獨立的 Python 程式,它是運行在 AppEngine 以外,因此它使用的是 google-cloud-datastore。而 Web API 中負責由 Datastore 提取數據的部分因為是運行在 App Engine 中而必須用 ndb。所幸兩者的差異還算可以接受 (但絕對不是無痛)。

其它幾點值得記錄的「特色」:

  • Datastore 中每筆資料 (稱為 entity) 的寫入必需是完整寫入。也就是說,沒辦法只更新某個 entity 上的某一欄 (稱為 property)。所以如果想更新某一欄,你還是得把整個 entity 抓下來,把那一欄的值改掉,再整個上傳。
  • Datastore 預設會對 entity 上的每個欄位都建立索引。以台股過濾器為例,Datastore 實際的資料用量中,約有 90% 是索引資料,實際資料量只佔 10%。在估算免費用量額度時請務必考慮這一點。
  • Datastore 中進行 Query 時,最多只能對一個索引欄位進行「等於之外的比較操作」(>, >=, <, <=)。如果想對多個欄位進行「等於之外的比較操作」,請先用第一個比較操作來 Query,然後再手動對取回的結果套用後續的比較操作。
  • Datastore 中也支援「將多個動作打包成批次」的功能,但這個批次動作預設是「完全異步的」 (且有上限最多 500 個 entities)。也就是說,當傳送批次動作的 API 返回後,根本也不知道動作到底完成沒。實際測試的結果,滿滿一個含有 500 筆 entities 的批次更新動作送出之後,要等待數秒才能更新完畢。這與 SQL、mongodb、Redis 裡所預期的「批次」定義是有出入的。若想要確保動作的完成,要改包成 Transactions 才可以。
  • Datastore 有提供「模擬器」-- 可在本地運行測試用的 Datastore 伺服器,如此一來無論是 ndb 或 google-cloud-datastore 都可以改向測試用的 Datastore 伺服器進行操作,以便驗證功能。

沒有留言:

張貼留言