
雲端服務與經驗談 [4] 架構與供應商相依問題
雲端服務與經驗談 [3] 虛擬機器與應用程式部署 << 前情 當你在架構中開始加入雲端服務的「成份」後,它提供與傳統地技術導入完全不同層次的「彈性」。在預算與使用需求皆滿足的情況下,你可以向上擴展整體服務的能力。不管是網頁的回應時間,或需要處理事務的吞吐量,都能因需要增減。這樣的變革能滿足許多技術上的需求,但需注意你的架構是否過份相依於服務供應商?高度相依的關係可能帶來的危害是成本控制的自由度降低。 例如經典的 Google App Engine 調整收費結構與價格事件,造成許多開發者付出更多的成本維持原先的服務並引發出走潮。Google App Engine(以下簡稱 GAE) 歸類為 Platform as a Service (PaaS)。當你看到一個雲端服務標上它是一個 Platform 的時候,請自動聯結上「它提到你一個特定的執行環境,僅能使用該供應商允許的軟體元件組裝你的服務」。 同樣是寫 Web 應用程式,你的儲存媒界在 GAE 上需使用它所提供的部分(並且允許的類別、函式呼叫都需符合白名單的範圍),只要依著效能指引手冊建議的方式實作,它會自動幫你處理好服務擴展性的問題。當你認同該服務的訂價策略與評估成本在可接受時是相安無事的,若出現了疑慮想要快速抽身變成一大難題。這個難題並不是無法避免的,但即使知道有許多技巧能減低依賴度,有時就是過於樂觀地認為「價格應該不會亂調才對」這正好應驗了俗語「千金難買早知道」。 以 GAE 的資料儲存方案為例,若是選用 Java 語言開發你有三種 API 可供選擇:
單純以使用的角度來說,第一個選項是讓你直接面對一組資料(若對 NoSQL 尚未接觸的人,可試著想成相近於關聯式資料庫的 Row),後二者是由 Object-relational mapping (ORM) 概念演化出來的 API,使用方式大概是利用 Java 類別,定義你資料存儲的結構,並利用 JDO 或 JPA 進行 CRUD 操作。 現實面對同一個目標有多種選擇時,當下最在意的因素會贏得最後的選項。它可能是效能,可能是可攜性,可能是團隊文化。以我們經驗過的情況,覺得使用 Low Level API 它的「包裝」最少,直達 Big Table 核心,在效能上是比 JDO 與 JPA 較適當的。同時這個選擇也考慮到不會將 JPA 操作關聯試資料庫思考慣性帶進 NoSQL 的操作。若對應至這回的主題「供應商相依問題」,就不會是這個選項!在 GAE 的手冊也提到:
由於 JPA 與 JDO 皆在 Java Specification Request (通常縮寫為 JSR) 有正式的釋出文件,也有許多實作品,理想上只在資料儲存的部分,不使用 GAE 特定的功能就能抽換掉底層 Data Storage。透過這一個間接操作 GAE Data Storage 的 API,減輕對 GAE 直接的依賴。理論上選用 JPA 比起 Low Level API 具有較高的可攜性,實際上遇到的問題是不同實作(GAE 與其他資料庫系統)可能有不同的行為與能力。 典型的情況就是 GAE 上的 JPA 操作的是 NoSQL 儲存系統,以使用關聯式資料庫系統的觀點來評估它,當然有著 NoSQL 優點與其極限。我們無法期待它有與關聯式資料庫提供好用的聚合函式,讓你方便的加總或計算平均值。條件篩選上,需在有建立索引的基本條件下才能運作。說明這些限制不是打擊使用者對於可攜性的期待,僅是闡明二套不同資料庫系統轉移時的困難。若將視角切回同樣是 NoSQL 的實作品,例如是抽換成 Cassandra,那麼操作模式上的不一致之處,就相對比關聯式資料庫好處理。 經過 GAE 調價事件,開發者對使用 PaaS 類型的服務採取謹慎的態度,並試著減少對它的依賴,漸漸朝向 IaaS 的服務靠攏。這就像選擇居住的城市一般,在一座城裡因為物價上漲,賺得的薪水無法支持消費時,只能被迫搬家一樣,搬往一個更適合自己居住的城市。在新的城市會有新的居住問題!服務都移往 IaaS 就省事了嗎?系統維護就需各憑本事不說,即使在 IaaS 類型的服務也是會有相依問題。您必需意識到 IaaS 不是只有虛擬機服務。通泛地說,IaaS 提供的是運算資源(虛擬機、MapReduce)、網路(流量、管理設備)、存儲體(關聯式資料庫、NoSQL 資料庫、檔案庫)。 相依性問題它會自然產生,先來談談因客觀條件產生的相依問題,它們是需要選擇並接受的,例如 CDN 服務。當你使用 Amazon CloudFront 時,在多數的情況下會覺得它的品質是可以接受的,一旦過了一抹淺淺地海峽事情就會有點變化。你必需注意所選用的供應商是否會因國家(或特定地區)有品質不一致的問題。因此,對於在中國地區選用 CDN,顯然 CloudFront 不是較理想的選擇。以同類型非本地廠商來說,選用 Azure 的 Storage 啟用 CDN 的情況會比 CloudFront 要穩定些。更保險的情況是直接使用當地的 CDN 服務商或當地的雲供服務供應商,例如阿里云。 在實作上我們會面臨到如何同時整合 AWS、Azure 與阿里云或其他服務商 API 整合的問題。這其實簡單的設計問題:適當地界定第三方套件的邊界。不管你用上何種設計模式,總之得避免直接使用第三方 API。我們在這不探討各種候選的 Design Pattern,我們直接示範使用介面與類別做出一個「間接層」。以操作儲存事務這件事,設計不需要完整包含第三方套件的功能,應著重在自己本身的需求。舉例來說,我定義一組 Stroage 介面: public interface Storage { public boolean put(String key, InputStream content); public String toUrl(String key); } public enum StorageType { AWS_S3, AZURE, ALI_CLOUD, DEV } 所有的細節都藏在介面之下,要透過 Factory 建立實際的操作物件: StroageFactory stroageFactory = new StroageFactory(); Storage storage = stroageFactory.create(StorageType.AWS_S3); if (storage.put("MY_KEY", new FileInputStream("sample.txt"))) { System.out.println(storage.toUrl("MY_KEY")); } 這樣一來操作 AWS S3 的細節就不需知道,甚至連建立物件都委託 IoC Container 管理,在部署時針對不同地區的 Server 設定不同的參數。透過間接層第三方套件的界邊由 Storage 介面一分為二。 Storage storage = stroageFactory.create(StorageType.DEV) 在測試 Server 上的 Storage 實作,也許就是只是存檔到 httpd 管理的目錄罷了。這樣你的程式可以輕鬆在正式環境與測試環境之間轉移,由於設計上已「內建」隔離修改的概念,要製作方便開發者在自己電腦上使用的環境也是輕而易舉的。 對於已將「隔離修改」的概念融入平日工作的團隊來說,處理相依性問題是輕而易舉的。撰寫此文是為了點出這個實作上的陋習。適度隔離無論是否使用雲端服務 API 都是該做的事,但未隔離修改的情況,會在輕易取得範例程式碼的情況下更加惡化。雲端服務供應商都相當盡力地使他們的 API 手冊與範例供人取用,並馬上能看到效果。在未謹慎使用的情況下,程式的品質會被未經整理的「剪下」「貼上」所污染,最終看到的相依不可拔只是問題的表象罷了。 |