[Windows Azure] 在雲端執行你的命令列應用程式
最近很常被問的問題,不外乎是怎麼將現有應用程式移轉到 Windows Azure Cloud 上面,但其中有一種類型的問題,就是如果應用程式是非 Web 型的應用程式要如何移轉到雲端,比例最多的就是命令列應用程式 (Command-line application, Console Application),這些應用程式可能是舊有應用程式,也有可能是包裝了某些演算法 (例如 Matlab Application) 的應用程式,這些應用程式有些可能是沒有原始碼,有些也可能是移轉不方便或改寫會耗費太多時日,因此如果能夠將它直接移轉到 Windows Azure 雲端 VM 的話,對應用程式開發廠商來說是很棒的。
現在我可以告訴你,這個答案是肯定的,而且作法早在今年三月就已經有人提出。這次我在交大的課程間,也有不少學生來詢問這方面的問題,原訂要現場實作一支命令列程式給大家看的,但因時間來不及而作罷 … 而且關鍵的部份一直沒有方向,直到今天 Carol 寄來一個連結,令我茅塞頓開啊。
在 Windows Azure VM 中執行 Console Application 是一定可行的,而且作法也很簡單,只要用 Process 類別來啟動 Console Application 即可,但是關鍵的點是如何探知在雲端 VM 中應用程式被放在哪裡,這個變數在 Windows Azure SDK 中並沒有提及,同時它是在 VM 中的環境變數,名稱為 RoleRoot,它會傳回 Role 所在的磁碟機路徑。請注意,VM 中應用程式的路徑並不是在 C:\,而是在 %RoleRoot%\approot\ 中,所以不可以直接把路徑設死 (至少 RoleRoot 不可以設死)。另外,開發人員也需要在應用程式封裝中加入該可執行檔,如此檔案才可以被發布在雲端 VM。
多說無益,來個範例給大家看。
1. 首先,建立一個 console application,我使用的是很簡單的應用程式,這個應用程式可以傳回目前系統時間:
namespace TimeGenerator
{
class Program
{
static void Main(string[] args)
{
Console.Out.Write(DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"));
}
}
}
2. 建立一個雲端應用程式,並加入一個 Worker Role,我在這裡所建立的是 WCF 應用程式,並且利用 REST API 方式顯露方法,這個服務會啟動前面所建立的應 Console 應用程式,請注意 FileName 的設定,這是執行 Console 應用程式的最重要關鍵程式碼:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Activation;
using System.ServiceModel.Description;
using System.ServiceModel.Channels;
using System.ServiceModel.Web;
using System.Threading;
using Microsoft.WindowsAzure;
using Microsoft.WindowsAzure.ServiceRuntime;
namespace TimeGeneratorService
{
[ServiceContract]
public interface ITimeGenerator
{
[OperationContract, WebGet]
string GetCurrentTime();
}
class TimeGeneratorService : ITimeGenerator
{
public string GetCurrentTime()
{
Process p = new Process();
p.StartInfo = new ProcessStartInfo()
{
FileName = Path.Combine(Environment.GetEnvironmentVariable("RoleRoot"), "approot", "TimeGenerator.exe"),
RedirectStandardOutput = true,
CreateNoWindow = true,
UseShellExecute = false
};
p.Start();
p.WaitForExit();
string result = p.StandardOutput.ReadToEnd();
p.Close();
return result;
}
}
}
3. 將第一步所建立的應用程式加入到 Worker Role 的專案中,方法可以是直接在 Visual Studio 中使用 [加入 > 現有項目] 的方式,或是將執行檔複製到專案目錄,再由 Visual Studio 顯示所有檔案再加入專案的方式,但不論是哪種方式,都要確認該檔案的建置動作 (Build Action) 是內容 (Content),同時也要設定複製到輸出目錄 (Output Folder) 為永遠複製 (Copy always) 或是有更新時複製 (Copy if newer):
4. 在 Worker Role 進入點設定將服務掛載起來:
private WebServiceHost _serviceHost = null;
public override bool OnStart()
{
// Set the maximum number of concurrent connections
ServicePointManager.DefaultConnectionLimit = 12;
DiagnosticMonitor.Start("DiagnosticsConnectionString");
// For information on handling configuration changes
// see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
RoleEnvironment.Changing += RoleEnvironmentChanging;
this._serviceHost = new WebServiceHost(
typeof(TimeGeneratorService), new Uri("http://localhost:8080/"));
_serviceHost.Open();
return base.OnStart();
}
並在 app.config 中加入 WCF 的設定(這裡剛好也是 WCF 4.0 的 Simple Configuration 功能):
<system.serviceModel>
<behaviors>
<serviceBehaviors>
<behavior>
<serviceMetadata httpGetEnabled ="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
<protocolMapping>
<add binding="wsHttpBinding" scheme ="http"/>
</protocolMapping>
</system.serviceModel>
5. 按 F5 啟動 Visual Studio Debugger 以啟動 Development Fabric,並且在瀏覽器中輸入 http://localhost:8080/GetCurrentTime,
瀏覽器回傳的會是由 Console Application 所傳回的時間。
Reference:
http://social.msdn.microsoft.com/Forums/en-IE/windowsazure/thread/2755d7c8-984a-43d4-9652-f6c82d8c4b2b
http://social.msdn.microsoft.com/Forums/en/windowsazure/thread/5bc5f411-5940-44ce-ac49-a3eb999bd51b