作者 王枫 发布于2014年5月15日

综述

本文介绍建立一个在Azure上使用Azure服务总线, 高吞吐量短信平台的必要步骤。在这篇文章中提出的解决方案是在响应由客户的具体要求,建立一个基于Windows Azure技术的复杂远程信息处理应用。

在Windows Azure中的通讯服务

Windows Azure平台通过不同的技术支持信息通信:

  • 存储队列
  • 服务总线

以下各段将给予每个类型的信息传递技术的概览。

存储队列

您可以使用Windows Azure中的队列,支持组件之间的异步通信。应用程序中的组件可以发布信息到一个队列。其他组件可以接收信息,并提供处理。队列中提供组件之间耐用的持久性存储,以及负载调平,负载均衡,和缩放的好处。

服务总线

Windows Azure服务总线为广泛的交流,大型活动分布,命名和服务发布提供了一个托管,安全和广泛可用的基础设施。服务总线为Windows Communication Foundation(WCF)和其他服务端点提供连接选项 - 包括REST端点 - 否则将很难或根本不可能达到。

针对Windows Server的服务总线是一组可安装的组件,为Windows Sever上提供Windows Azure服务总线的信息传递功能。适用于Windows Server的服务总线,使您能够在自我管理的环境,并在开发计算机上构建,测试和松耦合运行,信息驱动的应用程序。

通讯设计模式

使用存储队列构建松散耦合的信息传递方案

Windows Azure的队列存储是一个用于存储大量的信息,可以在世界任何地方通过身份验证的调用使用HTTP或HTTPS访问服务。一个单一的队列的信息可高达64KB的大小,队列可以包含数百万条信息,高达100TB的总极限容量的存储帐户。常见队列存储用途包括:

  • 建异步处理积压的工作
  • Windows Azure Web角色中传递信息到Windows Azure Worker角色

队列服务包含以下组件:

URL格式:队列可以通过以下URL格式访问:

http://<storage account>.queue.core.windows.net/<queue>

下面的URL地址针对图中的队列:

http://myaccount.queue.core.windows.net/imagesToDownload

  • 存储帐户:所有Windows Azure存储是通过一个存储帐户来访问的。存储帐户是访问队列的最高级别的命名空间。帐户内表格和队列内容存储的总大小不能超过100TB。
  • 队列: 队列中包含一系列信息。所有信息必须在队列中。
  • Message: 一个信息,无论任何格式的最大上限是64KB

一个涉及多个组件之间的存储队列典型的架构设计模式,可以类似于下列:

在这个模式中,多个前端Web Role客户端发送信息到一个或多个存储队列,然后多个Worker Role实例从一个或多个队列读取信息及进行处理。

上述架构可以通过以下方式缩放:

  • 存储队列:当信息并发数量增加,可以增加更多的存储队列
  • Worker Role实例:当信息的数量增加,Worker Role实例的数量可以增加,从而使更多的计算资源可用于处理从队列中的信息。

不过,也有数个点,我们需要考虑到,以确保在现实中的架构可以灵活扩展:

  • 存储帐户:一个单一的存储帐户的可扩展性目标是每秒5000条信息
  • 存储队列:一个单一队列的可扩展性目标是能够处理最多每秒500条信息

由于上述两个存储帐户和存储队列的限制,如果我们希望能够同时处理大量信息,我们需要分割我们的存储基础设施成多个队列和/或存储帐户。

例如,一个客户端以每秒1000条信息的速度传送到Azure存储队列中,我们希望我们的解决方案以无积压及最快的速度来处理信息,那么我们就需要使用4个队列及分割我们我们的信息传递到4个队列中,看下图。

每个队列每秒可以处理500个信息,包括入口和出口,如果我们想每个队列尽快处理信息,那么每个队列入口和出口每秒应该处理不超过250个信息。既然我们的入口和出口每秒要处理1000条信息,因此,我们需要共4个队列。Web Role客户端将需要分割信息及均匀地分配到可用的队列以分担工作负载。

在另一情况下,客户端以每秒10,000条的速度发送信息到存储队列,那么我们就需要创建多个存储帐户分担工作量。为了处理每秒10,000个信息,我们需要2个存储帐户,每个存储账户下, 需要20个队列。

利用服务总线主题发布订阅信息

服务总线主题和订阅支持信息发布/订阅传递通信模式。当使用主题和订阅,分布式应用程序个组件不直接彼此沟通,而是通过一个主题,充当中介的信息交换。

