<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Mog 的技术博客</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/mog/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/mog/feed.xml</link>
    <description />
    <lastBuildDate>Wed, 17 Apr 2019 04:09:27 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Paas 比较</title>
      <link>https://docs.microsoft.com/archive/blogs/mog/azure-paas-offer-comparison</link>
      <pubDate>Mon, 25 Apr 2016 05:47:07 GMT</pubDate>
      <dc:creator><![CDATA[Maud L]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/mog/?p=125</guid>
      <description><![CDATA[在2015的微软//Build大会中，Mark介绍了当前微软的公有云服务栈，提出Open Choice at Every Layer。



The Next Generation of...]]></description>
      <content:encoded><![CDATA[在2015的微软//Build大会中，Mark介绍了当前微软的公有云服务栈，提出Open Choice at Every Layer。

<a href="https://msdnshared.blob.core.windows.net/media/2016/04/image001.png"><img class="alignnone wp-image-135 size-full" src="https://msdnshared.blob.core.windows.net/media/2016/04/image001.png" alt="image001" width="624" height="350" /></a>

<a href="https://channel9.msdn.com/Events/Build/2015/3-618">The Next Generation of Azure Compute Platform with Mark Russinovich</a>

Open意味着更有活力，更多选择。这诚然是个好事，不过也意味着要学习更多知识。我对Azure Cloud Service 有4年的使用经验，ServiceFabric则是刚刚入门。在学习SF的过程中发觉，文档组织的有些混乱，脑子里的知识很零散，很容易遗忘。在看CloudFoundry时，发觉更加困难，不知如何开始。

为了把知识连接和巩固起来，我从Azure Cloud Service和中提炼出一些关键概念，然后在SF中寻找相关文档，学习它的运行方式。同时，发掘两个PaaS明显区别，进行详细比较并理解其缘由。在学习CloudFoundry时，用了同样的办法。作为学习的一个产出，有了如下这篇文章。文章中的误差和错误在所难免，随着进一步学习，会继续更新。
<h1>Candidate</h1>
Azure Cloud Service（或者叫Azure PaaS v1）, Service Fabric, Cloud Foundry

&nbsp;
<h1>Factor</h1>
<strong>Architecture</strong><strong>：</strong>从架构入手观察全局，避免迷失在细节中。

<strong>Dependency</strong><strong>：</strong>依赖性决定了PaaS能在哪里运行，移植性。

<strong>Application running environment</strong><strong>：</strong>应用程序的运行环境决定了应用程序能够控制的环境，从而影响应用程序的功能（Dev Functionality）。

<strong>Dev Functionality</strong><strong>：</strong>如用那种开发语言，什么操作系统，存储访问方式等等。

<strong>Deploy</strong><strong>：</strong>部署，包含PaaS环境的搭建以及应用程序的部署/更新。

<strong>Telemetry</strong><strong>：</strong>Logging &amp; Metrics &amp; Analytics。

<strong>Auto-recovering</strong><strong>：</strong>PaaS如何检测和自动恢复故障中的服务，需要多少人为干预。

<strong>Availability</strong><strong>：</strong>PaaS基础服务以及应用程序的可用性分析。

<strong>Scalability</strong><strong>：</strong>PaaS基础服务以及应用程序的规模伸缩性。

<strong><span style="text-decoration: line-through">Performance</span></strong><strong>：</strong>性能的比较太复杂，所以目前把它排除在外。

&nbsp;
<h1>Comparison</h1>
<h2>Architecture</h2>
Azure Cloud Service 架构和组件

<a href="https://msdnshared.blob.core.windows.net/media/2016/04/image002.png"><img class="alignnone wp-image-145 size-full" src="https://msdnshared.blob.core.windows.net/media/2016/04/image002.png" alt="image002" width="623" height="411" /></a>

<a href="https://msdnshared.blob.core.windows.net/media/2016/04/image003.png">Service Fabric 架构和组件</a>

<a href="https://msdnshared.blob.core.windows.net/media/2016/04/image003.png"><img class="alignnone wp-image-155 size-full" src="https://msdnshared.blob.core.windows.net/media/2016/04/image003.png" alt="image003" width="623" height="679" /></a>

Cloud Foundry架构和组件

<a href="https://msdnshared.blob.core.windows.net/media/2016/04/image004.png"><img class="alignnone wp-image-165 size-full" src="https://msdnshared.blob.core.windows.net/media/2016/04/image004.png" alt="image004" width="623" height="646" /></a>

&nbsp;
<h2>Dependency</h2>
azure cloud service 比azure iaas vm诞生还要早，是azure platform土著居民。Azure platform FC原生支持 cloud service stateless vm instance， 其好处是支持真正的 elistic auto scale，直接向azure 数据中心申请计算资源(vm)。不过cloud service 并非建立在azure iaas之上，其紧密依赖于FC，难以独立运行在其他云计算环境中。

service fabric 也叫 azure paas v2, 它建立在 azure公开的iaas服务上（VM, VM extension, VM scaleSet, Virtual Network, Azure Resource Manager，Availability Group），对azure内部没有紧密耦合。这样使其具有迁移其他平台的潜力，未来可以独立与Azure cloud，运行在win server， azure stack，甚至linux cluster上。

Cloud Foundry 是独立与云计算提供商的PaaS解决方案，它不和Cloud provider直接打交道，而是通过Bosh来完成系统的provision。bosh暴露了一组接口 cloud provider interface（CPI），cloud provider 只要实现了CPI，即可运行Bosh，也就可以运行Cloud Foundry。 因此，CF对底层云平台的依赖度很小

&nbsp;
<h2>App Running Environment</h2>
Azure Cloud Service每个服务（角色）实例独占一台虚拟机，用户代码的访问边界即为虚拟机。运行环境初始化(如安装中间件)可由startup  script完成。Azure虚拟机有多种型号可供选择，每个型号拥有不同的CPU核数，内存空间，硬盘空间和网络带宽。不过，相比于Process，Container，虚拟机的资源粒度仍然过大。

Service Fabric运维一组虚拟机组成的虚拟机资源池，所有的用户服务作为micro-service运行在虚拟机中。即每个服务实例作为虚拟机中的一个进程，共享虚拟机资源。SF提供load balance底层服务，减少进程间的影响。中间件如何安装(Java runtime, web server)? 另外，随着Win server 2016发布，SF未来会支持 Windows Container作为服务运行环境。

Cloud Foundry 同样运维一组虚拟机组成的资源池（Cells），用户代码运行在虚拟机上创建的Container中，container可以分配给定的内存大小，隔离性要比Process好。中间件可由buildpack完成，使用方便。集成 Diego后， Cloud foundry可以支持Docker镜像，这样，用户可以充分控制Container环境。

&nbsp;
<h2>Dev Functionality</h2>
<table style="height: 480px" border="1" width="721">
<tbody>
<tr>
<td width="156"></td>
<td width="156">Azure PaaS V1</td>
<td width="156">Service Fabric</td>
<td width="156">Cloud Foundry</td>
</tr>
<tr>
<td width="156">Dev Language</td>
<td width="156">提供了多种语言的SDK，还提供接口运行cmd, exe</td>
<td width="156">提供了.net SDK，还提供接口运行cmd, exe</td>
<td width="156">通过Buildpack 支持多种语言</td>
</tr>
<tr>
<td width="156">OS environment</td>
<td width="156">Windows Server Only</td>
<td width="156">Windows Server (Linux in plan)</td>
<td width="156">Linux, Windows(Iron Foundry)</td>
</tr>
<tr>
<td width="156">Middleware</td>
<td width="156">使用startup script 配置</td>
<td width="156">无*</td>
<td width="156">Buildpack, stack</td>
</tr>
<tr>
<td width="156">Storage</td>
<td width="156">本地磁盘做临时存储，external storage保存 persistent data</td>
<td width="156">Reliable collection做replica数据同步。external storage保存 persistent data</td>
<td width="156">external storage保存 persistent data</td>
</tr>
</tbody>
</table>
&nbsp;
<h2>Deploy</h2>
Azure Cloud Service：当部署用户Application时，通过SDK把代码和配置文件编译&amp;打包，之后可以通过Azure管理门户或者Management API直接部署到云端。代码Update支持deployment swap 和 in-place update 两种方式，都能实现无downtime的部署。

Service Fabric：在部署应用程序前，需要首先在云平台上provision Service Fabric Cluster，随后通过Service Fabric提供的API来部署应用。 SF支持 in-place update，在更新过程中，支持health monitor和rollback。

Cloud Foundry：同样，在部署应用程序前，需要首先在云平台上provision Cloud Foundry Instance，随后通过SF CLI来部署应用。CF支持 Blue-Green Deployment，类似与azure deployment swap。Cloud Foundry的 Application没有层级关系（Azure Cloud Service 有 Role的概念，Service Fabric 有 Service），因此在部署和更新复杂系统时，Cloud Foundry可能需要更多的额外操作。另外当应用程序的部署规模很大时，使用deployment swap更新并不合适。

&nbsp;
<h2>Telemetry</h2>
Azure Cloud Service：使用Windows Azure Diagnostics Extension（WAD） 来收集Log 和 Metrics。Log可由Azure IoT套件来做进一步分析， Metrics可以作为Scale Service 的输入，实现根据负载而动态Scale。

Service Fabric：默认的Logging机制是输出到虚拟机的Event Log，然后通过 WAD来上传到云存储。Metrics同样可以使用WAD。同时，SF提供Application level的Metrics接口，可由Application 汇报workload，SF的load balancer根据metrics完成负载平衡的工作。

Cloud Foundry：Cloud Foundry 使用 Loggregator 组件来完成Log/Metrics 收集工作，同时暴露接口供下游数据筛选和处理（Firehose, Nozzles）<a href="https://docs.cloudfoundry.org/loggregator/architecture.html">https://docs.cloudfoundry.org/loggregator/architecture.html</a>

运行在Azure VM之上的 CF也可以使用 WAD完成虚拟机level的logging

&nbsp;
<h2>Auto-Recovering</h2>
Azure Cloud Service：通过GuestOS Agent检测并汇报VM state给Fabric Controller，Fabric Controller take action (restart, reboot, reimage) to recover Instances. Cloud Service自身不能检测到僵尸实例，需要由外部系统来检测其功能性。<a href="https://blogs.msdn.microsoft.com/kwill/2011/05/05/windows-azure-role-architecture/">https://blogs.msdn.microsoft.com/kwill/2011/05/05/windows-azure-role-architecture/</a>

Service Fabric: 一旦replica或host VM出问题，由failover service 检测并恢复。

Cloud Foundry：由HM9000 或 Diego中的Converger 和Cell Rep 检测和恢复Application。

&nbsp;
<h2>Availability</h2>
Azure Cloud Service: 对于用户的服务，当每个Role运行至少2个实例时，Azure平台确保99.95%的服务可用性。Azure Cloud Service的底层模块RDFE，FC…都是Azure平台组件，客户不需要维护其可用性。

Service Fabric：Azure平台保证cluster 99.95%的可用性。应用程序通过创建多replica确保高可用性。SF的基础服务也作为特别的Application运行在SF cluster上。

Cloud Foundry: 应用程序通过创建多实例确保高可用性。CF controller不同模块的高可用性方案不相同，参考 <a href="https://docs.cloudfoundry.org/concepts/high-availability.html">https://docs.cloudfoundry.org/concepts/high-availability.html</a>，相比于上述两种Paas offer，CF高可用需要更多的配置。

&nbsp;
<h2>Scalability</h2>
Azure Cloud Service: 直接从Azure数据中心申请虚拟机资源，伸缩非常方便。通过Scale Service可以方便的实现auto-scale。虚拟机大小可以调整，但是需要重新部署。

Service Fabric：SF的scale分两个level，application level指的是application partition和replica的伸缩，可以通过SF API完成。SF cluster level指的是虚拟机数量的伸缩，这个需要通过Azure Management API来调整ScaleSet规模。

Cloud Foundry：CF的情况和SF类似，CF也是个cluster的管理系统，其scale也分两个level， application level指的是application instance的伸缩，既可以调整instance大小（Memory），也可以调整数量（container 数量）。CF cluster level指的是虚拟机数量的伸缩，这个通过BOSH来进行调整。
<h1>Additional</h1>
作为微软尝试的第一代云产品，从名字就能看出Azure  Cloud  Service被委于完整云计算模型的重任。通过增加Azure Drive，VM Role等特性，Azure cloud service不断扩展其应用场景。不过，微软经过一些战略调整，不断细分云计算的场景，目前已拥有从IAAS层到SaaS层很多的云计算产品，用户可以根据自由度、开发、维护复杂度的要求，选择更加合适的技术产品。Azure Cloud Service 的很多技术 (如GuestOS Agent, WAD)都已经被融合到Azure新一代的服务中，自身则会逐渐淡出。

<a href="https://msdnshared.blob.core.windows.net/media/2016/04/image005.png"><img class="alignnone size-full wp-image-175" src="https://msdnshared.blob.core.windows.net/media/2016/04/image005.png" alt="image005" width="653" height="367" /></a>

&nbsp;

<a href="https://channel9.msdn.com/Events/Build/2016/B885">VM Scale Sets &amp; Open Source PaaS on Azure: Deep Dive</a>

Service Fabric在public GA之前，它已经被微软内部使用了很长一段时间，很多云服务如SQL Azure Database，Azure Cache都建立在SF上。SF提供原生的分布式存储ReliableCollection，并且支持Partition和Replica，这为开发分布式应用提供了很多便利。不过服务隔离性和中间件机制的缺乏限制了应用场景，随着Windows Container技术的加入，以上不足有望被填补。

&nbsp;

CloudFoundry是开源项目，而且对云计算平台的依赖性很弱，具有很强的移植性，这些特性对担心被平台lock in的客户很有吸引力。CF使用Container技术，支持Linux Server和Windows，适用的场景很广。同时，CF提供service broker，marketplace，offer以及billing模块，管理功能更为丰富。劣势在于：CloudFoundry搭建和维护比较复杂，与微软产品相比，更难掌握。一些云提供商如IBM，Pivotal CloudFoundry代用户托管CloudFoundry cluster，减少了用户的工作。]]></content:encoded>
    </item>
    <item>
      <title>Windows Azure Storage性能调优（二）</title>
      <link>https://docs.microsoft.com/archive/blogs/mog/windows-azure-storage</link>
      <pubDate>Sun, 07 Dec 2014 13:05:00 GMT</pubDate>
      <dc:creator><![CDATA[Maud L]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/mog/2014/12/07/windows-azure-storage/</guid>
      <description><![CDATA[上一篇我们提出的方法都是通过减少网络传输时间来提升性能。接下来，我会简介一下WAS的系统结构，然后解释如何设计程序来减少WAS端执行时间。&nbsp;

WAS架构
WAS性能目标
分散工作负...]]></description>
      <content:encoded><![CDATA[<p>上一篇我们提出的方法都是通过减少网络传输时间来提升性能。接下来，我会简介一下WAS的系统结构，然后解释如何设计程序来减少WAS端执行时间。&nbsp;</p>
<ul>
<li><a href="#arch">WAS架构</a></li>
<li><a href="#perftarget">WAS性能目标</a></li>
<li><a href="#distribute">分散工作负载</a></li>
<li><a href="#avoidhot">避免Table热区</a></li>
<li><a href="#query">优化Table查询</a></li>
</ul>
<p>&nbsp;</p>
<h1><a name="arch"></a>WAS架构</h1>
<p>Windows Azure Storage是一个三层架构，分为前端(FE)、分区层(Partition)、分布式存储层(DFS)。</p>
<p>前端负责接受客户端HTTP(S)连接，验证身份，计费以及写日志。</p>
<p>分布式存储层为实际的物理存储。无论Blob，File，Queue还是Table，其最终都已Binary形式存放在此层。此层确保数据在三个独立的物理节点上保存备份，避免数据丢失。</p>
<p>分区层提供了逻辑数据结构的访问能力。它理解数据的逻辑抽象结构，能够按照用户的请求来正确存取DFS层数据。同时，分区层实现了跨数据中心的备份同步功能。</p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/2235.image001.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/2235.image001.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/2235.image001.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/2235.image001.png" alt="" border="0" /></a></p>
<p>WAS的设计目标是存储海量数据，实现大并发访问。海量存储由分布式存储层来实现，而大并发的目标是如何实现的呢？</p>
<p>从物理存储来看，大并发的压力来自于磁盘I/O和网络带宽。在DFS层，数据的物理存储单位是Extent，100MB-1GB不等。每个对象（Blob，Table或Queue）都由一个或多个Extent拼接而，除此之外，数据还有三个备份。这些Extent离散分布在多台独立的服务器上，因此，用户对对象的访问被分摊在多台服务器上，以达到高吞吐量。</p>
<p>数据逻辑结构的处理需要耗费Memory和CPU资源。Partition层将逻辑数据拆分为多个Partitions，比如：每个Blob, Queue即为一个Partition，Table中PartitionKey相同的所有实体存放在一个Partition。Partition层每台服务器负责服务一组Partitions，Master服务器会检测每台服务器的负载，并动态调整Partitions的分配。由于所有数据都存放在DFS层，因此负载调整会非常迅速。通过动态负载均衡，Partition层确保有足够的资源来响应客户请求。</p>
<p>&nbsp;</p>
<h1><a name="perftarget"></a>WAS性能目标</h1>
<p>微软在MSDN上公布了WAS的性能目标，这些数据是WAS的性能上限，若用户使用WAS超过指标时，WAS会返回给客户503（Server Busy）或500（Operation Timeout）。当用户接到此类错误时，建议用户采用回退的方式重试操作，这样可以缓解短暂高峰造成的WAS服务压力。</p>
<p>简要来说，性能目标有两层：Partition级和Storage Account级。Storage Account级的目标如下：</p>
<table style="width: 546px;" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<p><strong>Total Account Capacity</strong></p>
</td>
<td>
<p><strong>Total Request Rate (assuming 1KB object size)</strong></p>
</td>
<td>
<p><strong>Total Bandwidth for a Geo-Redundant Storage Account</strong></p>
</td>
<td>
<p><strong>Total Bandwidth for a Locally Redundant Storage Account</strong></p>
</td>
</tr>
<tr>
<td valign="top">
<p>500 TB</p>
</td>
<td valign="top">
<p>Up to 20,000 entities or messages per second</p>
</td>
<td valign="top">
<p>*Ingress: Up to 10 gigabits per second</p>
<p>*Egress: Up to 20 gigabits per second</p>
</td>
<td valign="top">
<p>*Ingress: Up to 20 gigabits per second</p>
<p>*Egress: Up to 30 gigabits per second</p>
</td>
</tr>
</tbody>
</table>
<p>* Ingress refers to all data (requests) being sent to a storage account.</p>
<p>* Egress refers to all data (responses) being received from a storage account.</p>
<p>Partition级别的性能目标为：</p>
<table style="width: 561px;" border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td>
<p><strong>Target Throughput for Single Blob</strong></p>
</td>
<td>
<p><strong>Target Throughput for Single Queue (1 KB messages)</strong></p>
</td>
<td>
<p><strong>Target Throughput for Single Table Partition (1 KB entities)</strong></p>
</td>
</tr>
<tr>
<td valign="top">
<p>Up to 60 MB per second, or up to 500 requests per second</p>
</td>
<td valign="top">
<p>Up to 2000 messages per second</p>
</td>
<td valign="top">
<p>Up to 2000 entities per second</p>
</td>
</tr>
</tbody>
</table>
<p>若要了解更多细节，可以访问如下官方文档</p>
<p><a href="https://msdn.microsoft.com/en-us/library/azure/dn249410.aspx">https://msdn.microsoft.com/en-us/library/azure/dn249410.aspx</a></p>
<p>&nbsp;</p>
<h1><a name="distribute"></a>分散工作负载</h1>
<p>了解到WAS的性能目标后，开发者在设计时就要尽量避免达到性能上限。</p>
<p>举个实例。两个Role通过WAS Queue来做消息中转，消息吞吐量在高峰时段可能达到10K，那么如果设计人员只使用单Queue，则Queue会成为性能瓶颈，无论增加多少Role虚拟机数量，吞吐量被始终限制在2000 messages/sec左右。解决方案就是使用多个Queue来分散工作负载。</p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/0624.image002.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/0624.image002.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/0624.image002.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/0624.image002.png" alt="" border="0" /></a></p>
<p>同样的道理，用户要避免使用一个WAS Account来放置过多的虚拟机磁盘，否则有可能达到WAS Account级的性能上限20,000事务/s。建议每个WAS Account最多放置40块虚拟机磁盘。</p>
<p>&nbsp;</p>
<h1><a name="avoidhot"></a>避免Table热区</h1>
<p>前面提到过，一个Table中PartitionKey相同的实体放置在同一个Partition里，而Partition的性能上限是2000实体访问/s。如果应用程序设计不当的话，有可能出现Table的某些Partition访问过于频繁，造成WAS响应慢或者报错（500，503）。举个具体的例子：一个日志系统，使用日期做PartitionKey，那么所有的写操作都会hit当日的Partition。如下通过测试，给大家一个直观的感觉。</p>
<p><strong>场景：</strong>一个博客系统，使用WAS Table来存储博客内容。现有两种设计：</p>
<p><strong>设计1</strong><strong>：</strong>PartitionKey=date, RowKey=BlogThreadID+PostID</p>
<p>代码如下：</p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/1781.image003.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/1781.image003.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/1781.image003.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/534x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/1781.image003.png" alt="" border="0" /></a></p>
<p><strong>设计2</strong><strong>：</strong>PartitionKey=date+BlogThreadID, RowKey=PostID</p>
<p>代码和设计1类似，唯独修改了PartitionKey</p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/4606.image004.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/4606.image004.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/4606.image004.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/534x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/4606.image004.png" alt="" border="0" /></a></p>
<p>两种设计保存的数据内容是一致的，只是PartitionKey和RowKey的格式不同。最终执行效率能有多大区别呢？我这里使用压力测试来检验，结果差别巨大！</p>
<p>设计1，随着用户负载增加，服务延迟越来越高，吞吐量维持在1380/s</p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/2804.image005.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/2804.image005.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/2804.image005.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/2804.image005.png" alt="" border="0" /></a></p>
<p>设计2，随着用户负载增加，服务延迟平稳，而吞吐量达到了2600/s</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/4743.image007.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/4743.image007.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/4743.image007.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/4743.image007.png" alt="" border="0" /></a></p>
<p>可见，开发者在设计PartitionKey，RowKey时要避免Hot Partition，否则其造成的性能差距是巨大的。</p>
<p>设计2仍然存在问题：Partition按照PartitionKey字符串排序，编号相近的Partition很有可能被分配在同一台Partition服务器。设计2可能造成Partition服务器压力过载，从而影响WAS响应速度。为了提高性能，用户可以考虑倒置日期字符串来避免Partition集中。</p>
<p>&nbsp;</p>
<h1><a name="query"></a>优化Table查询</h1>
<p>Table的实体有多个属性，通过筛选属性值，可以返回需要的实体。筛选条件的选择会直接影响查询效率。</p>
<p>数据库用户都知道索引能够避免扫描操作，减少I/O，从而提升效率。Table也支持索引，不过每个Table只有一个索引，即PartitionKey+RowKey。指定了PartitionKey和RowKey的查询叫做Point Query，他的查询效率也最高。</p>
<p>如果没有指定PartitionKey和RowKey，而是单单筛选其它属性，则查询退化为全表的扫描操作，效率会大大降低。不仅如此，之前WAS架构提到过，Table数据保存在多个Partition中，而Partitions分布在多台Partition服务器上。如果遍历的数据散布在多台服务器，则WAS会在遍历完一台Partition服务器后返回客户端一个Token，客户端凭Token来再次请求，WAS会将请求导向到下一个服务器，多次Roundtrip过程会耗费更多时间。</p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/3225.image009.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/3225.image009.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/3225.image009.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/3225.image009.png" alt="" border="0" /></a></p>
<p>以访问WAD Table为例，我测试了用全表查询，局部范围查询，指定Partition查询和Point查询来搜索同一个实体。</p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/0160.image010.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/0160.image010.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/0160.image010.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/0160.image010.png" alt="" border="0" /></a></p>
<p>结果表明PartitionKey 范围越小，查询速度就越快。</p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/8508.image012.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/8508.image012.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/8508.image012.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/533x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/8508.image012.png" alt="" border="0" /></a></p>
<p>因此，用户的查询操作要尽量指定PartitionKey或者一个PartitionKey范围，避免大数据范围的扫描。而设计者需要根据业务需求考虑如何组合PartitionKey，使查询语句尽量多的使用到PartitionKey。</p>
<h1>Table Storage 设计模式</h1>
<p>最近微软公布了一份官方文档，列举了使用Azure Table Storage下的一些设计技巧，感兴趣的读者可以直接访问如下文档</p>
<p>https://azure.microsoft.com/en-us/documentation/articles/storage-table-design-guide/?rnd=1</p>
<p>&nbsp;</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/3823.storage-table-design-image05.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/3823.storage_2D00_table_2D00_design_2D00_image05.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/3823.storage-table-design-image05.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/3823.storage_2D00_table_2D00_design_2D00_image05.png" alt="" border="0" /></a></p>]]></content:encoded>
    </item>
    <item>
      <title>Windows Azure Storage 性能调优（一）</title>
      <link>https://docs.microsoft.com/archive/blogs/mog/windows-azure-storage-2</link>
      <pubDate>Mon, 01 Dec 2014 04:32:00 GMT</pubDate>
      <dc:creator><![CDATA[Maud L]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/mog/2014/12/01/windows-azure-storage-2/</guid>
      <description><![CDATA[WAS (Windows Azure Storage)...]]></description>
      <content:encoded><![CDATA[<p>WAS (Windows Azure Storage) 是微软提供的云存储服务，也是整个Azure服务的基石，几乎所有Azure产品都依赖于WAS。WAS的性能也紧紧影响到其他服务的性能。在这篇文章中，我总结一下使用WAS的经验，与大家分享。</p>
<p>WAS使用HTTP(S) 协议，访问操作通过发送HTTP请求来完成。访问操作的时间可由Request传输时间、Response传输时间和服务端处理时间组成。</p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/7455.image002.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/7455.image002.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/7455.image002.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/7455.image002.png" alt="" border="0" /></a></p>
<p>为了提高性能，开发人员可以尝试减少每个过程的时间。总结起来，有如下一些方法：</p>
<ul>
<li>减少网络延迟
<ul>
<li><a href="#nearestdc">选择最近的数据中心</a></li>
<li><a href="#cdn">使用CDN减少网络延迟</a></li>
</ul>
</li>
<li>减少数据传输
<ul>
<li><a href="#compress">压缩Blob</a></li>
<li><a href="#json">使用JSON格式</a></li>
<li><a href="#projection">使用实体映射（Entity Projection）</a></li>
</ul>
</li>
<li><a href="#minmizetrans">减少请求事务数量</a></li>
<li><a href="#parallize">并发访问</a></li>
<li><a href="#other">其它网络优化</a></li>
<li>减少服务端受理时间(二）</li>
<li>避免Throttling（二）</li>
</ul>
<p>&nbsp;此文章介绍网络相关的优化方法。</p>
<h2><a name="nearestdc"></a>选择最近的数据中心</h2>
<p>网络延迟对任何网络服务的影响都很大。Azure在全球有17+个数据中心，为了减少网络延迟，开发者应该选择离产品用户最近的数据中心。如下网站可以显示你当前的网络访问全球每个数据中心WAS Blob的速度。</p>
<p><a href="https://azurespeedtest.azurewebsites.net/" target="_blank">https://azurespeedtest.azurewebsites.net/</a></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/7571.image003.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/7571.image003.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/7571.image003.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/7571.image003.png" alt="" border="0" /></a></p>
<p>在选择数据中心时，若没有特别原因，开发者需要尽量避免跨数据中心的网络访问，这样会造成额外的流量费用。比如说用户部署Cloud Service在香港，而Cloud Service需要访问 新加坡的WAS来读取数据，那么这个跨数据中心的访问将被额外收取流量费，而且跨数据中心的网络延迟会比数据中心内部访问要高。</p>
<h2><a name="cdn"></a>使用CDN减少网络延迟</h2>
<p>CDN（Content distributed Network） 是一个常用的HTTP访问优化方法， 其原理是：在某个区域或全球范围内分布很多文件缓存节点，尽可能的离用户更近。当用户需要从站点下载文件时，用户直接访问离他最近的缓存节点，若请求的数据已被缓存，则直接返回客户，起到网络提速功能；若请求的数据未被缓存，则从源站点下载。</p>
<p>Azure CDN有两个版本：Global版的CDN由EdgeCast公司提供，在全球24个地点部署有缓存节点。大陆版的CDN委托给ChinaCache运营，主要针对中国区网络。通过Azure CDN，可以加速WAS Blob， Cloud Service 和 Azure Website的网络访问速度，不过WAS Queue和Table不可以。</p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/6431.image005.jpg" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/6431.image005.jpg"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/6431.image005.jpg" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/6431.image005.jpg" alt="" border="0" /></a></p>
<p align="center">Edgecast 缓存节点分布</p>
<h2>减少数据传输</h2>
<p>减少传输的数据量可以减少Request或Response的网络传输时间。减少数据的方式可以有压缩数据和裁剪无关数据。</p>
<h3><a name="compress"></a>压缩 Windows Azure Blob</h3>
<p>Blob类似于文件存储，若文件是文本类型，则压缩文件可以节省很多带宽。比如网站的js，css文件，常用浏览器都支持Gzip压缩，因此在上传前，不妨用Gzip压缩文件。实例代码如下：</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var blobClient = new CloudBlobClient(account.BlobEndpoint, account.Credentials);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; blobClient.RetryPolicy = retry;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var blobContainer = blobClient.GetContainerReference("bundles");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var blob = blobContainer.GetBlockBlobReference("jquery-1.6.2.js");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; blob.Properties.ContentType = "text/javascript";</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; blob.Properties.CacheControl = "public, max-age=3600";</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; blob.Properties.ContentEncoding = "gzip";</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using (var compressed = blob.OpenWrite())</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using (var gzip = new GZipStream(compressed, CompressionMode.Compress))</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using (var filestream = File.OpenRead(filePath))</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; filestream.CopyTo(gzip);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>如下的一个测试显示，jquery文件经过压缩后，只有原先的1/4大。</p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/5736.image006.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/5736.image006.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/5736.image006.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/5736.image006.png" alt="" border="0" /></a></p>
<h3><a name="json"></a>使用JSON格式</h3>
<p>在早期版本的WAS，Table和Queue只支持ATOM格式的消息（XML），现在的版本支持JSON。相比于XML，JSON体积更小，可以更快的传输，并且更容易操作。如下为同样的数据以ATOM和JSON形式呈现，可以明显发现JSON格式更小。</p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/7701.image007.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/7701.image007.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/7701.image007.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/7701.image007.png" alt="" border="0" /></a></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/4810.image008.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/4810.image008.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/4810.image008.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/454x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/4810.image008.png" alt="" border="0" /></a></p>
<p>使用JSON格式，需要在HTTP request header上添加 header <strong>&ldquo;Accept: application/json&rdquo;</strong></p>
<h3><a name="projection"></a>使用实体映射（Entity Projection）</h3>
<p>在查询WAS Table时，默认情况下WAS会返回符合实体的所有属性。而在一些情况下，客户端只需要实体的某些属性。WAS Table提供了查询参数 &ldquo;$select&rdquo;，通过使用此参数，可以让WAS Table只返回客户端需要的属性。</p>
<p>&nbsp;&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/0143.image009.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/0143.image009.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/0143.image009.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/0143.image009.png" alt="" border="0" /></a></p>
<h2><a name="minmizetrans"></a>减少请求事务数量</h2>
<p>拿现实的例子作比方：淘宝小店发货，如果店主每接到一单就叫快递来拿，那么快递小哥要反复奔走于快递站和淘宝店之间，在取件上耗费了大量的时间。一个更可行的方式是，店主把要发的货屯起来，在每天下午3点叫快递一并拿走，这样快递小哥一天只用跑一次，节省了大量时间。访问WAS也是如此，用户需要发现访问数据的关联性，尽量把所做的操作并在更少的请求事务上。</p>
<p>WAS Table支持Batch操作，即把一组Table操作放到一个Request中完成。Batch即节省了大量耗费在数据传输上的时间，同时又确保事务原子特性。如下代码测试使用Batch一次插入100条记录，通过比较发现，其性能要比逐条插入高出4到16倍。</p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/6787.image010.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/6787.image010.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/6787.image010.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/6787.image010.png" alt="" border="0" /></a></p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/5228.image012.jpg" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/5228.image012.jpg"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/5228.image012.jpg" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/5228.image012.jpg" alt="" border="0" /></a></p>
<p>&nbsp;<br />需要注意的是，WAS Table Batch操作要求访问的实体存在同一个Partition中，操作上限为100。</p>
<h2><a name="parallize"></a>事务并行化</h2>
<p>当事务之间没有先后依赖关系时，可以通过并行执行来提升速度。用过网络快车、网络蚂蚁的人都懂的，使用多个线程同时下载文件的不同部分，然后拼接起来，可以达到更快的下载速度。</p>
<p>对于WAS Block blob，每个Blob又一组Block组成，用户可以用多线程并发的上传Block，当文件的所有Block上传完毕后，最后发一个请求，将blocks按顺序组装起来。由此方法，可以实现多线程上传，提高传输的吞吐量。图下所示，通过并发上传可以基本达到单Blob的性能上限60MB/s。</p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/5824.image014.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/5824.image014.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/5824.image014.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/5824.image014.png" alt="" border="0" /></a></p>
<p>用户可以直接使用微软提供的一个工具AZCOPY，它默认就使用多线程上传。</p>
<p><a href="https://blogs.msdn.com/b/windowsazurestorage/archive/2012/12/03/azcopy-uploading-downloading-files-for-windows-azure-blobs.aspx">https://blogs.msdn.com/b/windowsazurestorage/archive/2012/12/03/azcopy-uploading-downloading-files-for-windows-azure-blobs.aspx</a></p>
<h2><a name="other"></a>其它网络优化</h2>
<ul>
<li>当网络传输的object小于1400字节时，禁用Network Nagle会提高传输性能。</li>
<li>禁用 Expect 100-Continue，减少网络Roundtrip。</li>
<li>.NET 通过ServicePointManager来限制Outbound 连接数，默认为2。若程序大量使用并发连接，需要手动调整此阀值。</li>
</ul>
<p>&nbsp;</p>]]></content:encoded>
    </item>
    <item>
      <title>Microsoft Azure 托管服务监测</title>
      <link>https://docs.microsoft.com/archive/blogs/mog/microsoft-azure</link>
      <pubDate>Fri, 10 Oct 2014 01:41:00 GMT</pubDate>
      <dc:creator><![CDATA[Maud L]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/mog/2014/10/10/microsoft-azure/</guid>
      <description><![CDATA[没有什么服务是无敌的。当服务发生故障时，每分每秒都意味着客户的影响和经济损失。因此，通过服务监测来迅速报警，以及快速定位和修复问题都非常重要。
从外部或者客户角度来看，Azure托管服务和其他传统服...]]></description>
      <content:encoded><![CDATA[<p>没有什么服务是无敌的。当服务发生故障时，每分每秒都意味着客户的影响和经济损失。因此，通过服务监测来迅速报警，以及快速定位和修复问题都非常重要。</p>
<p>从外部或者客户角度来看，Azure托管服务和其他传统服务没有什么区别，因此，很多的监测工具仍然可以用在托管服务上，比如Probe探测，Google analytics。</p>
<p>若从服务内部接口来看，Azure托管服务和传统服务还是有很多不同之处的：托管服务运行在分布式环境下，每台服务器都是无状态的，随时可能被回收重建。因此，把监测日志放到服务器不太可靠，日志随时可能丢失。而且，把日志分散的保存在多台机器上也不利于查询使用。另外，托管服务往往依赖其他云服务（如存储），用户不能够登录到这些依赖服务的后端来检测，其实用户也没有这个义务。</p>
<p>在这篇文章，我想列举一下适用于Azure托管服务的监测方法，当作一个索引。</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="113">
<p><strong>监测位置</strong></p>
</td>
<td valign="top" width="270">
<p><strong>监测方法</strong></p>
</td>
</tr>
<tr>
<td rowspan="4" valign="top" width="113">
<p>虚拟机</p>
</td>
<td valign="top" width="270">
<p><a href="#wadlog">虚拟机日志</a>：</p>
<p>如：Application Log, Windows Event Log, IIS Log 及自定义日志文件</p>
</td>
</tr>
<tr>
<td valign="top" width="270">
<p><a href="#wadperf">性能数据</a>：</p>
<p>如CPU，Memory，每秒请求数&hellip;</p>
</td>
</tr>
<tr>
<td valign="top" width="270">
<p><a href="#guestoslog">GuestOS Agent Log</a></p>
</td>
</tr>
<tr>
<td valign="top" width="270">
<p><a href="#party3rd">3rd party monitoring tool</a> (e.g. NewRelic)</p>
</td>
</tr>
<tr>
<td rowspan="2" valign="top" width="113">
<p>外部</p>
</td>
<td valign="top" width="270">
<p><a href="#probe">Probe</a></p>
</td>
</tr>
<tr>
<td valign="top" width="270">
<p>analytics (e.g. google analytics)</p>
</td>
</tr>
<tr>
<td rowspan="3" valign="top" width="113">
<p>Azure内置监控功能</p>
</td>
<td valign="top" width="270">
<p><a href="#dashboard">Azure服务仪表盘（Status Dashboard）</a></p>
</td>
</tr>
<tr>
<td valign="top" width="270">
<p><a href="#storagemetrics">Storage Metrics &amp; Logs</a></p>
</td>
</tr>
<tr>
<td valign="top" width="270">
<p><a href="#portal">Azure管理网站</a></p>
</td>
</tr>
<tr>
<td valign="top" width="113">
<p>其他</p>
</td>
<td valign="top" width="270">
<p><a href="#appinsight">Application Insight</a></p>
</td>
</tr>
<tr>
<td valign="top" width="113">
<p>&nbsp;</p>
</td>
<td valign="top" width="270">
<p>&nbsp;</p>
</td>
</tr>
</tbody>
</table>
<p>&nbsp;</p>
<h2><a name="wadlog"></a>虚拟机日志</h2>
<p>有很多的日志默认输出到服务器本地磁盘上，比如Application Log，Windows Event Log 和 IIS Log，对于此类型的日志，全都可以使用WAD来自动上传到Azure Storage上，这样便于数据查看和过滤。在<a href="https://blogs.msdn.com/b/mog/archive/2014/09/23/cloud-service-availability-design.aspx#wad">可靠性设计这篇文章</a>中已经提到了配置方法，大家也可以直接看官方MSDN文档。</p>
<p><a href="https://azure.microsoft.com/en-us/documentation/articles/cloud-services-dotnet-diagnostics/">https://azure.microsoft.com/en-us/documentation/articles/cloud-services-dotnet-diagnostics/</a></p>
<p><a href="https://msdn.microsoft.com/library/azure/gg433048.aspx">https://msdn.microsoft.com/library/azure/gg433048.aspx</a></p>
<p>&nbsp;</p>
<h2><a name="wadperf"></a>性能数据</h2>
<p>性能数据是评价服务器运行状态、负载的重要指标。WAD可以收集性能数据并存放到Table Storage上。</p>
<p>用WAD收集性能数据的方式与收集日志基本一致，用户可以从常用Counter集合中勾选需要的Counter，并调整采样率，也可以直接输入定义的Counter名称。</p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/8524.image001.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/8524.image001.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/8524.image001.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/8524.image001.png" alt="" border="0" /></a></p>
<p>至于如何查看和使用这些性能数据，开发者可以自己编写监控程序来读取数据，也可以使用第三方的监控服务，如Azure Watch。我这里演示一下如何使用Azure管理网站查看性能数据。</p>
<ol>
<li>首先配置好WAD，部署网站。</li>
<li>登陆Azure管理网站，导航到托管服务配置页，把Monitoring LEVEL设置为 VERBOSE</li>
<li>导航到MONITOR页，点击 &ldquo;ADD METRICS&rdquo;，从列表中选择需要监视的Counter集合</li>
<li>性能数据会显示到上方的图表中，数据一般会有10-20分钟的延迟。</li>
</ol>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/5126.image003.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/5126.image003.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/5126.image003.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/5126.image003.png" alt="" border="0" /></a></p>
<ol>
<li>用户可以点击&ldquo;ADD RULE&rdquo;来添加警报规则，当性能数据超越规定阈值时，可以触发警报，自动发信给系统管理员。</li>
</ol>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/3817.image005.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/3817.image005.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/3817.image005.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/3817.image005.png" alt="" border="0" /></a></p>
<p>&nbsp;</p>
<h2><a name="guestoslog"></a>GuestOS Agent Log</h2>
<p>每个托管服务实例上运行了一组代理进程，他们的作用是初始化程序运行环境，启动程序并自动恢复程序，同时也会和平台沟通，汇报实例的运行状态。</p>
<p>作为一般的开发者，不需要关心此日志。不过感兴趣的话，可以远程登陆虚拟机，日志保存在C:\Logs目录下。这里推荐同事的一篇博文，他解释了代理进程的功能和启动流程。</p>
<p><a href="https://blogs.msdn.com/b/kwill/archive/2011/05/05/windows-azure-role-architecture.aspx">https://blogs.msdn.com/b/kwill/archive/2011/05/05/windows-azure-role-architecture.aspx</a></p>
<p>&nbsp;</p>
<h2><a name="party3rd"></a>3<sup>rd</sup>&nbsp;party monitoring tool</h2>
<p>有一些第三方的工具，以托管服务启动任务的形式运行，从虚拟机上观测程序，并把数据返回到他们的管理网页上，比如New Relic。我没有用过，大家可以自己尝试</p>
<p><a href="https://newrelic.com/azure">https://newrelic.com/azure</a></p>
<p>&nbsp;</p>
<h2><a name="probe"></a>Probe</h2>
<p>我也不知道怎么翻译，总之是一种常见的监测方式。从外部定期发送请求到网站（或服务），探测服务器是否相应正确，以及响应是否迅速。很多第三方服务商提供此类服务。Azure平台也提供此类服务&rdquo;ENDPOINTS MONITORING&rdquo;，不过还处在Beta阶段，功能单一，不支持发信报警功能，好处是不要钱。</p>
<p>ENDPOINT MONITORING 的设置方法如下：</p>
<ol>
<li>打开Azure管理网站，导航到托管服务的配置页。</li>
<li>设置ENDPOINTS节，填写监测的服务地址，以及监测地理位置。</li>
</ol>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/4466.image007.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/4466.image007.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/4466.image007.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/4466.image007.png" alt="" border="0" /></a></p>
<ol>
<li>在托管服务DASHBOARD页观看监测记录</li>
</ol>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/8078.image009.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/8078.image009.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/8078.image009.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/8078.image009.png" alt="" border="0" /></a></p>
<p>&nbsp;</p>
<h2><a name="dashboard"></a>Azure 服务仪表盘（Azure Status Dashboard）</h2>
<p>Azure平台提供了一个服务仪表盘，它显示了所有Azure服务当前的运行状态。用户也可以查询历史上发生过的问题。当用户的程序出现故障时，不妨先看一眼仪表盘，若是Azure服务故障，可以等待Azure平台修复问题。</p>
<p><em>Global Azure</em></p>
<p><a href="https://azure.microsoft.com/en-us/status/#current"><em>https://azure.microsoft.com/en-us/status/#current</em></a><em></em></p>
<p><em>China Azure (21</em><em>世纪互联)</em></p>
<p><a href="https://www.windowsazure.cn/support/service-dashboard/"><em>https://www.windowsazure.cn/support/service-dashboard/</em></a><em></em></p>
<p>&nbsp;</p>
<h2><a name="portal"></a>Azure管理网站</h2>
<p>Azure管理网站集成了各种Azure服务，用户可以从这里获得服务的运行状态信息。当Azure平台出故障时，平台也会推送通知到受影响用户的管理网站上。因此，当服务出问题时，用户可以登录管理网站，看看是否有平台故障，或者是否托管服器处在不正常的状态。</p>
<p>&nbsp;</p>
<h2><a name="storagemetrics"></a>Storage Metrics &amp; Logs</h2>
<p>Azure Storage提供的服务器端的日志和监测功能。此功能常被用作Storage相关的Troubleshooting。</p>
<p><a href="https://msdn.microsoft.com/en-us/library/azure/hh343270.aspx">https://msdn.microsoft.com/en-us/library/azure/hh343270.aspx</a></p>
<p>&nbsp;</p>
<h2><a name="appinsight"></a>Application Insight</h2>
<p>大家可能发现，Azure托管服务的监测日志比较分散，用户需要很多的配置才能监测服务。 App Insight是微软正在开发的产品，他集成了上述多种监测日志，然后在统一的面板上显示关于目标服务的所有信息。App Insight简化了用户对监测的配置操作，使用和更容易洞察服务的问题。目前，此产品还在beta阶段，版本变化比较快，不过很有潜力。参考如下链接：</p>
<p><a href="https://msdn.microsoft.com/en-us/library/dn481095.aspx">https://msdn.microsoft.com/en-us/library/dn481095.aspx</a></p>
<p>&nbsp;</p>]]></content:encoded>
    </item>
    <item>
      <title>Microsoft Azure 托管服务可靠性设计</title>
      <link>https://docs.microsoft.com/archive/blogs/mog/microsoft-azure-2</link>
      <pubDate>Tue, 23 Sep 2014 07:40:00 GMT</pubDate>
      <dc:creator><![CDATA[Maud L]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/mog/2014/09/23/microsoft-azure-2/</guid>
      <description><![CDATA[服务的可靠性至关重要，而影响可靠性的因素多种多样，例如程序错误、硬件故障或依赖服务故障，以及人为失误。工程师们要对每个方面做出合理的设计来避免或容错各种故障，才能使服务达到高的可靠性。除此之外，当...]]></description>
      <content:encoded><![CDATA[<div class="WordSection1">
<p>服务的可靠性至关重要，而影响可靠性的因素多种多样，例如程序错误、硬件故障或依赖服务故障，以及人为失误。工程师们要对每个方面做出合理的设计来避免或容错各种故障，才能使服务达到高的可靠性。除此之外，当服务已经中断，如何快速定位问题并解决也是产品设计时就需要考虑的。这篇文章只以<a href="https://blogs.msdn.com/b/mog/archive/2014/09/22/azure-cloud-service.aspx" target="_blank">&ldquo;CalculateCloud&rdquo;为例</a>，讨论一下托管服务的高可靠性设计。</p>
<p>拿出CalculateCloud的架构图看看，影响服务可靠性的有如下几方面：</p>
<ul>
<li>程序故障：这里只WebRole/WorkerRole代码出错。</li>
<li>服务器故障：这里只运行角色代码的虚拟机发生故障。</li>
<li>依赖外部服务的故障/维护：这里指托管服务所使用的Table或Queue发生故障。</li>
</ul>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/5355.image001.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/5355.image001.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/5355.image001.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/300x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/5355.image001.png" alt="" border="0" /></a></p>
<p>接下来针对每种故障，我们逐个寻找对策：</p>
<ul>
<li><a href="#code">程序故障</a></li>
<li><a href="#storage">Storage服务故障</a></li>
<li><a href="#server">服务器故障/维护</a></li>
<li><a href="#more">追求更高的可靠性</a></li>
<li><a href="#summary">总结</a></li>
</ul>
<h2><a name="code"></a>程序故障</h2>
<p>托管服务程序的调试和其他代码一样，在开发和测试阶段，可以使用模拟器在本地环境运行和调试代码。比如CalculateCloud非法字符输入的问题，在本地调试时，Visual Studio能够提示WorkerRole代码抛出异常。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/1588.image003.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/1588.image003.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/1588.image003.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/1588.image003.png" alt="" border="0" /></a>&nbsp;</p>
<p><a name="wad"></a>对于此问题，使用try/catch来受理异常，问题就搞定了。需要留意的是，一旦部署到生产环境下，debug会比较困难。相比之下，丰富的程序日志能帮助用户监测服务状态和快速定位问题。下面的代码使用默认的 System.Diagnostics.Trace类来输出日志。</p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>if</em><em> (msg != </em><em>null</em><em>)</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>string</em><em> result = </em><em>null</em><em>;</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>try</em><em></em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>// handle job here.</em><em></em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>var</em><em> nums = msg.AsString.Split(</em><em>','</em><em>);</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>double</em><em> answer = </em><em>double</em><em>.Parse</em><em>(nums[0]) * </em><em>double</em><em>.Parse</em><em>(nums[1]);</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>Thread</em><em>.Sleep</em><em>(</em><em>10000);</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result = </em><em>string</em><em>.Format</em><em>(</em><em>"Job handled. {0}*{1}={2}"</em><em>, nums[0], nums[1], answer);</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>catch</em><em> (</em><em>FormatException</em><em>)</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result = </em><em>"invalid input. msg="</em><em> + msg.AsString;</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>Trace</em><em>.TraceError</em><em>(</em><em>result);</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>catch</em><em> (</em><em>Exception</em><em> ex)</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result = </em><em>string</em><em>.Format</em><em>(</em><em>"failed to handle job. msg={0}"</em><em>,</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; msg.AsString);</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>Trace</em><em>.TraceError</em><em>(</em><em>result + </em><em>", error="</em><em> + ex);</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>finally</em><em></em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AddResultEntry(result);</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; queue.DeleteMessage(msg);</em></p>
<p><em>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</em><em>Trace</em><em>.TraceInformation</em><em>(</em><em>result);</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</em><em></em></p>
<p>角色项目默认使用Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener来写日志。程序日志会以二进制形式写到虚拟机的本地磁盘C:下。由于角色可以运行在多台虚拟机上，那么日志也会分散在多台服务器上，登录每台虚拟机来查看日志会很麻烦，这里可以使用托管服务的一个组件&ldquo;Windows Azure Diagnostics（WAD）&rdquo;来解决问题。WAD会在每台虚拟机上启动两个额外进程，它们定期将虚拟机上的日志上传到Azure Storage上，用户可以从一个集中的地方访问所有日志。WAD的配置如下：</p>
<p>打开的Calculate.Worker的角色属性面板，在Diagnostics节中选择&ldquo;Custom Plan&rdquo;，然后设置存储日志信息的Azure Storage 连接字符串，再点击&ldquo;Edit&hellip;&rdquo;按钮，修改Application Logs的上传频率为1分钟。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/2630.image005.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/2630.image005.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/2630.image005.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/2630.image005.png" alt="" border="0" /></a>&nbsp;</p>
<p>WebRole同样需要添加Trace代码并配置WAD，这里不再重复。</p>
<p>WAD开启后，应用程序日志会被定期上传到Table Storage &ldquo;WADLogsTable&rdquo;表，大家可以用各种工具来查看日志，比如集成在VisualStudio &ldquo;Server Explorer&rdquo;中的Azure资源查看器。</p>
<p>WADLogsTable的索引键为&rdquo;PartitionKey+RowKey&rdquo;,其中PartitionKey格式为字符串<strong><em>&rdquo;</em><em>0&rdquo;+datetime.Tick</em></strong>。一个常用的查询语句如下，其含义为：查询部署ID为 08cd08dce90d4516b21be7bfe6936c75，在9/24/2014 1:00:00 AM UTC后，级别至少为&ldquo;警告&rdquo;的所有日志。</p>
<p><em>PartitionKey</em><em> gt '0635471172000000000' and DeploymentId eq '08cd08dce90d4516b21be7bfe6936c75' and Level lt '4'</em></p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/3286.image007.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/3286.image007.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/3286.image007.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/3286.image007.png" alt="" border="0" /></a>&nbsp;</p>
<p>WAD提供了很多的功能，感兴趣的读者可以查看MSDN官方文档</p>
<p><a href="https://azure.microsoft.com/en-us/documentation/articles/cloud-services-dotnet-diagnostics/">https://azure.microsoft.com/en-us/documentation/articles/cloud-services-dotnet-diagnostics/</a></p>
<p><a href="https://msdn.microsoft.com/library/azure/gg433048.aspx">https://msdn.microsoft.com/library/azure/gg433048.aspx</a></p>
<h2><a name="storage"></a>Storage服务故障</h2>
<p>一个复杂系统必定会存在外部资源引用。当被依赖的资源发生故障时，往往会影响到系统的可靠性。在设计时，需要考虑到如何容错外部资源的故障，尽量降低对系统的影响，或者至少能快速定位出问题的资源，以便采取准确的恢复措施。</p>
<p>微软保证Azure Storage 在99.9%的时间内能正常运转，也就是说每个月最多可能offline 44分钟。因此，准备应对Storage故障是很有必要的。</p>
<p>Azure Storage运行在普通的硬件上，硬件故障不可避免，另外由与系统维护，版本更新或者负载均衡等原因，都会造成某些Storage服务器短暂下线，而其正在执行的用户请求就会失败。对于此类型的访问失败，用户只需重试一次，请求就会被移交给健康的服务器节点进行受理。因此，微软强烈建议客户端收到错误（超时或HTTP500）时重试几下。实际上，Azure Storage .net library默认已经实现了重试机制，并默认开启。对于直接使用REST API来访问Storage的代码，务必捕获异常状态并重试。</p>
<p>虽然较少发生，不过有时候重试也会失败，开发者需要自己捕获失败并受理。检查CalculateCloud的WorkerRole.cs文件，你会发现一旦访问storage的重试机制失败，WorkerRole程序就会因为未受理的异常而crash，从而导致更严重的系统服务中断。解决办法就是try/catch storage异常。这里我把错误记录下来，查询日志时若发现大量Storage错误信息，则说明Storage存在问题，需要采取措施。</p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>while</em><em> (</em><em>true</em><em>)</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>try</em><em></em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>var</em><em> msg = queue.GetMessage();</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &hellip;&hellip;</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>catch</em><em> (</em><em>Exception</em><em> ex)</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>// write trace.</em><em></em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>Trace</em><em>.TraceError</em><em>(</em><em>"error occured when access storage. "</em><em> + ex);</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</em><em></em></p>
<p>另外，为了进一步提高Storage的可靠性，可以使用Storage的异地备份-只读（RA-GRS）的功能，此功能将用户的数据存放在相邻的两个数据中心上，当其中一个数据中心故障，用户可以读取另一个数据中心备份。此功能可以保证数据读取<strong>99.99%</strong>的可靠性，而数据写入可靠性仍为<strong>99.9%</strong>。需要注意的是：数据在两个数据中心间同步，会有几分钟的延迟。使用RA-GRS的方法如下。</p>
<p>在Azure管理网站上导航到Storage配置页，更改Replication模式到&ldquo;READ-ACCESS GEO REDUNDANT&rdquo;</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/7183.image009.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/7183.image009.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/7183.image009.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/7183.image009.png" alt="" border="0" /></a>&nbsp;</p>
<p>除此之外，还要修改代码，让Storage客户端能够自动failover到异地备份上。以CalculateCloud显示运算结果为例，需要更改WebRole的Default.aspx.cs文件。</p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>protected</em><em> </em><em>void</em><em> Button2_Click(</em><em>object</em><em> sender, </em><em>EventArgs</em><em> e)</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>try</em><em></em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>var</em><em> connstr = </em><em>RoleEnvironment</em><em></em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .GetConfigurationSettingValue(</em><em>"DataConnection"</em><em>);</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>var</em><em> storageAccount = </em><em>CloudStorageAccount</em><em>.Parse</em><em>(connstr);</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>var</em><em> tableClient = storageAccount.CreateCloudTableClient();</em></p>
<p><span style="background-color: #ffff99;"><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tableClient.DefaultRequestOptions.LocationMode =<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>LocationMode</em><em>.PrimaryThenSecondary</em><em>;</em></span><em></em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &hellip;&hellip;</em></p>
<p>&nbsp;</p>
<h2><a name="server"></a>服务器故障/维护</h2>
<p>托管服务是PAAS层面的服务，服务器的可靠性由平台来负责。微软对托管服务可靠性保证（SLA）的定义为：当每个角色运行在至少两个实例（即虚拟机）时，保证托管服务99.95%的在线时间。这个SLA如何理解呢？我先解释一下Azure平台的可靠性设计。</p>
<p>托管服务下的每台虚拟机被同时分配到两种域：故障域和更新域。故障域是物理上的概念，数据中心上每组机柜既是一个物理域，它有独立的电源管理和网络组件，一般认为，多个故障域不会同时发生故障。更新域是逻辑上的概念，管理器(FC)按照规则，同一时间只会维护一个更新域中的虚拟机。在Azure平台上，运行同样角色的三台实例（即虚拟机）是这样分配的：</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="144">
<p>&nbsp;</p>
</td>
<td valign="top" width="144">
<p>故障域1</p>
</td>
<td valign="top" width="144">
<p>故障域2</p>
</td>
<td valign="top" width="144">
<p>故障域3</p>
</td>
</tr>
<tr>
<td valign="top" width="144">
<p>更新域1</p>
</td>
<td valign="top" width="144">
<p>WebRole_IN_0</p>
</td>
<td valign="top" width="144">
<p>&nbsp;</p>
</td>
<td valign="top" width="144">
<p>&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="144">
<p>更新域2</p>
</td>
<td valign="top" width="144">
<p>&nbsp;</p>
</td>
<td valign="top" width="144">
<p>WebRole_IN_1</p>
</td>
<td valign="top" width="144">
<p>&nbsp;</p>
</td>
</tr>
<tr>
<td valign="top" width="144">
<p>更新域3</p>
</td>
<td valign="top" width="144">
<p>&nbsp;</p>
</td>
<td valign="top" width="144">
<p>&nbsp;</p>
</td>
<td valign="top" width="144">
<p>WebRole_IN_2</p>
</td>
</tr>
</tbody>
</table>
<p>如此一来，无论是硬件故障还是系统维护，同一角色的虚拟机不会同时下线 。这样解释大家就明白了吧，托管服务SLA保证了角色实例不会同时下线，而开发者需要在这种机制上建立可靠的程序。</p>
<p>那么用户如何充分利用平台特性来实现服务的高可靠呢，<a href="https://www.davidchappell.com/about/index.php" target="_blank">David Chappell</a>总结了Azure编程三原则：</p>
<ul>
<li>每个托管服务拥有至少一个角色</li>
<li>每个角色需要运行至少两个实例</li>
<li>即使部分实例故障，整个Azure托管服务仍可正常运行</li>
</ul>
<p>大家若想了解更多信息，可以下载David所写的托管服务编程模型白皮书，或者翻看一下《Microsoft Azure开发与应用》。我这里用三原则来检查一下CalculateCloud项目。</p>
<p>第一原则是强制原则，显然满足。对于第二原则，目前每个角色只运行在一台虚拟机上。这个修改很方便，打开每个角色的属性面板，修改 Instance Count为 2。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/5460.image011.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/5460.image011.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/5460.image011.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/5460.image011.png" alt="" border="0" /></a>&nbsp;</p>
<p>第三原则比较复杂，具体讲就是要求同一角色的实例拥有同样的功能，并且保持无状态，其中一个实例下线后，另一个可以替代它执行任务。</p>
<p>对于WebRole，实例运行在负载均衡器（SLB）后，负载均衡会探测实例的状态，并将Request分配给健康的实例上。为保证第三原则，每个WebRole实例不应该将客户的状态（如Session）保存在本地（进程或磁盘），否则一旦虚拟机下线，运行在它上面的客户将遭遇信息丢失的问题。Calculate.Web角色访问的所有数据都保存在外部Azure Storage上，因此满足第三原则。</p>
<p align="center"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/4544.image013.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/4544.image013.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/4544.image013.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/389x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/4544.image013.png" alt="" border="0" /></a>&nbsp;</p>
<p>Calculate.Worker不在本地保存中间结果，运算结果也直接写到Azure Storage了，所以也满足地三原则。</p>
<p align="center"><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/3108.image014.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/3108.image014.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/3108.image014.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/376x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/3108.image014.png" alt="" border="0" /></a>&nbsp;</p>
<p>大家可以把CalculateCloud部署到云端测试一下，当重启一个Calculate.Web实例和一个Calculate.Worker实例时，整个服务仍然正常运行。即使Calculate.Worker所有的实例都重启，得益于Azure Queue持久化消息通信，服务仍然可用，只是计算工作会有所延迟。</p>
<p>&nbsp;</p>
<h2><a name="more"></a>追求更高的可靠性</h2>
<p>有些客户对服务可靠等级有更高的要求。比如发生数据中心级别的故障时，服务仍然可用。Azure平台能做到吗？当然能啊，只要愿意花更多的钱。</p>
<p>微软提供的另一个服务叫做Traffic Manager（简称TM），它工作在一组托管服务前，将客户的服务请求按照规则重定向到背后的某个托管服务上。以下图为例，解释一下流程。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/0285.image015.jpg" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/0285.image015.jpg"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/0285.image015.jpg" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/0285.image015.jpg" alt="" border="0" /></a>&nbsp;</p>
<ol>
<li>客户要访问服务（www.contoso.com），首先发DNS解析请求到公网的DNS服务器。对于服务提供商，他需要事先从域名提供商那里购买一个公网域名consoto.com。</li>
<li>DNS服务器将服务的域名（www.contoso.com）重定向到TM的域名（*.trafficmanager.net）。对于服务提供商，他需要事先配置子域名www.contoso.com的解析规则，将其CNAME到TM。</li>
<li>DNS服务器访问TM的域名服务器来索要解析的IP地址。</li>
<li>TM根据规则选择一个托管服务（现在TM支持所有公网服务）。TM会检测托管服务的状态，确保选择健康的服务。另外，用户可定义选择方式：根据性能，平均分配，或者故障迁移。</li>
<li>TM域名服务器把选择的服务IP地址返回给公网DNS服务器，最后返回给客户。</li>
<li>客户得到服务IP后，直接访问服务地址，不再经过TM。</li>
</ol>
<p>有了Traffic Manager，就可以做跨数据中心的服务备份了。以CalculateCloud为例，可以这样设计：在香港和新加坡各部署一个托管服务，然后在香港创建一个Azure Storage账号，并开启RA-GRS。香港的托管服务直接访问Azure Storage主地址，新加坡的托管服务访问Azure Storage主地址来写数据，而读取操作直接访问Azure Storage备份的地址（&lt;accountname&gt;-secondary.table.core.windows.net）。最后，创建一个Traffic Manger，将两个托管服务的地址添加到Endpoint Set中。</p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/5751.image017.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/5751.image017.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/5751.image017.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/5751.image017.png" alt="" border="0" /></a></p>
<p>这种设计可以在单个托管服务下线时，保持整个系统的可用性。而当整个香港数据中心下线时，系统进入只读运行模式。具体实施步骤如下：</p>
<p>首先把托管服务部署到香港数据中心和新加坡数据中心。为了让新加坡的服务访问Storage备份，还需要更新Default.aspx.cs代码。</p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>protected</em><em> </em><em>void</em><em> Button2_Click(</em><em>object</em><em> sender, </em><em>EventArgs</em><em> e)</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>try</em><em></em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>var</em><em> connstr = </em><em>RoleEnvironment</em><em></em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .GetConfigurationSettingValue(</em><em>"DataConnection"</em><em>);</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>var</em><em> storageAccount = </em><em>CloudStorageAccount</em><em>.Parse</em><em>(connstr);</em></p>
<p><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>var</em><em> tableClient = storageAccount.CreateCloudTableClient();</em></p>
<p><span style="background-color: #ffff99;"><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; tableClient.DefaultRequestOptions.LocationMode = </em></span></p>
<p><span style="background-color: #ffff99;"><em>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </em><em>LocationMode</em><em>.SecondaryThenPrimary</em><em>;</em></span><em></em></p>
<p>&nbsp;</p>
<p>接下来，登录Azure管理网站，创建一个Traffic Manager。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/1616.image019.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/1616.image019.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/1616.image019.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/1616.image019.png" alt="" border="0" /></a>&nbsp;</p>
<p>然后，打开TM的Endpoint配置页，把我之前部署的两个托管服务添加进来。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/3531.image021.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/3531.image021.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/3531.image021.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/3531.image021.png" alt="" border="0" /></a>&nbsp;</p>
<p>搞定。Traffic Manager还有很多配置选项，感兴趣的朋友看看MSDN文档吧。</p>
<p><a href="https://msdn.microsoft.com/en-us/library/azure/hh744833.aspx">https://msdn.microsoft.com/en-us/library/azure/hh744833.aspx</a></p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/8030.image023.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/8030.image023.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/8030.image023.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/8030.image023.png" alt="" border="0" /></a>&nbsp;</p>
<p>当然，除了上面的设计方案外，咱还可以在每个数据中心创建单独的Azure Storage，这样即使数据中心故障，服务也完全可用。不过这需要对代码做更多的调整，确保客户访问每个托管服务都能得到一致的结果。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/1425.image025.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/1425.image025.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/1425.image025.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/1425.image025.png" alt="" border="0" /></a>&nbsp;</p>
<p>Traffic Manager是个结构简单的服务，它的可靠性保证（SLA）为99.99%。不过根据我的经验，从服务GA到目前为止，零故障。索契冬奥会官方网站的方案就用了TM，当时他们在四个数据中心部署了八个托管服务，用TM做负载平衡。</p>
<h2><a name="summary"></a>总结</h2>
<p>文章并没有涵盖所有Azure服务，而提到的方案也并非可以套用到所有场景下，这里只当作为抛砖引玉，供大家参考。这里总结一下提到的方法。</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="151">
<p><strong>故障类型</strong></p>
</td>
<td valign="top" width="424">
<p><strong>应对方法</strong></p>
</td>
</tr>
<tr>
<td valign="top" width="151">
<p>代码故障</p>
</td>
<td valign="top" width="424">
<ul>
<li>本地模拟器调试</li>
<li>用Windows Azure Diagnostics 收集日志</li>
</ul>
</td>
</tr>
<tr>
<td valign="top" width="151">
<p>Azure Storage故障</p>
</td>
<td valign="top" width="424">
<ul>
<li>重试</li>
<li>捕获和受理Storage Exception</li>
<li>启用异地备份-只读（RA-GRS）功能</li>
</ul>
</td>
</tr>
<tr>
<td valign="top" width="151">
<p>服务器故障</p>
</td>
<td valign="top" width="424">
<ul>
<li>Azure托管服务编程三原则</li>
</ul>
</td>
</tr>
<tr>
<td valign="top" width="151">
<p>数据中心大灾难</p>
</td>
<td valign="top" width="424">
<ul>
<li>Traffic Manager</li>
<li>多Azure Storage账户做Failover</li>
</ul>
</td>
</tr>
</tbody>
</table>
<p>另外，高可靠性意味着系统冗余，增加运营的成本和系统复杂度。用户需要在可靠性和各种指标之间找到合适的平衡点。</p>
</div>]]></content:encoded>
    </item>
    <item>
      <title>Microsoft Azure 托管服务实践 (CalculateCloud)</title>
      <link>https://docs.microsoft.com/archive/blogs/mog/microsoft-azure-calculatecloud</link>
      <pubDate>Mon, 22 Sep 2014 07:14:00 GMT</pubDate>
      <dc:creator><![CDATA[Maud L]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/mog/2014/09/22/microsoft-azure-calculatecloud/</guid>
      <description><![CDATA[Azure是微软推出的云服务品牌之一，主要提供IAAS和PAAS服务。在Azure平台建立初期，微软认为PAAS既拥有IAAS的计算功能，同时还比IAAS更容易管理和规模扩展。在这个理念的推动下，...]]></description>
      <content:encoded><![CDATA[<div class="WordSection1">
<p>Azure是微软推出的云服务品牌之一，主要提供IAAS和PAAS服务。在Azure平台建立初期，微软认为PAAS既拥有IAAS的计算功能，同时还比IAAS更容易管理和规模扩展。在这个理念的推动下，微软在Azure平台只推出了一个PAAS层的计算产品：托管服务（Hosted Service）。</p>
<p>托管服务将业务系统中的模块抽象成&ldquo;角色（Role）&rdquo;逻辑单元，开发者只需编写每个&ldquo;角色&rdquo;的代码，并声明每个角色的运行环境（网络端口，存储）即可。Azure托管服务控制器（FC，或Fabric Controller）会负责分配虚拟机，配置环境和执行角色代码。<br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/1452.logiccompare.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/1452.logiccompare.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/1452.logiccompare.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/1452.logiccompare.png" alt="" border="0" /></a></p>
<p style="text-align: center;">逻辑组件和托管服务组件的对应关系</p>
<p>相比于传统系统，托管服务的部署极为简练，并且，服务器的维护交给FC来自动完成。唯一不足之处是：托管服务的开发模型不同于传统Windows系统开发，传统已有程序不能直接运行在托管服务上，而用户要经过一定的学习才能掌握托管服务开发。</p>
<table border="1" cellspacing="0" cellpadding="0">
<tbody>
<tr>
<td valign="top" width="277">
<p>传统系统</p>
</td>
<td valign="top" width="277">
<p>托管服务</p>
</td>
</tr>
<tr>
<td valign="top" width="277">
<p>传统开发模型</p>
<p>需要购买/租借服务器</p>
<p>手动配置服务器环境</p>
<p>手动部署程序到服务器</p>
<p>定期维护服务器，打补丁</p>
<p>服务器故障时，手动做故障迁移</p>
</td>
<td valign="top" width="277">
<p>新开发模型，需要额外学习</p>
<p>通过Azure管理界面随时申请服务器资源。</p>
<p>用多少，付多少。</p>
<p>用户一键部署。</p>
<p>服务器维护，硬件故障迁移都有FC自动完成。</p>
</td>
</tr>
</tbody>
</table>
</div>
<div class="WordSection1">
<p>这里通过简单的项目示例，操练一下如何使用Azure托管服务。演示项目的源代码请在GitHub下载</p>
<p><strong><a href="https://github.com/mogliang/CalculateCloud" target="_blank">https://github.com/mogliang/CalculateCloud</a></strong></p>
<p>&nbsp;</p>
<h1>功能需求：</h1>
<p>简单的乘法运算处理。用户通过网页递交和查看运算任务。</p>
<h1>架构规划：</h1>
<p>搭建一个两层结构的系统。前端Web层接受用户的计算请求，做初步处理，然后递交给后端业务逻辑层受理。后端业务逻辑层受理完毕后，由前端层返回结果给用户。</p>
<p>前端层由WebRole实现，后端业务层由WorkerRole担任。WebRole使用Azure消息队列（Queue）向WorkerRole传递任务。WorkerRole将运算结果保存在Azure表（Table）中，由WebRole读取并显示给客户。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/5736.lb.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/5736.lb.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/5736.lb.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/5736.lb.png" alt="" border="0" /></a></p>
<p style="text-align: center;">托管服务的结构图</p>
<p>这里我们没有使用角色间TCP通信（或HTTP），是因为每个角色都部署在多台虚拟机上，若某台虚拟机故障或拓扑变化，将造成部分通信失败。当然，通过重新连接可以解决问题，只不过队列编程更加简便。</p>
<p>&nbsp;</p>
<h1>开发准备：</h1>
<ul>
<li>Visual Studio 2012/2013</li>
<li>安装 Microsoft Azure Tool for Visual Studio<br />https://azure.microsoft.com/en-us/downloads/</li>
</ul>
<p>&nbsp;</p>
<h1>实现步骤：</h1>
<p>启动Visual Studio，创建一个托管服务。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/4456.blog1_1.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/4456.blog1_5F00_1.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/4456.blog1_1.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/4456.blog1_5F00_1.png" alt="" border="0" /></a></p>
<p>按照之前规划，我们添加两个Role，一个Asp.net WebRole，取名Calculate.Web，另一个WorkerRole，取名Calculate.Worker。</p>
<p>首先编辑WebRole，在Cloud项目中双击Calculate.Web，打开Role属性面板。点击&ldquo;Settings&rdquo;页，添加一个连接字符串。Name为DataConnection，Value为&ldquo;UseDevelopmentStorage=true&rdquo;，表示使用云存储模拟器。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/3582.blog1_3.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/3582.blog1_5F00_3.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/3582.blog1_3.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/3582.blog1_5F00_3.png" alt="" border="0" /></a></p>
<p>这里使用的Setting叫做&ldquo;托管服务设置字段&ldquo;，其功能类似于.net 配置文件中的&lt;AppSettings&gt;，这些设置字段可以在服务运行过程中动态更改。</p>
<p><br />接下来编辑Calculate.Web项目。WebRole项目和普通的ASP.net项目几乎完全相同，只不过WebRole运行在一组虚拟机上，由负载均衡器来把用户请求均分给每台虚拟机。因此，在编程过程中要考虑到分布式环境限制，比如Session存储。WebRole中可以通过<a href="https://msdn.microsoft.com/en-us/library/azure/microsoft.windowsazure.serviceruntime.roleenvironment.aspx">RoleEnvironment</a>类来访问托管服务、角色环境等信息。</p>
<p>创建用户提交计算任务的页面。打开Default.aspx，按如下格式添加两个TextBox和一个Button。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/0458.blog1_4.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/0458.blog1_5F00_4.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/0458.blog1_4.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/0458.blog1_5F00_4.png" alt="" border="0" /></a></p>
<p>在Code-behind文件中引用如下名空间：</p>
<pre>using Microsoft.WindowsAzure.ServiceRuntime;<br />using Microsoft.WindowsAzure.Storage;<br />using Microsoft.WindowsAzure.Storage.Queue;</pre>
<p>然后添加Button Click事件处理函数，把用户输入的公式包裹在消息里，放入Azure Queue：</p>
<pre>        // Submit calculation job.<br />        protected void Button1_Click(object sender, EventArgs e)<br />        {<br />            // read Azure storage connection string from Cloud settings<br />            var connstr = RoleEnvironment.GetConfigurationSettingValue("DataConnection");<br /><br />            // create Azure Queue Client<br />            var storageAccount = CloudStorageAccount.Parse(connstr);<br />            var queueClient = storageAccount.CreateCloudQueueClient();<br /><br />            // get queue named "caljobqueue", create if it doesn't exist<br />            var queue = queueClient.GetQueueReference("caljobqueue");<br />            queue.CreateIfNotExists();<br /><br />            // warp user's input into queue message, add to queue.<br />            string msgstr = string.Format("{0},{1}",<br />                TextBox1.Text,<br />                TextBox2.Text);<br /><br />            queue.AddMessage(<br />                new CloudQueueMessage(msgstr));<br /><br />            // all done. Write application log<br />            System.Diagnostics.Trace.TraceInformation("Message added. " + msgstr);<br />        }</pre>
<p><em>&nbsp;</em></p>
<p>接下来，编辑Calculate.Worker。同样先为Calculate.Worker添加Azure Storage连接字符串</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/8540.blog1_5.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/8540.blog1_5F00_5.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/8540.blog1_5.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/8540.blog1_5F00_5.png" alt="" border="0" /></a></p>
<p>接着编辑Calculate.Worker项目代码，让Worker接收Azure Queue消息并处理。</p>
<p>WorkerRole的入口类为WorkerRole，继承自<a href="https://msdn.microsoft.com/library/azure/microsoft.windowsazure.serviceruntime.roleentrypoint.aspx">RoleEntryPoint</a>，他有三个重要函数</p>
<ul>
<li>OnStart() 角色实例初始化时被调用，用于执行开发人员添加到一些初始化任务，调用期间，角色实例在云端的状态报告为忙碌（busy）。</li>
<li>Run() 主要的业务逻辑，开发人员在此处定义应用程序将要完成的任务，所有的代码由一个无限循环控制，正常情况下不会退出该方法。</li>
<li>OnStop() 角色实例退出时执行该方法，开发者可以在此处添加代码执行后期数据处理和实例监控等。</li>
</ul>
<p>我们不需要做初始化和析构，因此只需要重写Run()函数：</p>
<pre>    public class WorkerRole : RoleEntryPoint<br />    {<br />        public override void Run()<br />        {<br />            // read Azure storage connection string from Cloud settings<br />            var connstr = RoleEnvironment.GetConfigurationSettingValue("DataConnection");<br /><br />            // create Azure Queue Client<br />            var storageAccount = CloudStorageAccount.Parse(connstr);<br />            var queueClient = storageAccount.CreateCloudQueueClient();<br /><br />            // get queue named "caljobqueue", create if it doesn't exist<br />            var queue = queueClient.GetQueueReference("caljobqueue");<br />            queue.CreateIfNotExists();<br /><br />            while (true)<br />            {<br />                // get message from queue, if queue is not empty, <br />                // it return one message, otherwise, return null<br />                var msg = queue.GetMessage();<br />                if (msg != null)<br />                {<br />                    // handle job here.<br />                    var nums = msg.AsString.Split(',');<br />                    double answer = double.Parse(nums[0]) * double.Parse(nums[1]);<br /><br />                    string result = string.Format("Job handled. {0}*{1}={2}", nums[0], nums[1], answer);<br />queue.DeleteMessage(msg);<br /><br />                    // add applciation log<br />                    Trace.TraceInformation(result);<br />                }<br />                Thread.Sleep(10000);<br />            }<br /><br />        }<br />}</pre>
<p>到此为止，项目实现了用户输入，和后端处理的逻辑。接下来编写结果反馈的代码。</p>
<p>在WorkerRole项目添加一个实体类，继承自Microsoft.WindowsAzure.Storage.Table.TableEntity</p>
<pre>using Microsoft.WindowsAzure.Storage.Table;<br />using System;<br />using System.Collections.Generic;<br />using System.Linq;<br />using System.Text;<br />using System.Threading.Tasks;<br /><br />namespace Calculate.Worker<br />{<br />    public class CalResultEntry:TableEntity<br />    {<br />        public string Result { set; get; }<br />    }<br />}</pre>
<p>然后在WorkerRole.cs中添加插入Table的函数</p>
<pre>        // initialize Table on Role start<br />        CloudTable _resultTable = null;<br />        public override bool OnStart()<br />        {<br />            // read Azure storage connection string from Cloud settings<br />            var connstr = RoleEnvironment.GetConfigurationSettingValue("DataConnection");<br /><br />            // create Azure Table Client<br />            var storageAccount = CloudStorageAccount.Parse(connstr);<br />            var tableClient = storageAccount.CreateCloudTableClient();<br /><br />            // get table named "calresulttable", create if it doesn't exist<br />            _resultTable = tableClient.GetTableReference("calresulttable");<br />            _resultTable.CreateIfNotExists();<br /><br />            return base.OnStart();<br />        }<br /><br />        void AddResultEntry(string result)<br />        {<br />            // PartitionKey+RowKey is table's primary index, must have<br />            var newentry = new CalResultEntry<br />            {<br />                PartitionKey = DateTime.UtcNow.ToString("yyyyMMdd"),<br />                RowKey = DateTime.UtcNow.Ticks.ToString(),<br />                Result = result<br />            };<br /><br />            var addOp = TableOperation.Insert(newentry);<br />            _resultTable.Execute(addOp);<br />        }</pre>
<p>然后修改Run()，让计算结果输出到Table上</p>
<pre>        var nums = msg.AsString.Split(',');<br />        double answer = double.Parse(nums[0]) * double.Parse(nums[1]);<br />        string result = string.Format("Job handled. {0}*{1}={2}", nums[0], nums[1], answer);<br /> <strong> AddResultEntry(result);</strong></pre>
<pre><strong> ......</strong></pre>
<p>最后，为WebRole添加读取Table的代码。</p>
<p>在WebRole项目，添加一个已有文件，引用WorkerRole的&ldquo;CalResultEntry.cs&rdquo;</p>
<p>打开Default.aspx，添加一个Label用来显示结果，再添加一个按钮。</p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/6136.blog1_7.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/6136.blog1_5F00_7.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/6136.blog1_7.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/6136.blog1_5F00_7.png" alt="" border="0" /></a></p>
<p>添加按钮点击的事件处理函数，读取当日的计算结果，显示出来</p>
<pre>        protected void Button2_Click(object sender, EventArgs e)<br />        {<br />            var connstr = RoleEnvironment.GetConfigurationSettingValue("DataConnection");<br />            var storageAccount = CloudStorageAccount.Parse(connstr);<br />            var tableClient = storageAccount.CreateCloudTableClient();<br />            var table = tableClient.GetTableReference("calresulttable");<br />            table.CreateIfNotExists();<br /><br />            // get today's result form table<br />            var results = from en in table.CreateQuery&lt;Calculate.Worker.CalResultEntry&gt;()<br />                          where en.PartitionKey == DateTime.UtcNow.ToString("yyyyMMdd")<br />                          select en;<br /><br />            // display result on page<br />            string resStr = "";<br />            foreach (var en in results)<br />            {<br />                resStr += en.Result + "&lt;br&gt;";<br />            }<br />            Label1.Text = resStr;<br />        }</pre>
<p>在此博客最后可以下载完整的项目代码。</p>
<h1>测试结果：</h1>
<p>Azure Tool提供了托管服务模拟器，开发者可以在本机上运行和调试托管服务。</p>
<p>按F5启动调试，点击桌面右下角模拟器图标，显示模拟器UI，每个控制台表示一个正在运行的虚拟机，绿灯表示程序在虚拟机上正常运行。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/5415.blog1_8.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/5415.blog1_5F00_8.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/5415.blog1_8.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/5415.blog1_5F00_8.png" alt="" border="0" /></a>&nbsp;&nbsp;</p>
<p>在弹出的浏览器窗口中，测试一些基本乘法，工作正常。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/8272.blog1_9.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/8272.blog1_5F00_9.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/8272.blog1_9.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/8272.blog1_5F00_9.png" alt="" border="0" /></a></p>
<p>大家应该发现，当输入非数字时，WorkerRole程序就会Crash，这是因为缺少输入数据格式检查。<strong>为了保持实例代码的简洁，项目缺少很多异常处理逻辑，读者需要注意并自行处理</strong>。</p>
<p>&nbsp;</p>
<h1>部署到云端</h1>
<p>在真正部署到云端前，开发者需要拥有一个Azure订阅。有国外信用卡和手机号码的朋友可以在<a href="https://azure.microsoft.com/en-us/">https://azure.microsoft.com/en-us/</a> 申请试用。国内的朋友能够从21世纪互联的<a href="https://www.windowsazure.cn/">https://www.windowsazure.cn/</a> 网站申请试用。</p>
<p>部署的方法有很多，我这里演示从Azure管理网站部署。最近，我和同事出版了一本Azure相关书籍《Microsoft Azure开发与应用》，其中对部署有更多的讨论，感兴趣的朋友可以买来看看。</p>
<p>首先注意，云端不能访问Azure Storage模拟器，因此必须修改WebRole和WorkerRole的连接字符串&ldquo;DataConnection&rdquo;，使用一个真实的Azure Storage。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/1526.blog1_10.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/1526.blog1_5F00_10.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/1526.blog1_10.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/1526.blog1_5F00_10.png" alt="" border="0" /></a></p>
<p>没有Azure Storage的话，通过Azure管理网站创建一个。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/6607.blog1_11.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/6607.blog1_5F00_11.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/6607.blog1_11.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/6607.blog1_5F00_11.png" alt="" border="0" /></a></p>
<p>打包托管服务项目。右键点击云项目，选择&ldquo;Package&rdquo;即可。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/2671.blog1_12.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/2671.blog1_5F00_12.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/2671.blog1_12.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/2671.blog1_5F00_12.png" alt="" border="0" /></a></p>
<p>打包后生成两个文件，cspkg包含了项目编译后的文件和托管服务的环境定义（ServiceDefintion.csdef）。Cscfg为托管服务的配置。配置文件中的内容可以在托管服务运行时动态调整。</p>
<p>下一步，登录Azure 管理网站。点击&ldquo;+NEW&rdquo;--&gt;&rdquo;CLOUD SERVICE&rdquo;--&gt;&rdquo;QUICK CREATE&rdquo;，给个名字，选择&ldquo;East Asia&rdquo;数据中心（香港）。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/1423.blog1_14.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/1423.blog1_5F00_14.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/1423.blog1_14.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/1423.blog1_5F00_14.png" alt="" border="0" /></a></p>
<p>创建成功后，点击进入托管服务项的详细页，再点击下方&ldquo;UPLOAD&rdquo;按钮上传托管服务包，勾选&ldquo;Deploy even if one or more roles contains a single instance&rdquo;。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/2678.blog1_15.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/2678.blog1_5F00_15.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/2678.blog1_15.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/2678.blog1_5F00_15.png" alt="" border="0" /></a></p>
<p>部署进度可以通过下方状态栏观察，部署成功后，服务仍然需要时间来创建和初始化虚拟机，最终当虚拟机进入&rdquo;Ready&ldquo;阶段时，服务正式运行。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/1033.blog1_16.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/1033.blog1_5F00_16.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/1033.blog1_16.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/1033.blog1_5F00_16.png" alt="" border="0" /></a></p>
<p>访问一下看看吧。服务的默认域名为&lt;servicename&gt;.cloudapp.net。</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/7167.blog1_18.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/7167.blog1_5F00_18.png"><img src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/7167.blog1_18.png" original-url="https://blogs.msdn.com/resized-image.ashx/__size/550x0/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72/7167.blog1_5F00_18.png" alt="" border="0" /></a></p>
<p><strong>本地<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/66/72/Download/2248.CalculateCloud.zip" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-66-72-Download/2248.CalculateCloud.zip">项目源文件下载</a></strong></p>
<p>&nbsp;</p>
</div>]]></content:encoded>
    </item>
  </channel>
</rss>