我最近做了一个Windows Azure上面的项目,自己在做的过程中遇到了很多问题。有的是我自己摸索解决,有的是到网上寻找零碎的信息结合起来解决的。我感觉应当把某些解决方法集中一下,方便我以后查阅,也方便其他人。本文涉及的内容主要是Azure的Cloud Service服务。

 

在Windows Azure中启用Trace

部署到Windows Azure上的代码是不能够用Visual Studio直接调试的,所以保留完善的Trace来诊断问题非常重要。用Windows Azure SDK 2.0建立的项目已经为云端Trace做好了准备,但并没有真正启用。现在我们来看怎么完善最后的工作。

 

Windows Azure Cloud Service项目的Trace是在主项目上集中配置的。打开主项目的Roles文件夹可以看到每个Role的cscfg文件。我们双击其中一个打开,默认是这样的:

Configuration

注意下面的Diagnostics段,勾选的话就打开了Trace。但是默认情况下,Trace使用的Storage Account是“DevelopmentStorage”其实就是本地的Azure模拟器。我们只要为其指定一个真正的Azure Storage Account就可以了。建议专门为Trace建立一个Storage Account,而不要使用其他数据的Account。如图即可选择一个Storage Account。

StorageAccount

 

在代码中,直接使用System.Diagnostics.Trace类的相应的方法,例如WriteLine或者TraceError等即可将Trace写入指定Storage Account的表格中。因为Azure的存储是收费的,所以分级使用Trace很重要,推荐使用TraceInformation,TraceError这样自带错误等级的方法。某些使用Windows Azure SDK老版本或者从其他项目移植为Azure项目时,App.config或者Web.config中可能缺少Trace Listener的设置,这样也无法在运行时正确写入Trace。我们可以打开WebRole或者WorkerRole项目自己的.config,确认有这样一段在configuration段之内:

 

 
  <system.diagnostics>
    <trace>
      <listeners>
        <add type="Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" name="AzureDiagnostics">
          <filter type="" />
        </add>
      </listeners>
    </trace>
  </system.diagnostics>
 

 

WebRole/WorkerRole项目引用的DLL等类库工程直接使用System.Diagnostics.Trace类即可,无需进行配置。

 

除了使用Trace类之外,Windows Event Log也很重要,因为诸如云服务运行时异常,崩溃之类的信息是在Windows Event Log当中的。我们可以在前面的Cloud Service主项目的配置页,单击Edit按钮来进行筛选。如果需要诸如性能计数器等额外信息,也可以在此选择:

image

 

最后是Trace的查看方法。经过以上配置,System.Diagnostics.Trace记录下来的Trace会保存到设定Storage Account的WADLogsTable表格中;Windows Event Logs记录到WADWindowsEventLogsTable表格中。在Azure的管理门户网站上是看不到表格存储的,需要用代码来读取其中的内容。当然,我们也可以用一些现成的第三方工具,比如我用的是这个Azure Storage Explorer

storageExplorer

 

如此一来,从启用Trace,代码中记录Trace到查看Trace的全套流程就都实现了。

 

在WebRole和WorkerRole的VM上安装Windows Server的功能

我们都知道Azure Cloud Service的WebRole和WorkerRole都是在各自的VM里面执行的,其VM的操作系统以及版本都可以在部署的时候进行指定。例如我们可以选择Windows Server 2012等系统。但是默认情况下大部分Windows Server的功能都是没有启用的。一台本地的服务器我们可以用Windows Server管理工具来安装新功能/角色,那么在Cloud Service下面怎么安装呢?答案是使用PowerShell。

 

首先我们需要确定所需功能的名称。在Windows Server本地的PowserShell中执行Get-WindowsFeature命令。如果没有本地的Windows Server,可以登录到Azure的云服务VM上,或者创建一台单独的VM,在里面执行。你将会看到如下的功能列表:

image

 

第一列是模拟在GUI模式下看到的树状的功能列表,而第二列就是在PowerShell中安装它所需的名称。记住这个名称。下面我们要在Cloud Service虚拟机部署的时候利用部署任务来安装Windows Feature。在需要该Windows功能的WebRole或者WorkerRole项目中添加一个扩展名是ps1的文件,并且选择总是拷贝到Output文件夹。

image

 

在ps1中输入如下代码。我的例子是安装了一个Media Foundation功能。注意,安装该功能是否需要重启应当提前确定清楚。需要重启的功能安装命令后面加-Restart。如果不加的话部署时会卡在那里。


Import-Module Servermanager 
$mf = Get-WindowsFeature "Server-Media-Foundation" 
if (!$mf.Installed) { 
  Add-WindowsFeature -name "Server-Media-Foundation" -Restart 
}
 

 

注意,保存这个文件的时候我们一定要选择VS文件菜单中的“Save with advanced options”选项,然后按照UTF-8 without signature这个编码来保存。因为UTF-8的三字节特征字符是PowerShell所不支持的。切记这一步操作!

image

 