相比在服务总线队列,每个信息是由单一消费者处理的,主题及订阅使用发布/订阅模式提供了一个对多个形式的通信。一个主题注册多个订阅是可能的。当一个信息被发送到一个话题后,它会被每个订阅独立处理。您可以选择为基于每一个订阅基础上的主题, 注册过滤规则,它可以让你过滤/限制主题订阅中接收的信息。

服务总线主题和订阅,让你在横跨非常大量的用户和应用程序下, 可以扩展到处理一个非常大的信息数目。

自动转发链接服务总线实体来缩放单个主题

自动转发功能能让您使一个订阅或队列链到相同的服务命名下另一个队列或主题。当自动转发启用,服务总线自动移除被放置在第一个队列或订阅(源)的信息,并把它们放入第二队列或主题(目标)。

您可以使用自动转发向外扩充一个单独主题。服务总线限制一个主题订阅的数量。可以通过创建第二层主题容纳更多的订阅。需要注意的是,即使你并不受服务总线订阅数目的限制,增加了第二层的主题,可以提高你的主题的整体吞吐量。

当把别主题串联起来而得到一个拥有许多订阅的复合主题时,建议你有一个中等数量的第一级主题订阅及许多二级的主题订阅。例如,第一级以20个主题订阅,彼等各自链接到第二层200个主题订阅,允许比第一层200个主题订阅每个链接有20个第二层主题订阅更高的吞吐量。

参考案例

下面的示例使用中国的Azure公共云计算平台,及解释利用服务总线在中国Azure平台专门所需的编码。使用的Azure SDK 2.0及此示例介绍了Windows Azure服务总线在Azure SDK 2.0提供的,新的事件驱动编程模式。

Scenario场景

上面的例子是基于建立连接汽车远程信息处理方案,其中远程信息处理单位不断发送车数据到云端。各种车辆数据还有多个接收器。为了支持非常高吞吐量的汽车数据到云中,例子采用了2层自动转发的向外扩展做法。在第一层,每款车数据主题有少量订阅, 例如10 个,每个订阅自动转发到第二层主题。每个第二层主题然后有多个订阅 -接收器 Worker Roles. 每个接收器Worker Role包含多个实例。

取决于实际吞吐量要求,第一层和第二层的主题可以独立扩展, 增加的主题及订阅的数量。以及每个接收器Worker Role可以不断增加对Worker Role实例的向外扩展。

连接到服务总线

