今年項目組做的是Cloud產品,有幸接觸到了雲計算的知識,也了解並使用了當今流行的雲計算平台Amazon AWS與Microsoft Azure。我們的產品最初只部署在AWS平台上,現在產品決定同時支持Azure,所以有幸學習下Azure,並在查看文檔資料以及寫Demo過程中發現了其中的一些不同。雖然AWS與Azure是兩款旗鼓相當的競爭產品,但是還是有很多區別。
本文主要是自己學習Service Bus中的學習筆記,自己有些結論也都跟微軟技術支持確認過。個人觀點,拋磚引玉:-)
消息服務對於雲計算產品是很重要的。例如SQS,我們可以使用持久化隊列來儲存消息,這樣同一產品的不同Module在通信過程中利用隊列存儲消息,可避免消息的丟失(因為消息會被分布式冗余的存儲,屬於隊列的內部實現)。針對隊列服務,AWS提供了SQS,Azure則提供了Service Bus(當然除了Service Bus,微軟還提供了Storage部分中的Queue功能,但個人認為這個Queue偏重消息存儲,而Service Bus中的Queue偏重通信,具體區別可參照此文章http://msdn.microsoft.com/library/azure/hh767287)。例外AWS里面,SNS可以和SQS一起使用。SNS中就提供了對SQS的支持,如果有消息產生需要通知Module,SNS可以直接將消息存儲在SQS中。
而微軟的Azure提供的Service Bus功能,主要有五部分子功能組成:
1. Queue 隊列
提供的功能與Amazon SQS類似
2. Topic/Subscription 主題/訂閱
更高級的Queue,類似與一個虛擬隊列,可接收發送到主題的消息副本,從訂閱接收消息的方式與隊列接收消息的方式相同。
3. Relay 中繼
創建Hybrid Application時使用,我沒有使用過此功能。
4. Notification Hub 通知中心
主要是向Device移動設備推送通知,例如注冊一個APP在通知中心,通知中心可向運行此APP的所有設備發送推動通知,支持幾乎所有的手機平台。
5. Event Hub 事件中心
可用於存儲產品運行時產生或收集的大量事件。
這里我們的產品主要使用的將是第二項功能,主題/訂閱。這樣我們產品中的一個Module只需將消息發送至主題中,凡是訂閱了該主題的訂閱(隊列)都是有一個該消息的副本。這樣我們的另一個Module只需關注訂閱中的消息,取出消息,處理消息,達成通信的目的。
在Azure中,要使用主題/訂閱,首先要在Service Bus中新建一個Namespace,然后新建主題與訂閱即可。
值得注意的是,之前Service Bus提供的認證方式都是ACS,前一段時間都改為了SAS(共享訪問簽名)。而這一改動需要SDK的支持,據我所知,Azure Java SDK上周才剛剛在新版本中支持了這一認證,可謂效率之低。具體詳情可參見我上一篇文章:【Microsoft Azure學習之旅】Azure Java SDK - Service Bus的認證問題http://www.cnblogs.com/KevinSong/p/4146811.html。
下面的代碼是我根據微軟文檔寫的一個Demo。使用Java SDK實現,作用是向主題發送消息,然后從訂閱中取出消息。
1. 創建一個主題,並向主題發送消息
1 //create a topic in name space 2 public void Create(TopicInfo topic){ 3 System.out.println("=>CreateTopic"); 4 try{ 5 if(!IsAlreadyExist(topic)){ 6 CreateTopicResult result = service.createTopic(topic); 7 System.out.println("Create Topic Result: " + result.toString()); 8 } 9 else{ 10 System.out.println("This Topic already exist, doesn't need to create"); 11 } 12 } 13 catch (ServiceException e){ 14 System.out.println("ServiceException encountered: " + e.getMessage()); 15 } 16 System.out.println("<=CreateTopic"); 17 } 18 19 //send message to a topic 20 public void SendMessage(TopicInfo topic){ 21 System.out.println("=>SendMessage"); 22 try{ 23 BrokeredMessage msg = new BrokeredMessage("testMsg"); 24 25 //set one property to this msg 26 msg.setProperty("testProperty", "kevin01"); 27 28 service.sendTopicMessage(topic.getPath(), msg); 29 } 30 catch (ServiceException e){ 31 System.out.println("ServiceException encountered: " + e.getMessage()); 32 } 33 System.out.println("<=SendMessage"); 34 }
2. 創建一個關注該主題的訂閱,並從訂閱中取出消息
1 //create a subscription in name space 2 public void Create(TopicInfo topic, SubscriptionInfo sub){ 3 System. out.println("=>CreateSubscription" ); 4 try{ 5 System. out.println("Topic Info: " + topic .getPath().toString()); 6 7 if(!IsAlreadyExist(topic , sub )){ 8 CreateSubscriptionResult result = service.createSubscription(topic .getPath(), sub); 9 //and we can create a rule for this topic/subscription 10 //RuleInfo rule = new RuleInfo(); 11 System. out.println("Create Subscription Result: " + result.toString()); 12 } 13 else{ 14 System. out.println("This subscription already exist. No need to create"); 15 } 16 } 17 catch (ServiceException e ){ 18 System. out.println("ServiceException encountered: " + e.getMessage()); 19 } 20 System. out.println("<=CreateSubscription" ); 21 } 22 23 24 //receive message from subscription 25 public void ReceiveMessage(TopicInfo topic, SubscriptionInfo sub){ 26 System. out.println("=>ReceiveMessage" ); 27 ReceiveMessageOptions opts = ReceiveMessageOptions. DEFAULT; 28 opts.setReceiveMode(ReceiveMode. PEEK_LOCK); 29 try{ 30 ReceiveSubscriptionMessageResult result = service.receiveSubscriptionMessage(topic .getPath(), sub.getName(), opts); 31 32 BrokeredMessage msg = result.getValue(); 33 34 if(msg != null && msg.getMessageId() != null){ 35 //print the message info 36 System. out.println("Body: " + msg .toString()); 37 System. out.println("Message ID: " + msg.getMessageId()); 38 39 System. out.println("Custom Property Value: " + msg.getProperty("testProperty" )); 40 41 //delete this message 42 service.deleteMessage( msg); 43 } 44 else{ 45 System. out.println("There's no message in this subscription. Topic: " + topic.getPath() + ", Subscription: " + sub .getName()); 46 } 47 } 48 catch (ServiceException e ){ 49 System. out.println("ServiceException encountered: " + e.getMessage()); 50 } 51 System. out.println("<=ReceiveMessage" ); 52 }
3. Main函數
1 //test topic/subscription 2 public static void main(String[] args){ 3 //create a topic, and send a message to this topic 4 ServiceBusTopicHandler topicHandler = new ServiceBusTopicHandler(); 5 TopicInfo topic = new TopicInfo("testtopic"); 6 topicHandler.Create(topic); 7 topicHandler.SendMessage(topic); 8 9 //create a subscription about this topic, and receive message from this subscription 10 ServiceBusSubscriptionHandler subHandler = new ServiceBusSubscriptionHandler(); 11 SubscriptionInfo sub = new SubscriptionInfo("testsubscription"); 12 subHandler.Create(topic, sub); 13 14 while(true){ 15 subHandler.ReceiveMessage(topic, sub); 16 } 17 }
得益於微軟提供的文檔與SDK,代碼實現很簡單。但是有個地方值得注意的是,
如何從訂閱/隊列中取出消息?
這涉及到Poll和Push技術的區別。
最簡單的是我們可以使用Poll技術,就是例如在while(true)循環中一遍遍去查詢,如果有新消息就取出並處理,但是這樣會大大影響性能。在AWS中,我們可以利用SNS技術中的一項,注冊http endpoint到SNS,如果有新消息來了,可以通過SNS調用我們自己的Web API來通知我們要去處理新消息,這樣就是Push的效果。但是在Azure中,有類似SNS的功能嗎?沒有。
這樣SNS的技術類似於Push技術,如果有新消息,它會主動調用你的API通知你。而非Poll那樣低效率的一遍遍查詢。但是微軟Azure在去年提出了一項Long Polling的新技術,它的名字叫做Event-Driven Message Programing Model,事件驅動消息編程模型,該技術的發布信息可參考http://msdn.microsoft.com/en-us/library/azure/dn198643.aspx。這其實本質是一項Long Polling長輪詢技術。
在Azure .Net SDK中,你可以使用OnMessage方法,來注冊一個回調Call back函數,如果有新消息來到,你的Call back會被調用到,來實現對消息的處理。本質是有不同的子線程,在執行long polling查詢。這跟Push技術還是有很大的區別。
但現在問題是,Event-Driven Message Programming Model僅僅在.Net SDK中提供,在其他語言的Azure SDK版本中並不提供此項功能。曾經今年有人在MSDN問過這個問題,為什么不同的SDK區別對待,微軟的回復請參照https://social.msdn.microsoft.com/forums/windows/en-us/7d0d4733-a696-4b72-927c-004caae029f7/event-driven-consumer-model-not-available-for-microsoft-azure-java-sdk?forum=windowsazuredevelopment。
由此可見,微軟對於.NET SDK的重視,而對於類似Java的不重視啊,畢竟是自家的語言。.Net SDK已經目前發布了數百個Release,而Java SDK還僅僅是v0.7.0版本。。。當然通過詢問微軟,Java SDK在將來有可能也支持此項功能(Event-Driven Message),但具體什么時間會支持一切未可而知。
無論是AWS,還是Azure,都有完整詳細的文檔提供,這里只是我在學習過程的一些總結。我的代碼示例(Java實現,采用Azure Java SDK)也附上如下。
Service Bus Queue/Subject/Subscription Demo下載地址:http://files.cnblogs.com/KevinSong/testServiceBusQueue.zip
有任何問題,歡迎討論。小白一個,繼續學習:-)
參照文檔:
1. Service Bus隊列,主題和訂閱
http://msdn.microsoft.com/library/azure/hh367516.aspx
2. 介紹Event-Driven Message Programming Model
http://fabriccontroller.net/blog/posts/introducing-the-event-driven-message-programming-model-for-the-windows-azure-service-bus/
3. AWS與Azure區別
http://azure.microsoft.com/en-us/campaigns/azure-vs-aws/
Best Regards
Kevin Song
2014年12月19日