雲端服務與經驗談 [4] 架構與供應商相依問題 by qrtt1 | CodeData
top

雲端服務與經驗談 [4] 架構與供應商相依問題

分享:

雲端服務與經驗談 [3] 虛擬機器與應用程式部署 << 前情

當你在架構中開始加入雲端服務的「成份」後,它提供與傳統地技術導入完全不同層次的「彈性」。在預算與使用需求皆滿足的情況下,你可以向上擴展整體服務的能力。不管是網頁的回應時間,或需要處理事務的吞吐量,都能因需要增減。這樣的變革能滿足許多技術上的需求,但需注意你的架構是否過份相依於服務供應商?高度相依的關係可能帶來的危害是成本控制的自由度降低。

例如經典的 Google App Engine 調整收費結構與價格事件,造成許多開發者付出更多的成本維持原先的服務並引發出走潮。Google App Engine(以下簡稱 GAE) 歸類為 Platform as a Service (PaaS)。當你看到一個雲端服務標上它是一個 Platform 的時候,請自動聯結上「它提到你一個特定的執行環境,僅能使用該供應商允許的軟體元件組裝你的服務」。

同樣是寫 Web 應用程式,你的儲存媒界在 GAE 上需使用它所提供的部分(並且允許的類別、函式呼叫都需符合白名單的範圍),只要依著效能指引手冊建議的方式實作,它會自動幫你處理好服務擴展性的問題。當你認同該服務的訂價策略與評估成本在可接受時是相安無事的,若出現了疑慮想要快速抽身變成一大難題。這個難題並不是無法避免的,但即使知道有許多技巧能減低依賴度,有時就是過於樂觀地認為「價格應該不會亂調才對」這正好應驗了俗語「千金難買早知道」。

以 GAE 的資料儲存方案為例,若是選用 Java 語言開發你有三種 API 可供選擇:

  • Low Level API
  • Java Data Objects (JDO)
  • Java Persistence API (JPA)

單純以使用的角度來說,第一個選項是讓你直接面對一組資料(若對 NoSQL 尚未接觸的人,可試著想成相近於關聯式資料庫的 Row),後二者是由 Object-relational mapping (ORM) 概念演化出來的 API,使用方式大概是利用 Java 類別,定義你資料存儲的結構,並利用 JDO 或 JPA 進行 CRUD 操作。

現實面對同一個目標有多種選擇時,當下最在意的因素會贏得最後的選項。它可能是效能,可能是可攜性,可能是團隊文化。以我們經驗過的情況,覺得使用 Low Level API 它的「包裝」最少,直達 Big Table 核心,在效能上是比 JDO 與 JPA 較適當的。同時這個選擇也考慮到不會將 JPA 操作關聯試資料庫思考慣性帶進 NoSQL 的操作。若對應至這回的主題「供應商相依問題」,就不會是這個選項!在 GAE 的手冊也提到

In addition to the low-level Java API, the SDK also supports two standard Java interfaces for data storage, Java Data Objects (JDO) and the Java Persistence API (JPA), which you can use to manage and enforce the structure of entities. These interfaces let you model your data objects as Java classes, making it easier to port your application between the App Engine Datastore and other data storage solutions.

由於 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 手冊與範例供人取用,並馬上能看到效果。在未謹慎使用的情況下,程式的品質會被未經整理的「剪下」「貼上」所污染,最終看到的相依不可拔只是問題的表象罷了。

後續 >> 雲端服務與經驗談 [5] 雲服務的成本控制與優化

分享:
按讚!加入 CodeData Facebook 粉絲群

留言

留言請先。還沒帳號註冊也可以使用FacebookGoogle+登錄留言

關於作者

目前在一家網路應用軟體公司擔任開發工作,對多媒體處理與雲端應用充滿興趣,工作之餘亦常整理開發經驗分享於網路或於社群活動時進行分享。

熱門論壇文章

熱門技術文章