深入瞭解 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 的資料模型

人物還是許可權領域類是標準的 Spring Security,所以我除了它們是由 Hibernate 對映的之外,不會再多說什麼,因此當應用程式部署時需要 Cloud Foundry MySQL 服務。

更有趣的是狀態領域類。透過向該類新增一個靜態的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 儲存在狀態訊息中,並新增一個瞬態的、只讀的作者屬性,以便輕鬆訪問關聯的人物例項。知道怎麼做就簡單了!

你還可以看到標籤不是作為單獨的領域例項儲存的(標籤是狀態訊息中以“#”開頭的任何字串)。相反,每個狀態將其標籤列表作為純字串儲存——這是 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 社群所有即將舉行的活動。

檢視所有