設計Web應用程序時要注意可伸縮性

 Max Indelicato是一位軟件開發主管和前首席軟件架構師,他最近發表了一篇關於如何設計具備可伸縮性的web應用程序 的文章。他提出要選擇正確的部署和存儲解決方案,選擇可伸縮的數據存儲和模式,並且使用抽象層。

適合工作的工具

Indelicato的第一個建議是“爲工作選擇正確的工具”,想要達到這個目的,就要選擇下列架構解決方案中的一種:

  • 使用雲部署解決方案
  • 使用可伸縮的數據存儲解決方案,像MongoDB、CouchDB、Cassandra或者Redis。
  • 添加高速緩存層,像Memcached。

他認爲在開始開發應用程序的時候,這些解決方案並不是必須的,但是在開始時就選擇可伸縮的數據存儲解決方案是很明智的,因爲那會避免之後再進行切 換。將應用程序部署到雲中會爲我們帶來一些好處,特別是對於創業公司來說,因爲他們無法準確地確定他們的應用程序在啓用之後會有多少人使用。將應用程序部 署到雲中之後,當需求增加時,就可以讓應用程序以優雅的方式進行伸縮。很多軟件架構師都講述了他們不得不對應用程序進行擴展的事件,其中他們會引入高速緩 存層,那會解決大部分問題。但是我們應該在設計階段就考慮相應的解決方案。 這樣在之後就很容易實現了。

可伸縮的數據存儲

接下來,Indelicato建議選擇支持分區、複製並且有彈性的數據存儲,包括以下幾種: MongoDB、Cassandra、Redis、Tokyo Cabinet、Project Voldemort,或者選擇MySQL作爲關係型數據庫。這是很必要的,因爲不管怎樣,在應用程序的生命週期中,分區都是必要的。對於可伸縮性來說,分 區並不是必需的,但是對於“確保高可用性”就是必需的。靈活性可以讓我們快速地增加更多的節點,這可能是出現流量峯值的時候,也可能是“由於硬件故障或升 級、大型的伸縮模式的變更或者任何需要讓節點下線的情況下,需要對節點進行維護的時候。”

可伸縮的數據模式

Indelicato建議創建一種模式,從而讓我們可以很容易地進行數據sharding,他還給出了下面的臨時組件的例子,User和UserFeedEntry:

Collection (or Table, or Entries, etc) User
{
    UserId : guid, unique, key
    Username : string
    PasswordHash : string
    LastModified : timestamp
    Created : timestamp
}
 
Collection (or Table, or Entries, etc) UserFeedEntry
{
    UserFeedEntryId : guid, unique, key
    UserId : guid, unique, foreign key
    Body : string
    LastModified : timestamp
    Created : timestamp
}

 

然後他建議根據UserId進行分區:

通過根據UserId字段對User集合和UserFeedEntry集合分區,我們會將兩種相關的數據塊放在同一個節點上。所有UserId爲 xxx-xxx-xxx-xxx的UserFeedEntry數據和UserId爲xxx-xxx-xxx-xxx的User數據會被包含在同一數據片段 中。

爲什麼這是可伸縮的呢? 因爲我們對於這個應用程序的需求完全是針對數據的分發的。當每個訪問者訪問User的信息頁面時,系統會向數據片段發出請求以獲取User欄顯示用戶的詳 細信息,然後再向同一個數據片段發出請求以獲得用戶的UserFeedEntries。這兩個請求中,一個會獲得一條數據,而另一個會獲得多條數據,而這 些數據都包含在同一數據片段中。 假設在一天之中對大多數用戶的信息都有相同次數的訪問,那麼我們已經設計了可伸縮的模式,它會支持我們的web應用程序的需求。

使用抽象層

Indelicato的最後一條建議是使用下述抽象層中的一種,但不僅限於這些: 元數據庫(Repository)、緩存和服務。當創建元數據庫層的時候,他建議:

不要以針對你所抽象的數據存儲特有的方式來爲方法命名。 例如,如果你抽象的是關係型的數據庫,一般我們會爲了執行SQL查詢和命令而定義Select()、Insert()、Delete()、 Update()函數。不要這麼做。 相反,應該讓你的函數名不那麼專門化,可以使用Fetch()、Put()、Delete()和Replace()。這會確保你更好地遵循元數據庫模式, 並且當你需要切換底層數據庫的時候,工作會更簡單。

如果可能的話使用接口(或者抽象類等等) 將這些接口傳遞給應用程序中更高的層,這樣你永遠不會直接引用元數據庫的特定的固有實現。這對於構建和單元測試也是非常棒的,因爲你可以編寫其他固有實現,它們會預先帶有與測試案例相關的數據。

將所有針對存儲的特殊代碼封裝到一個類(或者模塊等等)中,真正的元數據庫會引用或者繼承它。只在每個函數中放置針對存取函數所必需的細節(查詢語句等等)。

時刻要牢記,並非所有元數據庫都需要抽象相同的數據存儲解決方案。只要你願意,你可以將User存儲在MySQL中,而將 UserFeedEntries存儲在MongoDB中,元數據庫要以這樣的方式實現,它們支持這麼做而不需要付出太多代價。之前的三點建議也間接地有助 於我們做到這一點。

Indelicato說,對於高速緩存層,在開始時他經常會使用“簡單的頁面(或者視圖等等)級別的緩存或者服務層的緩存,因爲這是兩個不會經常發生狀態變更的區域。”

Indelicato認爲需要對服務層進行足夠的抽象,這樣當需求增加時,我們可以很容易地從服務的內部實現切換到進程之外的實現。

有些人認爲在構建應用程序的時候不需要考慮可伸縮性問題,因爲那會在必要的時候得到強調。 但是如果我們想要從開始就考慮可伸縮性,你還有什麼好的建議呢?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章