愛流浪的小風

技術隨手寫

使用Asp.Net MVC打造Web Api (27) - 在Azure上執行排程工作

| Comments

我們在營運線上網站時,除了網站本身的維護之外,也常常會遇到需求是必須定期執行一些批次程式,有可能是用來更新靜態檔案讓資料保持在最新版本,又或是檢查系統資料是否有異常的情況發生,若有異常情況就馬上發送警告通知讓維護人員知道,而今天的內容就是要向大家介紹如何在Azure上設定排程工作並執行。

使用Windows的Task Scheduler進行排程工作

Azure的Cloud Service其實也是由一個個的VM所構成,而VM中當然會具有Windows的Task Scheduler,因此我們可以透過一些簡單的Command,讓Cloud Service在發行時新增一個排程工作,這麼一來就可以最快速的實現我們想要的排程功能,這也是所需成本最低的一種方法!

  1. 新增一個批次工作內容

    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);
        }
    }
    
  2. 建立成批次

    public class Program
    {
        static void Main(string[] args)
        {
            BatchJob job = new BatchJob();
            job.Execute();
    
            Console.WriteLine("Finish!");
            Console.Read();
        }
    }
    
  3. 將批次放到WebRole的專案中,並設置為複製到輸出目錄

  4. 新增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>   
    
  6. 發行網站就完成了批次工作的建立

延伸閱讀:

使用Worker Role配合Schedule Library撰寫排程工作

除了使用VM中的Task Scheduler之外,我們也可以建立一個Worker Role,並且利用Schedule的Library執行Job的方式來執行排程工作

  1. 新增Cloud Service專案

  2. 新增一個背景工作角色

  3. 使用Nuget加入Quartz.Net Library

  4. 新增一個批次的邏輯

    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);
        }
    }
    
  5. 在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();
        }
    }
    
  6. 發行背景工作角色到雲端,完成排程批次的設定!

延伸閱讀:

使用MobileService和Service Bus Queue來執行批次工作

  1. 在Service Bus新增一個服務,並取得金鑰

  2. 建立一個行動服務,並且選擇排程器

  3. 建立一個排程工作

  4. 輸入指令碼,透過排程器,時間到時新增一個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);
            }
        });
    }
    
  5. 執行一次後,可以看到確實新增了資料到佇列中

  6. 我們只要建立一個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();
        }
    }
    
  7. 開啟MobileService排程以及發行批次執行的Worker Role即可

延伸閱讀:

本日小結

透過一些簡單的排程工作,可以減低平常我們在維運網站時的手工作業,更甚至可以藉由一些定時的資料檢查來確認系統有沒有異常的狀況,今天主要也是提供一些在Azure上設立排程工作的方法給大家,大家可以依據自己的使用情境選擇適合的來使用,關於今天的內容,歡迎大家一起討論 ^_^

Comments

comments powered by Disqus