深入 Grails 與 Cloud Foundry

工程 | Peter Ledbrook | 2011年4月21日 | ...

我之前的文章中,我向您展示了使用對應的外掛程式將 Grails 應用程式部署到 Cloud Foundry 是多麼容易。 希望這能引起您的興趣,讓您準備好看看更複雜的 Grails 應用程式,該應用程式展示了 GORM 外掛程式的強大功能,並擴展了 Cloud Foundry 服務。 如果您還沒有 Cloud Foundry 帳號,請耐心等待。 對於公告的回應非常熱烈,因此需要一些時間才能處理完積壓的請求。

GrailsTwitter

簡單的 Twitter 範例幾乎已成為 Grails 範例應用程式的標準,因此另一個版本是為 Cloud Foundry 開發的也就不足為奇了。 您可以在 GitHub 上找到程式碼,以及其他 Cloud Foundry 範例,您也可以測試 應用程式的實例,該實例已在雲端上執行。

在本例中,我們混合使用了資料儲存,其中 MongoDB 儲存狀態訊息和標籤,SQL 資料庫用於使用者資訊(因為我們正在使用 Spring Security),而 Redis 用於快取整體標籤資訊


GrailsTwitter 的資料模型

PersonAuthority領域類別是標準的 Spring Security,因此我不會再多說,除了它們是由 Hibernate 對應的,因此在部署應用程式時需要 Cloud Foundry MySQL 服務。

更令人感興趣的是Status領域類別。 透過將靜態mapWith屬性新增至類別,我們可以確保其例項儲存在 MongoDB 而不是 SQL 儲存中

package org.grails.twitter

import org.grails.twitter.auth.Person

class Status {
    static mapWith = "mongo"
    static transients = ["author"]
	
    String message
    Long authorId
    List<String> tags = []
    Date dateCreated
	
    Person getAuthor() {
        return Person.get(authorId)
    }
}

除此之外,它看起來像一個非常標準的 GORM 領域類別。 那麼,我們如何將儲存在 MongoDB 中的狀態訊息連結到儲存在 SQL 資料庫中的使用者呢? 在這種情況下,我們不能使用標準的 GORM 一對一關係,因此我們改為在狀態訊息中明確儲存使用者的 ID,並新增一個暫時性、唯讀的author屬性,以便輕鬆存取關聯的Person實例。 當您知道如何操作時,就這麼簡單!

您還可以注意到,標籤並未儲存為單獨的領域實例(標籤是狀態訊息中以 '#' 開頭的任何字串)。 相反,每個Status將其標籤列表儲存為純字串 - 這是 MongoDB 網站上建議的方法。 這表示儲存和檢索狀態訊息非常便宜,但這也提出了一個重要的問題:您如何找出系統中有哪些標籤,以及每個標籤出現了多少次? 畢竟沒有「標籤」表可以查詢,這通常是您在關聯式資料庫中會有的。

這就是 MongoDB 對 MapReduce 函數支援的用武之地。 Map 函數分解所有狀態訊息中的所有標籤,然後 Reduce 函數將它們與每個標籤的總計數聚合在一起。 您可以在TagService.cacheTags()方法中看到相關程式碼。 了解 MapReduce 可能需要一些時間,但它非常適合平行運算。 但是,對於單個 MongoDB 實例,此操作並不是非常快。 因此,快取結果是有意義的。

快取

每當我想到快取時,我的想法都會立即轉向 Ehcache。 它就像 Java 平台上開源快取的祖父。 即使是 Grails 的 Spring Cache 外掛程式目前也硬式編碼為使用它。 那麼,當我們的應用程式部署到雲端時,我們可以使用它嗎? 如果我們只打算擁有一個應用程式實例,那麼當然可以。 您只需要設定磁碟儲存的位置,儘管這並非易事,我稍後會談到檔案系統存取。 但雲端部署的關鍵優勢之一是能夠快速建立多個實例來處理負載,在這種情況下,Ehcache 並不是一個選項。