接下来再给该项目新加一个bat文件。这个文件当中我们调用PowerShell来执行上述部署命令:


if "%EMULATED%"=="true" goto :EOF
powershell -command "Set-ExecutionPolicy Unrestricted" 2>> error.out
powershell .\installfeatures.ps1 2>> error.out
 

 

第一句首先我们将PowerShell的执行策略改为Unrestricted,因为它默认是Restricted,不能进行Windows功能安装等任务。我们将这个文件保存为startuptask.bat,同样要利用高级保存选项将它保存为UTF-8 without signature编码,而且别忘了选中总是拷贝到Output文件夹。

 

最后一步我们要到Cloud Service主项目的ServiceDefinition.csdef文件中添加执行bat文件的任务。先找到这个文件:

ServiceDefinition

在刚才添加过ps1和bat文件所对应的WebRole或者WorkerRole的配置段中,添加这样的代码:


<Startup>
  <Task commandLine="startuptask.bat" executionContext="elevated" taskType="simple">
    <Environment>
      <Variable name="EMULATED">
        <RoleInstanceValue xpath="/RoleEnvironment/Deployment/@emulated"/>
      </Variable>
    </Environment>
  </Task>
</Startup>
 

 

其中我们建立的EMULATED环境变量就是之前bat文件中引用的。利用这个环境变量就可以防止本地调试Azure的时候执行上述ps1脚本。如此一来,只要部署Cloud Service项目,其VM上就会自动安装上Media Foundation功能。其他经由PowerShell可以实现的VM管理任务也可以照此方法容易地实现。

 

Windows Azure Storage Service使用心得

接下来是一些关于Storage Service的使用心得集合,如果错误欢迎指出。

 

调整并发连接数限制

刚建立好的Cloud Service WorkerRole的OnStart()方法中我们能看到这样一行代码

ServicePointManager.DefaultConnectionLimit = 12;
建议大家把这个限制改成100甚至更高。因为Azure的存储服务访问都要经过网络访问,瓶颈出现在连接数限制上是很常见的问题。要注意的是,Azure Storage Service也不能过度并发访问。如果连接数过高也会返回服务端错误。明智的做法是适当进行分组,然后并发地对BlockBlob的各个Block进行访问;或者并发地对多个Blob进行访问。我在实践中选择的分组大小是10个Block一组,我并没有测试它是否是最好的分组大小,有兴趣的人可以亲自去试一下。

 

使用BufferedStream访问Blob

Windows Azure SDK的Blob对象有一个属性,叫StreamMinimumReadSizeInBytes。它的默认值是4MB。别被它骗了,实际上Blob的OpenRead和OpenWrite打开的流,如果进行尺寸很小的频繁读写,性能还是非常差的。实际上OpenRead和OpenWrite打开的流比较适合上载和下载整段的数据。当你需要按照访问本地文件那样的逻辑访问Blob的时候,一定要给他套上一个System.IO.BufferedStream。而且记得手工把Buffer的大小改成4MB(默认是4K,对Azure的Blob来说毫无作用)。

 

关于表格存储中的DateTime类型

我在一个表格存储所对应的Entity类中使用了DateTime类型的属性,结果发现插入操作总是失败。经过我的寻找,发现DateTime类型的属性不初始化就直接进行插入或者更新操作的话是不行的。因为Azure Table Storage中的时间日期格式与.net的日期格式范围不同。Azure的日期只能表示从公元1601年开始,而.net的日期不初始化的话是从0001年开始的。这样一旦提交就是一个非法日期了。那么,如果我真的有必要表示一个日期是未初始化的应该怎么做呢?只要将该字段声明成DateTime?类型即可(Nullable<DateTime>)。

 

采用Task风格的async/await来访问Azure Storage Service

Azure storage service的访问全部都通过网络来进行,所以充分利用异步操作可以很好地节约本地的线程资源,提高响应性。但是当前的Windows Azure SDK 2.0里面并未提供C#5/VB11的async/await语法能够访问的Task风格异步API,而是传统的Begin/End风格。据说在下一个版本的SDK当中会全面提供Task风格的异步API,在此之前我们只好绕路一下。做法就是利用Task.Factory对象的FromAsync方法。比如说我们希望异步地调用CloudBlockBlob的Delete方法,可以这样写:

await Task.Factory.FromAsync(
    blob.BeginDelete(null, null),
    blob.EndDelete);
 

这个方法能够处理各种有返回值或者没有返回值的Begin/End系列异步方法。有返回值时需要手动传入返回值的类型作为泛型参数:

var stream = await Task.Factory.FromAsync<Stream>(
    blob.BeginOpenWrite(null, null),
    blob.EndOpenWrite);

 

以上就是我对Windows Azure开发总结出的一些粗浅的小技巧,希望能够对大家有所帮助。如果建议或者讨论,欢迎到新浪微博上关注 @装配脑袋

 posted on 2013-07-21 01:16  装配脑袋  阅读(7577)  评论(28编辑  收藏  举报