我最近做了一個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文件。我們雙擊其中一個打開,默認是這樣的:
注意下面的Diagnostics段,勾選的話就打開了Trace。但是默認情況下,Trace使用的Storage Account是“DevelopmentStorage”其實就是本地的Azure模擬器。我們只要為其指定一個真正的Azure Storage Account就可以了。建議專門為Trace建立一個Storage Account,而不要使用其他數據的Account。如圖即可選擇一個Storage Account。
在代碼中,直接使用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按鈕來進行篩選。如果需要諸如性能計數器等額外信息,也可以在此選擇:
最后是Trace的查看方法。經過以上配置,System.Diagnostics.Trace記錄下來的Trace會保存到設定Storage Account的WADLogsTable表格中;Windows Event Logs記錄到WADWindowsEventLogsTable表格中。在Azure的管理門戶網站上是看不到表格存儲的,需要用代碼來讀取其中的內容。當然,我們也可以用一些現成的第三方工具,比如我用的是這個Azure Storage Explorer:
如此一來,從啟用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,在里面執行。你將會看到如下的功能列表:
第一列是模擬在GUI模式下看到的樹狀的功能列表,而第二列就是在PowerShell中安裝它所需的名稱。記住這個名稱。下面我們要在Cloud Service虛擬機部署的時候利用部署任務來安裝Windows Feature。在需要該Windows功能的WebRole或者WorkerRole項目中添加一個擴展名是ps1的文件,並且選擇總是拷貝到Output文件夾。
在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所不支持的。切記這一步操作!
接下來再給該項目新加一個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文件的任務。先找到這個文件:
在剛才添加過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開發總結出的一些粗淺的小技巧,希望能夠對大家有所幫助。如果建議或者討論,歡迎到新浪微博上關注 @裝配腦袋 。