中国Azure比全球Azure平台有不同端点URL,全球Azure所提供的一些文档和示例代码并不适合使用在Azure中国环境。下面是需要创建连接到Azure中国服务总线的示例代码:

  1. NamespaceManager namespaceManager;
  2. MessagingFactory messagingFactory;
  3. string namespaceAddress = "<your-namespace>";
  4. string issuerName = "<your-issuer-name>";
  5. string issuerKey = "<your-issuer-key>";
  6. string endpointuriAddress = string.Format("sb://{0}.servicebus.chinacloudapi.cn",
  7. namespaceAddress);
  8. string stsendpointAddress = string.Format("https://{0}-sb.accesscontrol.chinacloudapi.
  9. cn/", NamespaceAddress);
  10.  
  11. TokenProvider tp = TokenProvider.CreateSharedSecretTokenProvider(issuerName,
  12. issuerKey, new Uri(stsendpointAddress));
  13. namespaceManager = new NamespaceManager(new Uri(endpointuriAddress), tp );
  14. messagingFactory = MessagingFactory.Create(endpointuriAddress, tp);

创建主题及自动转发订阅

  1. public bool CreateTopic(string topicNamePrefix, int topicSuffixIndex, int NumOfTopics)
  2. {
  3. bool success;
  4. try
  5. {
  6. var topic = this.namespaceManager.CreateTopic(string.Format("{0}_{1}",
  7. topicNamePrefix, topicSuffixIndex.ToString()));
  8.  
  9. for (int i = 0; i < NumOfTopics; i++)
  10. {
  11. CreateSubscription(topicSuffixIndex, i, topic.Path);
  12. }
  13. success = true;
  14. }
  15. catch (Exception)
  16. {
  17. success = false;
  18. }
  19. return success;
  20. }
  21.  
  22. public bool CreateSubscription( int topicSuffixIndex, int subscriptionIndex,
  23. string srcTopic)
  24. {
  25. bool success = false;
  26. string destTopic = srcTopic + subscriptionIndex.ToString();
  27. TopicDescription dTopic = null;
  28. try
  29. {
  30. dTopic = this.namespaceManager.GetTopic(destTopic);
  31. }
  32. catch (Microsoft.ServiceBus.Messaging.MessagingEntityNotFoundException)
  33. {
  34. dTopic = null;
  35. }
  36.  
  37. if (dTopic == null)
  38. {
  39. dTopic = this.namespaceManager.CreateTopic(destTopic);
  40.  
  41. var subscription = this.namespaceManager.CreateSubscription(dTopic.Path, "
  42. Subscription_0" );
  43.  
  44. }
  45.  
  46. SubscriptionDescription srcSubscription = new SubscriptionDescription(srcTopic,
  47. string.Format("Subscription{0}_{1}", topicSuffixIndex.ToString(), subscriptionIndex.ToString()));
  48. srcSubscription.ForwardTo = dTopic.Path;
  49. var ForwardSubscription = this.namespaceManager.CreateSubscription(srcSubscription,
  50. new SqlFilter(string.Format("PartitionID='{0}'", subscriptionIndex.ToString())));
  51.  
  52. return success;
  53. }

执行下面的代码片段创建的主题和订阅。下面的代码执行的结果将创建5个第一层主题及10个第二个层主题。每个第一层主题将有10个自动转发订阅到10个第二层主题。

  1. for (int i = 0; i < 5 i++)
  2. {
  3. CreateTopic(“SBMessageTopic”, i, 10);
  4. }

当从Azure管理门户网站查看时,可以查看以下主题:

第一层有10个主题订阅:

第二层主题包含2个订阅为2个接收器的Work Role。

发送信息个到服务总线主题

下面的示例代码并行发送一批信息到其中一个第一层主题:

  1. public bool SendMessage(string msgid, string message, string label, int nIterations,
  2. int NumOfPartitions )
  3. {
  4. bool success = false;
  5. string topicName = sbmessagetopic_0”;
  6.  
  7. TopicClient topicClient = this.messagingFactory.CreateTopicClient(topicName);
  8.  
  9. List<CustomMessage> MessagesList = new List<CustomMessage>();
  10. for (int i = 0; i < nIterations; i++)
  11. {
  12. CustomMessage customMessage = new CustomMessage() { Body = message, Date =
  13. DateTime.Now, Label = label , MSGID=msgid};
  14. MessagesList.Add(customMessage);
  15. }
  16.  
  17. Parallel.ForEach<CustomMessage>(MessagesList, new Action<CustomMessage>(
  18. (CarDataMessage) =>
  19. {
  20. BrokeredMessage bm = null;
  21. try
  22. {
  23. bm = new BrokeredMessage(CarDataMessage);
  24.  
  25. bm.Properties["PartitionID"] = RandomDigit(1, NumOfPartitions);
  26.  
  27. topicClient.Send(bm);
  28.  
  29. success = true;
  30. }
  31. catch (Exception)
  32. {
  33. // TODO: do something
  34. }
  35. finally
  36. {
  37. if (bm != null)
  38. {
  39. bm.Dispose();
  40. }
  41. }
  42. }
  43. ));
  44.  
  45. return success;
  46. }
  47.  
  48. public static string RandomDigit(int size, int length)
  49. {
  50. Random random = new Random();
  51. const string digitString = "0123456789";
  52. var chars = Enumerable.Range(0, size)
  53. .Select(x => digitString[random.Next(0, length)]);
  54. return new string(chars.ToArray());
  55. }
  56.  
  57. public class CustomMessage
  58. {
  59. private string msgid;
  60. private DateTime date;
  61. private string body;
  62. private string label;
  63.  
  64. public DateTime Date
  65. {
  66. get { return this.date; }
  67. set { this.date = value; }
  68. }
  69.  
  70. public string Body
  71. {
  72. get { return this.body; }
  73. set { this.body = value; }
  74. }
  75.  
  76. public string Label
  77. {
  78. get { return this.label; }
  79. set { this.label = value; }
  80. }
  81.  
  82. public string MSGID
  83. {
  84. get { return this.msgid; }
  85. set { this.msgid = value; }
  86. }
  87. }

下面的代码片段执行发送信息指令,它发送1000个信息到第一层主题并从第一层主题订阅随机转发每个信息到第二层主题其中一个":

  1. SendMessage(“Your-Msg-ID”, Message Text , Message Label”, 1000, 10);

同时发送大量的信息时,建议利用大型Worker Role并且有多个实例,以提高的并行度。

接收信息

下面的示例代码在Work Role启动由Azure SDK 2.0提供的事件驱动信息泵:

  1. public void StartMessagePump(string topicName, string subscriptionName)
  2. {
  3. SubscriptionClient subscriptionClient = this.messagingFactory.
  4. CreateSubscriptionClient(topicName, subscriptionName, ReceiveMode.PeekLock);
  5. var eventDrivenMessagingOptions = new OnMessageOptions();
  6. eventDrivenMessagingOptions.AutoComplete = true;
  7. eventDrivenMessagingOptions.ExceptionReceived += OnExceptionReceived;
  8. eventDrivenMessagingOptions.MaxConcurrentCalls = 256;
  9.  
  10. subscriptionClient.OnMessage(OnMessageArrived, eventDrivenMessagingOptions);
  11. }

当信息到达时, OnMessage()被调用:

  1. /// <summary>
  2. /// This event will be called each time a message arrives.
  3. /// </summary>
  4. /// <param name="message"></param>
  5. private static void OnMessageArrived(BrokeredMessage message)
  6. {
  7. var custmsg = message.GetBody<CustomMessage>();
  8.  
  9. System.Diagnostics.PerformanceCounter perfCounterReceived =
  10. new System.Diagnostics.PerformanceCounter("ServiceBusDemo", "Message Receive/
  11. Sec", RoleEnvironment.CurrentRoleInstance.Id, false);
  12. if (perfCounterReceived != null)
  13. {
  14. perfCounterReceived.ReadOnly = false;
  15. perfCounterReceived.IncrementBy(1);
  16. }
  17.  
  18. Trace.WriteLine(string.Format(" > {0} - Received message for Vehicle: {1} (Thread: {2})",
  19. DateTime.Now, custmsg.MSGID, Thread.CurrentThread.ManagedThreadId));
  20.  
  21. if (custmsg.MSGID == string.Empty)
  22. throw new InvalidOperationException("Invalid Message Id: " + custmsg.MSGID);
  23. }

和下面的功能在发生错误时被调用时:

  1. /// <summary>
  2. /// Event handler for each time an error occurs.
  3. /// </summary>
  4. /// <param name="sender"></param>
  5. /// <param name="e"></param>
  6. static void OnExceptionReceived(object sender, ExceptionReceivedEventArgs e)
  7. {
  8. if (e != null && e.Exception != null)
  9. {
  10. Trace.WriteLine(string.Format(" > Exception received: {0}", e.Exception.Message));
  11. }
  12. }

下面的代码片段是Worker Role执行run()方法进行订阅到第二层主题。这个例子进行订阅10个第二层主题:

  1. for (int i = 0; i < 10; i++)
  2. {
  3. string topic = string.Format("sbmessagetopic_0{0}", i.ToString());
  4. StartMessagePump(topic, "Subscription_1");
  5. }
  6. while (true)
  7. {
  8. Thread.Sleep(10000);
  9. Trace.TraceInformation("SBMessageConsumer1 Working", "Information");
  10. }

测试服务总线吞吐量

上面的测试是通过使用一个2芯信息产生者Worker Role实例和一个2芯信息的接收者实例,在1个第一层主题和10个第二层主题。吞吐量保持在每秒1000 - 1200信息之间。

该解决方案可以通过加入多个第一层主题,以及更多的第二层主题进一步向外扩展。当适当数量的信息接收器的情况下,服务总线解决方案可以轻松地向外扩展支持每秒数千或数万的信息吞吐量。

小结

本文描述了在Azure上处理应用程序和设备间进行信息传递的不同信息传递组件。此外,这篇文章亦介绍了不同的方式在Azure上来扩展信息传递的基础设施,以向外扩展的方法来处理大量信息传递并发。

感谢马国耀对本文的审校。

本文转载自:http://www.infoq.com/cn/articles/experience-of-scalable-messaging-in-azure-environment

如何在Azure环境里做好信息传递可扩展性经验分享的更多相关文章

  1. 如何在 Azure 虚拟机里配置条带化

    什么是条带化(striping) 条带 (strip) 是把连续的数据分割成相同大小的数据块,把每段数据分别写入到阵列中的不同磁盘上的方法.简单的说,条带是一种将多个磁盘驱动器合并为一个卷的方法. 许 ...

  2. 如何在SAP云平台ABAP编程环境里把CDS view暴露成OData服务

    Jerry 2016年在学习SAP CDS view时,曾经写过一个CDS view的自学系列,其中有一篇提到了一个很方便的注解: @OData.publish: true 加上这个注解的CDS vi ...

  3. 业务零影响!如何在Online环境中巧用MySQL传统复制技术【转】

    业务零影响!如何在Online环境中巧用MySQL传统复制技术 这篇文章我并不会介绍如何部署一个MySQL复制环境或keepalived+双主环境,因为此类安装搭建的文章已经很多,大家也很熟悉.在这篇 ...

  4. tg2015 信息传递 (洛谷p2661)

    题目描述 有n个同学(编号为1到n)正在玩一个信息传递的游戏.在游戏里每人都有一个固定的信息传递对象,其中,编号为i的同学的信息传递对象是编号为Ti同学. 游戏开始时,每人都只知道自己的生日.之后每一 ...

  5. ASP.NET 在 Windows Azure 环境中使用基于 SQLServer 的 Session

    Session 嘛,占一点儿服务器资源,但是总归比 ViewState 和 Cookie 安全点儿,所以还是要用的. Windows Azure 环境中的 Web 服务器经由负载均衡调度,根本无法保证 ...

  6. [NOIP2015] 提高组 洛谷P2661 信息传递

    题目描述 有n个同学(编号为1到n)正在玩一个信息传递的游戏.在游戏里每人都有一个固定的信息传递对象,其中,编号为i的同学的信息传递对象是编号为Ti同学. 游戏开始时,每人都只知道自己的生日.之后每一 ...

  7. 洛谷 P2661 信息传递 Label:并查集||强联通分量

    题目描述 有n个同学(编号为1到n)正在玩一个信息传递的游戏.在游戏里每人都有一个固定的信息传递对象,其中,编号为i的同学的信息传递对象是编号为Ti同学. 游戏开始时,每人都只知道自己的生日.之后每一 ...

  8. NOIP 2015 信息传递

    kawayi 题目描述 有n个同学(编号为1到n)正在玩一个信息传递的游戏.在游戏里每人都有一个固定的信息传递对象,其中,编号为i的同学的信息传递对象是编号为Ti同学. 游戏开始时,每人都只知道自己的 ...

  9. AC日记——信息传递 洛谷 P2661 (tarjan求环)

    题目描述 有n个同学(编号为1到n)正在玩一个信息传递的游戏.在游戏里每人都有一个固定的信息传递对象,其中,编号为i的同学的信息传递对象是编号为Ti同学. 游戏开始时,每人都只知道自己的生日.之后每一 ...

随机推荐

  1. JAVA长连接demo

    http://blog.csdn.net/caomiao2006/article/details/38830475 JAVA长连接demo 2014-08-25 23:20 4767人阅读 评论(2) ...

  2. iOS开发——TTS文本发音

    iOS的文本转发音,从iOS7开始,iOS系统自带这个功能.能够实现中文.英文的发音.而且实现的起来非常方便.就像我看到有的博主说的三行代码搞定. (在iOS7之前(目前已不适配了),比如iOS6实现 ...

  3. 14_输出映射2_resultMap

    [resultMap] 如果查询出来的列名和pojo的属性名不一致,通过定义一个resultMap对列名和pojo属性名之间做一个映射列表. 1.定义resultMap,(在UserMapper.xm ...

  4. log4j使用细节

    问题一:打印不同类的类名信息? 在log4j中通常是通过Logger.getLogger(class)指定所打印的类名,但是当我们需要打印不同类信息时,目前只能这样做,在不同的类文件中构建不同的log ...

  5. 24种设计模式--原型模式【Prototype Pattern】

    今天我们来讲原型模式,这个模式的简单程度是仅次于单例模式和迭代器模式,非常简单,但是要使用好这个模式还有很多注意事项.我们通过一个例子来解释一下什么是原型模式. 现在电子账单越来越流行了,比如你的信用 ...

  6. 《精通CSS-高级Web标准解决方案》阅读计划

    第一周     第1章 基础知识 1 第2章 为样式找到应用目标 1 第3章 可视化格式模型 1 第4章 背景图像效果 1       第二周     第5章 对链接应用样式 1 第6章 对列表应用样 ...

  7. c语言位运算符

    C语言既具有高级语言的特点,又具有低级语言的功能. 所谓位运算是指进行二进制位的运算. C语言提供的位运算: 运算符   含义  &   按位与  |   按位或  ∧   按位异或  ∽   ...

  8. wampserver下升级php7

    1.下载php7   http://windows.php.net/download#php-7.0 选择 VC14 x86 Thread Safe  64位选X64 32位选X86 2.下载VC14 ...

  9. hadoop1——map到reduce中间的shuffle过程

    ---恢复内容开始--- shuffle和排序 过程图如下: MapReduce确保每个reduce的输入都按键排序,系统执行排序的过程——将map输出作为输入传给reduce——成为shuffle, ...

  10. 《疯狂java讲义》笔记 1-5章

    1.编译语言和解释语言理解,摘自李刚老师的<疯狂Java讲义>第三版: 就是说,Java和.net都是编译型有事解释型语言.编译型就是根据不同平台编译成不同的可执行机器码,编译过程中会进行 ...