我們在營運線上網站時,除了網站本身的維護之外,也常常會遇到需求是必須定期執行一些批次程式,有可能是用來更新靜態檔案讓資料保持在最新版本,又或是檢查系統資料是否有異常的情況發生,若有異常情況就馬上發送警告通知讓維護人員知道,而今天的內容就是要向大家介紹如何在Azure上設定排程工作並執行。
※使用Windows的Task Scheduler進行排程工作
Azure的Cloud Service其實也是由一個個的VM所構成,而VM中當然會具有Windows的Task Scheduler,因此我們可以透過一些簡單的Command,讓Cloud Service在發行時新增一個排程工作,這麼一來就可以最快速的實現我們想要的排程功能,這也是所需成本最低的一種方法!
新增一個批次工作內容
public class BatchJob
{
public void Execute()
{
MailAddress from = new MailAddress("xxx@gmail.com", "kirkchen", Encoding.UTF8);
MailMessage mail = new MailMessage(from, new MailAddress("xxx@gmail.com"));
string subject = "Test Subject";
mail.Subject = subject;
mail.SubjectEncoding = Encoding.UTF8;
string body = "Test Body";
mail.Body = body;
mail.BodyEncoding = Encoding.UTF8;
mail.IsBodyHtml = false;
mail.Priority = MailPriority.High;
SmtpClient client = new SmtpClient();
client.Host = "smtp.gmail.com";
client.Port = 587;
client.Credentials = new NetworkCredential("xxx", "xxx");
client.EnableSsl = true;
client.Send(mail);
}
}
建立成批次
public class Program
{
static void Main(string[] args)
{
BatchJob job = new BatchJob();
job.Execute();
Console.WriteLine("Finish!");
Console.Read();
}
}
將批次放到WebRole的專案中,並設置為複製到輸出目錄
新增cmd檔,建立排程工作
::Start Task Scheduler Service
net start "task scheduler"
::Create user for schedule job
net user jobuser1 P@ssw0rd /add
::Set user as adimn
net localgroup Administrators jobuser1 /add
::Create Schedule job
schtasks /create /SC MINUTE /MO 1 /TN BatchJob /TR %~dp0BatchJob/ApiSample.Consoles.Console.exe /F /RU jobuser1 /RP P@ssw0rd
註: %~dp0可以取得cmd所在目錄
5. 修改發行專案的ServiceDefinition.csdef(或*.csdef),在發行時啟動cmd檔
<WebRole name="ApiSample.UI.WebSite" vmsize="ExtraSmall">
<Sites>
<Site name="Web">
<Bindings>
<Binding name="Endpoint1" endpointName="Endpoint1" />
</Bindings>
</Site>
</Sites>
<Startup>
<Task commandLine="Batchs\startuptask.cmd" executionContext="elevated" taskType="simple" />
</Startup>
<Endpoints>
<InputEndpoint name="Endpoint1" protocol="http" port="80" />
</Endpoints>
<Imports>
<Import moduleName="Diagnostics" />
<Import moduleName="RemoteAccess" />
<Import moduleName="RemoteForwarder" />
</Imports>
</WebRole>
延伸閱讀:
※使用Worker Role配合Schedule Library撰寫排程工作
除了使用VM中的Task Scheduler之外,我們也可以建立一個Worker Role,並且利用Schedule的Library執行Job的方式來執行排程工作
新增Cloud Service專案
新增一個背景工作角色
使用Nuget加入Quartz.Net Library
新增一個批次的邏輯
public class BatchJob : IJob
{
public void Execute(IJobExecutionContext context)
{
Trace.Write("Execute Job");
MailAddress from = new MailAddress("xxx@gmail.com", "kirkchen", Encoding.UTF8);
MailMessage mail = new MailMessage(from, new MailAddress("xxx@gmail.com"));
string subject = "Test Subject";
mail.Subject = subject;
mail.SubjectEncoding = Encoding.UTF8;
string body = "Test Body";
mail.Body = body;
mail.BodyEncoding = Encoding.UTF8;
mail.IsBodyHtml = false;
mail.Priority = MailPriority.High;
SmtpClient client = new SmtpClient();
client.Host = "smtp.gmail.com";
client.Port = 587;
client.Credentials = new NetworkCredential("xxx", "xxx");
client.EnableSsl = true;
client.Send(mail);
}
}
在Worker Role使用Quartz.Net設定批次的排程
public class WorkerRole : RoleEntryPoint
{
private IScheduler scheduler;
private ManualResetEvent CompletedEvent = new ManualResetEvent(false);
public override void Run()
{
DateTimeOffset runTime = DateBuilder.EvenMinuteDate(DateTime.UtcNow);
DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(null, 10);
var job = JobBuilder.Create<BatchJob>()
.WithIdentity("BatchJob", null)
.Build();
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("default", null)
.StartAt(runTime)
// execute preiod with cron format
.WithCronSchedule("1 * * * * ?")
.Build();
scheduler.ScheduleJob(job, trigger);
this.CompletedEvent.WaitOne();
}
public override bool OnStart()
{
// construct a scheduler factory
ISchedulerFactory factory = new StdSchedulerFactory();
// get a scheduler
this.scheduler = factory.GetScheduler();
this.scheduler.Start();
return base.OnStart();
}
public override void OnStop()
{
this.scheduler.Clear();
this.CompletedEvent.Set();
base.OnStop();
}
}
發行背景工作角色到雲端,完成排程批次的設定!
延伸閱讀:
* Cron format
* Quartz.Net
※使用MobileService和Service Bus Queue來執行批次工作
在Service Bus新增一個服務,並取得金鑰
建立一個行動服務,並且選擇排程器
建立一個排程工作
輸入指令碼,透過排程器,時間到時新增一個Queue Message
function BatchJob() {
var azure = require('azure');
console.info('Start TestService Bus');
var queueService = azure.createServiceBusService('[servicebus name]','[servicebus key');
queueService.createQueueIfNotExists('job', function(error){
if(!error){
console.info(error);
}
});
var message = {
body: 'BatchJob'
};
queueService.sendQueueMessage('job', message, function(error){
if(!error){
console.info(error);
}
});
}
執行一次後,可以看到確實新增了資料到佇列中
我們只要建立一個Worker Role,在有訊息佇列時執行工作即可
public class WorkerRole : RoleEntryPoint
{
// 佇列的名稱
const string QueueName = "job";
// QueueClient 可進行安全對話。已建議您進行快取,
// 而非在每次要求時重新建立
QueueClient Client;
ManualResetEvent CompletedEvent = new ManualResetEvent(false);
public override void Run()
{
Trace.WriteLine("開始處理訊息");
// 起始訊息幫浦,且已叫用每則已收到的訊息回呼,用戶端結果的呼叫會停止幫浦。
Client.OnMessage((receivedMessage) =>
{
try
{
var message = receivedMessage.GetBody<string>();
if(message!="BatchJob"){
return;
}
MailAddress from = new MailAddress("xxx@gmail.com", "kirkchen", Encoding.UTF8);
MailMessage mail = new MailMessage(from, new MailAddress("xxx@gmail.com"));
string subject = "Test Subject";
mail.Subject = subject;
mail.SubjectEncoding = Encoding.UTF8;
string body = "Test Body";
mail.Body = body;
mail.BodyEncoding = Encoding.UTF8;
mail.IsBodyHtml = false;
mail.Priority = MailPriority.High;
SmtpClient client = new SmtpClient();
client.Host = "smtp.gmail.com";
client.Port = 587;
client.Credentials = new NetworkCredential("xxx", "xxx");
client.EnableSsl = true;
client.Send(mail);
receivedMessage.Complete();
}
catch
{
receivedMessage.Abandon();
}
});
CompletedEvent.WaitOne();
}
public override bool OnStart()
{
// 設定並行連線數目上限
ServicePointManager.DefaultConnectionLimit = 12;
// 若不存在,請建立佇列
string connectionString = CloudConfigurationManager.GetSetting("Microsoft.ServiceBus.ConnectionString");
var namespaceManager = NamespaceManager.CreateFromConnectionString(connectionString);
if (!namespaceManager.QueueExists(QueueName))
{
namespaceManager.CreateQueue(QueueName);
}
// 起始到 Service Bus 佇列的連線
Client = QueueClient.CreateFromConnectionString(connectionString, QueueName);
return base.OnStart();
}
public override void OnStop()
{
// 關閉到 Service Bus 佇列的連線
Client.Close();
CompletedEvent.Set();
base.OnStop();
}
}
開啟MobileService排程以及發行批次執行的Worker Role即可
延伸閱讀:
* Azure Task Scheduling Options
※本日小結
透過一些簡單的排程工作,可以減低平常我們在維運網站時的手工作業,更甚至可以藉由一些定時的資料檢查來確認系統有沒有異常的狀況,今天主要也是提供一些在Azure上設立排程工作的方法給大家,大家可以依據自己的使用情境選擇適合的來使用,關於今天的內容,歡迎大家一起討論 ^_^