領先一步
VMware 提供培訓和認證,助您加速進步。
瞭解更多上週,Cloud Foundry 上將 RabbitMQ 作為服務提供的可用性宣佈。現在,任何執行在 Cloud Foundry 上的應用程式都可以透過 RabbitMQ 代理傳送和接收訊息,該代理可以透過單個命令(例如 'vmc create-service rabbitmq')作為服務進行配置。訊息服務例項可以在應用程式之間共享,並且由於 RabbitMQ 是一個基於協議的代理,這些應用程式甚至可以用不同的語言編寫。因此,對於那些對在雲中執行的模組化、多語言、事件驅動應用程式感興趣的人來說,這是一個激動人心的訊息。我將釋出一系列關注這些型別應用程式的部落格。在這篇文章中,我將保持簡單,重點介紹 Spring 開發人員的初始體驗。
首先,我建議您檢視本教程,即使您之前沒有 Cloud Foundry 的經驗,這也是最佳入門方式。在那裡您將看到如何使用 Maven 構建一個簡單的 Spring 應用程式,並使用 VMC 命令列工具將其部署到 Cloud Foundry。然後,該應用程式透過增強其 MVC 控制器以釋出和檢索訊息來引入 RabbitMQ。它展示瞭如何透過 Spring AMQP 庫配置和使用 RabbitMQ 服務。
此外,在最初的 Cloud Foundry 公告發布當天(與這些部落格 文章在同一天),我釋出了另一篇部落格,其中涵蓋了“cloud”名稱空間支援的基礎知識。閱讀該文章也可能有助於為即將看到的內容奠定基礎。具體來說,我們擴充套件了“cloud”名稱空間以包含對 RabbitMQ ConnectionFactory 的支援,這將在我們下面的配置概述中介紹。
現在,我將介紹另一個演示簡單聊天伺服器的示例應用程式。RabbitMQ 為多功能聊天應用程式提供了一個極好的骨幹,因為它支援不同型別的交換機,例如“direct”/點對點、“topic”基於釋出/訂閱和“fanout”用於簡單廣播。此外,RabbitMQ 支援多種語言繫結。再加上在雲中啟用訊息傳遞基本上只需輕觸開關,現在許多不同的應用程式可以輕鬆共享該服務。如上所述,我將逐步增強示例,並在稍後釋出更多部落格文章,以涵蓋這些交換機型別和一些多語言聊天,但目前我的目標是透過扇出交換提供一個可訪問的起始點,僅支援全域性廣播。應用程式的當前狀態並不比教程中介紹的更復雜。我將介紹一些配置和程式碼,但如果您想跟著並深入瞭解更多細節,我建議從 GitHub 上的 SpringSource cloudfoundry-samples 倉庫克隆示例。
這是執行中的應用程式的樣子
該表單將使用 jQuery 提交 HTTP POST 請求
$('#chatForm').submit(
function() {
$.post(
$('#chatForm').attr("action"),
$('#chatForm').serialize(),
function(response) {
if (response) {
confirm(response.id);
}
});
$('#text').val("");
return false;
});
並且,聊天日誌將透過輪詢定期更新——也使用 jQuery 的 AJAX 支援
$.ajax({
url : "chatlog",
success : function(message) {
if (message && message.length) {
var messagesDiv = $('#messages');
messagesDiv.html(message);
messagesDiv.animate({ scrollTop: messagesDiv.attr("scrollHeight") - messagesDiv.height() }, 150);
}
timer = poll();
},
error : function() {
timer = poll();
},
cache : false
});
如果您克隆倉庫並進入“rabbit-chat”目錄,您將看到以下結構
├── pom.xml
├── src
│ └── main
│ ├── java
│ │ └── org
│ │ └── cloudfoundry
│ │ └── samples
│ │ └── rabbitmq
│ │ └── chat
│ │ └── ChatController.java
│ ├── resources
│ │ └── static
│ │ └── js
│ │ └── jquery.min.js
│ └── webapp
│ └── WEB-INF
│ ├── spring
│ │ └── servlet-context.xml
│ ├── views
│ │ └── chat.jsp
│ └── web.xml
pom.xml 檔案聲明瞭依賴項。特別值得關注的是以下幾項
web.xml 檔案聲明瞭一個 Spring MVC DispatcherServlet 和一個單一的包羅永珍的 servlet 對映 (“/”)。
如您所見,只有一個名為“ChatController”的控制器。它透過註解進行配置。它使用 @Controller 和 @RequestMapping 註解以及 @Autowired。由於 ChatController 確實是應用程式的核心(也是其唯一的 Java 程式碼),讓我們快速瀏覽一下整個實現
@Controller
public class ChatController {
@Autowired
private volatile AmqpTemplate amqpTemplate;
private final Queue<String> messages = new LinkedBlockingQueue<String>();
@RequestMapping(value = "/")
public String home() {
return "WEB-INF/views/chat.jsp";
}
@RequestMapping(value = "/publish", method = RequestMethod.POST)
@ResponseStatus(value = HttpStatus.OK)
public void publish(@RequestParam String username, @RequestParam String text) {
this.amqpTemplate.convertAndSend(username + ": " + text);
}
@RequestMapping(value = "/chatlog")
@ResponseBody
public String chatlog() {
return StringUtils.arrayToDelimitedString(this.messages.toArray(), "<br/>");
}
/**
* This method is invoked when a RabbitMQ Message is received.
*/
public void handleMessage(String message) {
if (messages.size() > 100) {
messages.remove();
}
messages.add(message);
}
}
有 3 個控制器方法(用 @RequestMapping 註解的方法),每個方法只有一行程式碼。其中最簡單的是 home(),它返回要渲染的 JSP 位置。我通常會使用 Spring MVC ViewResolver,但由於它只是一個使用 AJAX 的單頁應用程式,因此這是唯一直接渲染的檢視。如您所見,每當您請求應用程式根目錄時,都會呼叫 home()。
publish(..) 方法用於處理對相對 URL “/publish” 的 HTTP POST 請求,它期望請求中有兩個引數:username 和 text。這些引數由您在上一節中看到的 HTML 表單提供。該表單由 chat.jsp 渲染。publish 方法所做的只是將 username + text 值連線成一個字串,然後將其轉換為 AMQP 訊息,並透過 AmqpTemplate 傳送,之後它會以簡單的 HTTP 200 (OK) 狀態響應。模板例項已自動注入到控制器中。我們很快將檢視 AmqpTemplate 配置以及支援在 Cloud Foundry 上使用 RabbitMQ 服務的底層 ConnectionFactory。
chatlog() 方法只是返回多達 100 條最新的聊天訊息。它是上一節中所示的 AJAX 請求正在輪詢的方法。handleMessage(..) 方法負責將這些聊天訊息排隊,因此它是連線到底層訊息監聽器的方法。這幾乎涵蓋了應用程式的功能。
現在,我們可以詳細介紹此應用程式的配置。這本可以完全用 Java 註解完成,但希望您同意這是一個相當簡潔的配置檔案
<context:component-scan base-package="org.cloudfoundry.samples.rabbitmq.chat"/>
<mvc:annotation-driven/>
<mvc:resources location="file:./src/main/resources/static/,classpath:/static/" mapping="static/**"/>
<rabbit:queue id="chatQueue"/>
<rabbit:fanout-exchange name="chatExchange">
<rabbit:bindings>
<rabbit:binding queue="chatQueue"/>
</rabbit:bindings>
</rabbit:fanout-exchange>
<rabbit:template connection-factory="rabbitConnectionFactory" exchange="chatExchange"/>
<rabbit:admin connection-factory="rabbitConnectionFactory"/>
<rabbit:listener-container>
<rabbit:listener queues="chatQueue" ref="chatController" method="handleMessage"/>
</rabbit:listener-container>
<cloud:rabbit-connection-factory id="rabbitConnectionFactory"/>
“component-scan”元素使帶 @Contoller 註解的類註冊為 Spring 管理的物件,並激活 @Autowired 支援。兩個帶“mvc”字首的元素只是設定 MVC @RequestMapping 支援並啟用靜態資源的載入(在這種情況下用於“resources/static/js”目錄中提供的 jQuery 支援)。
其餘元素與 RabbitMQ 配置相關。“rabbit:admin”生成一個 RabbitAdmin 例項,該例項負責識別在同一應用程式上下文中定義的交換機、佇列和繫結。請注意,佇列元素將其 id 設定為“chatQueue”,但該 id 沒有“name”屬性。這將觸發建立一個具有唯一生成名稱的佇列,該佇列專屬於此特定應用程式。換句話說,“id”屬性的值不對映到佇列的名稱;它是 Spring bean 的 id,而不是 RabbitMQ 佇列的 id。儘管它具有生成的名稱,但它需要可識別以便在此應用程式上下文中進行引用。例如,您可以看到它在此處定義的“chatExchange”的繫結中被引用,該交換機被定義為扇出交換機。由於存在“rabbit:admin”元素,該交換機也將針對代理進行宣告。
“rabbit:template”非常簡單。它需要引用 ConnectionFactory(別擔心,我們稍後會講到),如果您希望其“send”方法釋出到非預設的無名交換機,您可以在此處提供。我們正在釋出到我們剛剛討論過的“chatExchange”。
“rabbit:listener-container”與 Spring JMS 支援中同名元素幾乎相同。這個監聽器正在監聽“chatQueue”,請記住這只是對 bean id 的引用,該特定佇列的真實名稱是由代理生成的。每當訊息到達該佇列時,我們之前看到的“handleMessage”方法將被呼叫。在方法引數是 String 的情況下,監聽器容器的介面卡將自動處理訊息體的轉換。由於該方法引數不需要接受實際的 Message 例項,並且方法名稱可以是任何我們想要的名稱,我們稱之為“訊息驅動的 POJO”。換句話說,它對訊息 API 沒有直接依賴。它被監聽器容器呼叫是一種控制反轉的形式。
最後,是連線工廠配置。在這種情況下,我們使用“cloud”名稱空間及其“rabbit-connection-factory”元素。只要您的應用程式繫結到 Cloud Foundry 中的單個“rabbitmq”服務,建立 ConnectionFactory 例項就不需要其他資訊。該名稱空間支援底層的程式碼將從環境本身確定憑據。該名稱空間支援由“cloudfoundry-runtime”庫提供,您可以在此應用程式的pom.xml檔案中看到其宣告。
您可以使用 vmc 命令列工具或 SpringSource Tool Suite 執行應用程式。使用 vmc,您會看到如下內容
$ vmc push
Would you like to deploy from the current directory? [Yn]: y
Application Name: rabbit-chat-sample
Application Deployed URL: 'rabbit-chat-sample.cloudfoundry.com'?
Detected a Java SpringSource Spring Application, is this correct? [Yn]: y
Memory Reservation [Default:512M](64M, 128M, 256M, 512M or 1G)
Creating Application: OK
Would you like to bind any services to 'rabbit-chat-sample'? [yN]: y
Would you like to use an existing provisioned service [yN]? n
The following system services are available:
1. mongodb
2. mysql
3. postgresql
4. rabbitmq
5. redis
Please select one you wish to provision: 4
Specify the name of the service [rabbitmq-5e262]:
Creating Service: OK
Binding Service: OK
Uploading Application:
Checking for available resources: OK
Processing resources: OK
Packing application: OK
Uploading (3K): OK
Push Status: OK
Staging Application: OK
Starting Application: OK
如果使用 STS,您只需啟用 Cloud Foundry 支援(可從儀表板的“Extensions”選項卡獲得),然後建立一個新的伺服器例項(有關所有詳細資訊,請參閱 入門指南),然後您可以簡單地將應用程式拖到該例項上。將應用程式新增到伺服器例項後,您可以透過 UI 配置和繫結服務。以下螢幕截圖顯示了“rabbit-chat”示例應用程式。
還記得關於“chatQueue”id 的討論以及由於使用了 id 而不是 name 屬性而生成的佇列名稱嗎?嗯,我們在這裡使用 id 而不是 name 的原因是,我們希望應用程式的每個例項都有自己獨有的、實際上是匿名的佇列。扇出交換機只有一個命名例項。應用程式的每個例項都會將其自己的佇列繫結到該交換機。這種交換機和佇列的解耦對於可擴充套件的雲應用程式非常適用(特別是那些名稱是生成的佇列將在其擁有例項關閉時自動刪除)。
要擴充套件應用程式,您可以在命令列使用 VMC
$ vmc instances rabbit-chat-sample +1
Scaling Application instances up to 2: OK
或者,您可以使用 STS 支援。這是從上面釋出的相同螢幕截圖中獲取的“例項”配置的聚焦檢視
本部落格旨在成為系列文章的第一篇。在即將釋出的文章中,我們將探討以下內容