快取通常是簡單的鍵值儲存,因此目前 Cloud Foundry 上提供了一種替代方案,即 Redis 服務,而其他快取服務(例如 vFabric Gemfire)正在開發中。 在 GrailsTwitter 應用程式中,我們列舉了標籤及其總計數,並將結果儲存在 Redis 排序集合中。 這是透過交易完成的(透過redis.multi()/.exec()),以確保集合以原子方式更新 - 這很重要,因為可能會有許多並行請求進入標籤列表。 作為額外的好處,排序集合確保我們始終按照「總計數」的順序檢索標籤。

當然,由於 Redis 是一個獨立的實例,您可以擁有任意數量的應用程式實例:它們都將使用相同的 Redis 服務。 該應用程式也示範了如何直接使用 Redis 而不是透過 GORM API - 我們沒有在 GrailsTwitter 中將任何領域類別對應到 Redis。

範例應用程式還示範了 Searchable 外掛程式的使用,該外掛程式基於 Compass,在本例中處理使用者搜尋。 現在,外掛程式必須將其搜尋索引儲存在某個地方,那麼當我們將應用程式部署到 Cloud Foundry 時,它如何知道將它們放在哪裡呢?

檔案系統存取

在許多生產環境中,您事先知道可以儲存檔案(例如搜尋索引)的位置,因此您將路徑放入執行時期配置中。 或者,您可以透過grails.config.locations設定包含外部配置檔案,而運維團隊將把適當的路徑放入該檔案中。 但是,使用 Cloud Foundry,您事先不知道任何可用的檔案位置,也無法部署外部配置檔案。

幸運的是,Cloud Foundry 在執行時期透過HOME環境變數提供合適的檔案路徑。 您的應用程式所要做的就是讀取它,然後使用該路徑作為基礎建立所需的任何目錄和檔案。 就 Searchable 而言,Cloud Foundry 外掛程式會自動將搜尋索引的標準位置替換為基於HOME變數的位置,因此您不必做任何事情!

這一切都很棒,但是雲端中的檔案系統存取存在嚴重的問題,我在前面談到 Ehcache 時已經暗示了這個問題:如果您有多個應用程式實例,那麼每個實例都會有自己(不同)的檔案副本。 這通常不是您想要的。 例如,所有應用程式實例都應共用相同的搜尋索引,否則,使用者訪問一個實例會獲得與由不同實例服務的使用者不同的搜尋結果。

Cloud Foundry 仍處於早期階段,因此您可以期望在今年內會有更多服務上線。 搜尋服務(以及其他服務)的需求已經提出。 同時,我們可以提供一種變通方案,該方案說明了雲端中協調挑戰的常見解決方案:使用 Redis 對發布/訂閱訊息的支援來保持檔案同步。 例如,在 Grails Twitter 中,我們可以在每次新增使用者時觸發事件,然後所有實例都會監聽該事件並索引新使用者。 修改或刪除使用者也是如此。 撰寫本文時,Grails 的 Redis 外掛程式尚不支援發布/訂閱(即將推出!),但在該支援可用之前,您可以使用 Spring Data。

未來

如您所見,透過明智地使用可用的服務(尤其是 Redis),您已經可以將適度複雜的應用程式部署到 Cloud Foundry。 而且,您可以如此輕鬆地將應用程式部署到 Cloud Foundry,使其成為一個引人注目的部署目標。

儘管如此,重要的是要了解未來將在 Cloud Foundry 中新增更多服務,這將使您能夠部署具有更多功能的應用程式。 隨著這些服務的到來,我們致力於確保對應的 Grails 支援與現有的支援一樣流暢且易於使用。 因此,盡情實驗、享受樂趣,並期待 Cloud Foundry 服務的一些令人興奮的補充!

取得 Spring 電子報

保持與 Spring 電子報的聯繫

訂閱

領先一步

VMware 提供訓練與認證,以加速您的進展。

了解更多

取得支援

Tanzu Spring 在一個簡單的訂閱中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支援與二進位檔案。

了解更多

即將到來的活動

查看 Spring 社群中所有即將到來的活動。

查看全部