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

Person”和“Authority”領域類是標準的 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”屬性,以便輕鬆訪問關聯的“PersonPerson

”例項。知道方法後就簡單了!您還可以看到,標籤不是作為單獨的領域例項儲存的(標籤是狀態訊息中以 '#' 開頭的任何字串)。相反,每個“StatusStatus

”將其標籤列表儲存為普通字串——這是 MongoDB 網站上建議的一種方法。這意味著儲存和檢索狀態訊息非常廉價,但這確實提出了一個重要問題:您如何找出系統中存在哪些標籤以及每個標籤有多少個出現次數?畢竟沒有可以查詢的“tag”表,而這在關係型資料庫中通常是會有的。這就是 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 外掛會自動將搜尋索引的標準位置替換為基於“HOMEHOME

”變數的位置,所以您無需做任何事情!這一切都很棒,但是雲上的檔案系統訪問存在一個嚴重問題,我之前在談論 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 社群所有即將到來的活動。

檢視全部