ASP.NETã§ããã¯ã°ã©ãŠã³ãããã»ã¹ãéå§ããå¿
èŠããããŸããã çåãçããŸããïŒãããã©ã®ããã«è¡ãã®ãæåã§ããïŒ
SCOTT HANSELMANããã°ãå°ã
調ã¹ãŠã¿ããš ãã
ASP.NETã§ããã¯ã°ã©ãŠã³ãã¿ã¹ã¯ãå®è¡ããæ¹æ³ ããšãããšã³ããªãèŠã€ãããŸããã ãã®èšäºã¯ããã»ã©æ°ãããã®ã§ã¯ãããŸãã-2014幎ã§ãããããªãé¢é£æ§ãããã®ã§ããã·ã¢èªã«ç¿»èš³ããããšã«ããŸããã
æ°å¹ŽåãPhil Haackã¯ASP.NETã§ããã¯ã°ã©ãŠã³ãã¿ã¹ã¯ãè¡ãããšã®å±éºæ§ã«é¢ãã
åªããèšäºãæžããŸã
ã ã 圌ã¯ãããã¯ã°ã©ãŠã³ãããã»ã¹ã®éå§ã«é¢é£ãã3ã€ã®äž»ãªãªã¹ã¯ãç¹å®ããŸããã
- èŠæ±ã«é¢é£ä»ããããŠããªãã¹ã¬ããã®æªåŠçã®äŸå€ã«ãããããã»ã¹ãåé€ãããå ŽåããããŸãã
- Webãã¡ãŒã ã§ãµã€ããå®è¡ãããšãåãã¿ã¹ã¯ã®è€æ°ã®ã€ã³ã¹ã¿ã³ã¹ãåæã«å®è¡ããå¯èœæ§ã®ããã¢ããªã±ãŒã·ã§ã³ã®è€æ°ã®ã€ã³ã¹ã¿ã³ã¹ã誀ã£ãŠçµäºããå¯èœæ§ããããŸãã
- ãµã€ããå®è¡ãããŠããã¢ããªã±ãŒã·ã§ã³ãã¡ã€ã³ã¯ãããŸããŸãªçç±ã§ããµã€ãã§å®è¡ãããŠããããã¯ã°ã©ãŠã³ãã¿ã¹ã¯ãã¢ã³ããŒãããã³åé€ã§ããŸãã
ãã¡ãããããã¯ã°ã©ãŠã³ãã¿ã¹ã¯ã管çããç¬èªã®ãããŒãžã£ãŒãäœæã§ããŸãã ããããã»ãšãã©ã®å Žåãããªãã¯ééã£ãŠããŸãã éçºè
ã®ã¹ãã«ã«ææŠãã人ã¯ããŸããã ãã®ãããªãããŒãžã£ãŒãäœæããããšã¯ãããªã埮åŠãªããšã§ãã ãããŠããªããããå¿
èŠãªã®ã§ããïŒ
ããã¯ã°ã©ãŠã³ãã§ã¿ã¹ã¯ãå®è¡ããããã®å€ãã®çŽ æŽããããªãã·ã§ã³ããããŸãã ãããŠãæœè±¡çãªæè¡ã ãã§ãªããæ¢è£œã®ã©ã€ãã©ãªããããŸãã
ASP.NETã¢ããªã±ãŒã·ã§ã³ã«ã¯ãIISã®äžã®ç¬èªã®ãµãŒããŒã§å®è¡ã§ãããã®ãšãAzureã§ãã¹ãã§ãããã®ããããŸãã
ããã¯ã°ã©ãŠã³ãã¿ã¹ã¯ãéå§ããã«ã¯ãããã€ãã®ãªãã·ã§ã³ãéžæã§ããŸãã
- åºæ¬ïŒç¬èªã®ASP.NETãµã€ãã§ããã¯ã°ã©ãŠã³ãã¿ã¹ã¯ãäœæããããã«äœ¿çšã§ããHangfireãŸãã¯åæ§ã®ã©ã€ãã©ãªã
- ã¯ã©ãŠãïŒ Azure WebJobs Azure Web Jobsã Webãµã€ãã®å€éšã§ããã¯ã°ã©ãŠã³ãã¿ã¹ã¯ãå®è¡ããè² è·ã調æŽã§ããAzureã®ãã€ãã£ããµãŒãã¹ã
- é«åºŠïŒ ã¯ã©ãŠããµãŒãã¹ã®Azure WebããŒã« ã ãµã€ãã«é¢ä¿ãªãããã¯ã°ã©ãŠã³ãã¿ã¹ã¯ãå®è¡ã§ãããµãŒãã¹ãè² è·ã調æŽããå®è¡ãå¶åŸ¡ã§ããŸãã
Azureã¿ã¹ã¯ã䜿çšããæ¹æ³ãšãAzureã¯ã©ãŠããµãŒãã¹ã§WebããŒã«ãã©ã®ããã«æ©èœãããã«ã€ããŠãWebäžã«å€æ°ã®èšäºãšãããªããããŸãã ãã ããèªåã®ãµãŒããŒã§ããã¯ã°ã©ãŠã³ãã¿ã¹ã¯ãå®è¡ããæ¹æ³ã«é¢ããèšäºã¯ããŸããããŸããã
ãµã€ããèšãããã«ïŒãWebBackgrounderã¯ãASP.NET Webã¢ããªã±ãŒã·ã§ã³ã§ã®ã¿åäœããWebãã¡ãŒã äºæã®ããã¯ã°ã©ãŠã³ãã¿ã¹ã¯ãããŒãžã£ãŒã®æŠå¿µã®ãã¹ãã§ããã ã³ãŒãã¯é·å¹Žå€æŽãããŠããŸãããã
NuGetããã±ãŒãžã¯ 50äžå以äžããŠã³ããŒããã
ãŠããŸãã
ãã®ãããžã§ã¯ãã䜿çšãããšã1ã€ã®ã¿ã¹ã¯ã®ã¿ã§äœæ¥ããWebã¢ããªã±ãŒã·ã§ã³ã®å®è¡äžã«ããã¯ã°ã©ãŠã³ãã§ç¹°ãè¿ãã¿ã¹ã¯ã管çã§ããŸãã
ãã®ã©ã€ãã©ãªã¯æããã«ãã¹ãŠã®å Žåã«é©ããŠããããã§ã¯ãããŸããã ãã ããASP.NETã¢ããªã±ãŒã·ã§ã³ã®å®è¡äžã«1ã€ã®ã¿ã¹ã¯ã®ã¿ãå®è¡ããå¿
èŠãããå Žåã¯ãWebBackgrounderã§ååã§ãã
using System; using System.Threading; using System.Threading.Tasks; namespace WebBackgrounder.DemoWeb { public class SampleJob : Job { public SampleJob(TimeSpan interval, TimeSpan timeout) : base("Sample Job", interval, timeout) { } public override Task Execute() { return new Task(() => Thread.Sleep(3000)); } } }
.NET 4.5.2 QUEUEBACKGROUNDWORKITEMã§è¿œå
QueueBackgroundWorkItemã¯ãWebBackgrounderã®åºçŸã«å¿ããŠ.NET 4.5.2ã«è¿œå ããããšèšããŸãã ããã¯Task.Runã®åãªãå®è£
ã§ã¯ãããŸããã ããã¯ãã£ãšäœãã§ãïŒ
QBWIã¯ããªã¯ãšã¹ãã«é¢ä¿ãªããããã¯ã°ã©ãŠã³ãã§å®è¡ãããã¹ã±ãžã¥ãŒã«ãããã¿ã¹ã¯ã管çããŸãã éåžžã®ThreadPoolèŠçŽ ãšã®éãã¯ãASP.NETãçŸåšAPIã䜿çšããŠç»é²ãããŠããäœæ¥é
ç®ã®æ°ãèªåçã«è¿œè·¡ããã¢ããªã±ãŒã·ã§ã³ãã¡ã€ã³ããªãã«ãªã£ãŠããå ŽåãASP.NETã©ã³ã¿ã€ã ããããå»¶æããããšããå®è¡äžã®ããã¯ã°ã©ãŠã³ããã·ã£ããããŠã³ã§ããããšã§ãã¿ã¹ã¯ã
ASP.NETã©ã³ã¿ã€ã ã¯ãã¢ããªã±ãŒã·ã§ã³ãã¡ã€ã³ã®ã·ã£ããããŠã³ã90ç§ã ãé
ãããããšãã§ããã¿ã¹ã¯ãå®äºããããšãã§ããããšã«æ³šæããŠãã ããã ãããã£ãŠããã®æéå
ã«ã¿ã¹ã¯ãå®äºã§ããªãå Žåã¯ãä»ã®ããä¿¡é Œæ§ã®é«ãææ®µã䜿çšããå¿
èŠããããŸãã
APIã¯éåžžã«åçŽã§ã-ãFunc <CancellationTokenãTask>ãã䜿çšããŸãã 以äžã¯ãMVCããããã¯ã°ã©ãŠã³ãããã»ã¹ãéå§ããå°ããªäŸã§ãã
public ActionResult SendEmail([Bind(Include = "Name,Email")] User user) { if (ModelState.IsValid) { HostingEnvironment.QueueBackgroundWorkItem(ct => SendMailAsync(user.Email)); return RedirectToAction("Index", "Home"); } return View(user); }
FluentSchedulerã¯ãæµadvancedãªã€ã³ã¿ãŒãã§ã€ã¹ãåãããããé«åºŠã§æŽç·Žãããã¿ã¹ã¯ãããŒãžã£ãŒã§ãã ããã«ãããã¿ã¹ã¯ãèµ·åããããã»ã¹ãå®éã«å¶åŸ¡ã§ããŸãã
using FluentScheduler; public class MyRegistry : Registry { public MyRegistry() {
FluentSchedulerã¯IoCããµããŒãããŠãããITaskFactoryã€ã³ã¿ãŒãã§ã€ã¹ãå®è£
ããã ãã§ããæ°ã«å
¥ãã®Dependency Injectionã©ã€ãã©ãªã䜿çšããŠç°¡åã«æ¥ç¶ã§ããŸãã
FluentSchedulerã¯.NET Coreã§åäœããããšã«æ³šæããŠãã ããã
ããã¯ã°ã©ãŠã³ãã¿ã¹ã¯ã§çºçããäŸå€ãåŠçããã«ã¯ãJobManagerãªããžã§ã¯ãã®JobExceptionã€ãã³ãã䜿çšã§ããŸãã ãã®ã€ãã³ãã«ãããã¿ã¹ã¯ã§çºçããäŸå€ã«é¢ããæ
å ±ãååŸã§ããŸãã
JobManager.JobException += (info) => Log.Fatal("An error just happened with a scheduled job: " + info.Exception);
Quartz.NETã¯ã.NETã«å®è£
ãããŠããäžè¬çãªJavaãã¬ãŒã ã¯ãŒã¯ã®ããŒãžã§ã³ã§ãã ãã¬ãŒã ã¯ãŒã¯ã¯ç©æ¥µçã«éçºäžã§ãã ããã¯ã°ã©ãŠã³ãã¿ã¹ã¯ãäœæããããã«ãQuartzã¯IJobã€ã³ã¿ãŒãã§ã€ã¹ãšå¯äžã®Executeã¡ãœããã䜿çšããŸããããã¯å®è£
ããå¿
èŠããããŸãã
using Quartz; using Quartz.Impl; using System; namespace ScheduledTaskExample.ScheduledTasks { public class JobScheduler { public static void Start() { IScheduler scheduler = StdSchedulerFactory.GetDefaultScheduler(); scheduler.Start(); IJobDetail job = JobBuilder.Create<MyJob>().Build(); ITrigger trigger = TriggerBuilder.Create() .WithDailyTimeIntervalSchedule (s => s.WithIntervalInHours(24) .OnEveryDay() .StartingDailyAt(TimeOfDay.HourAndMinuteOfDay(0, 0)) ) .Build(); scheduler.ScheduleJob(job, trigger); } } }
Application_Startå
ã§ãããŒãžã£ãŒãèµ·åããã«ã¯ãJobScheduler.StartïŒïŒãåŒã³åºãå¿
èŠããããŸãã éå§ããã«ã¯ã
Scheduled Actionsããã³Quartz.NET ïŒ
Quartz.Netã䜿çšããASP.NETã®
Scheduled Tasksã®ç¿»èš³ïŒãèªãããšãã§ããŸãã
ãã®ãããžã§ã¯ãã«ã¯ããªããŸãšããªããã¥ã¡ã³ããããã䜿çšãéå§ããåã«èªã䟡å€ããããŸãïŒå°ãªããšãã
Quartzã䜿çšããéçºã®ãã¥ãŒããªã¢ã«ãã芧ãã ãã ïŒã
ãããŠãæåŸã®ã¬ãã¥ãŒã§ãããæ©èœæ§ã®é¢ã§ã¯ééããªãããã¹ãŠã®Hangfireã®äžã§æãå
é²çã§ãã ããã¯ãASP.NETã®éåžžã«é«åºŠãªããã¯ã°ã©ãŠã³ãã¿ã¹ã¯ãã¬ãŒã ã¯ãŒã¯ã§ãã ãªãã·ã§ã³ã§ãRedisãSQL ServerãSQL AzureãMSMQããŸãã¯RabbitMQã䜿çšããŠãã¿ã¹ã¯å®è¡ã®ä¿¡é Œæ§ãé«ããããšãã§ããŸãã
Hangfireã®ããã¥ã¡ã³ãã¯æ¬åœã«åªããŠããŸãã ãã¹ãŠã®ãªãŒãã³ãœãŒã¹ãããžã§ã¯ãã«ãã®ãããªããã¥ã¡ã³ããæããããã
Hangfireã®æãçŽ æŽãããæ©èœã®1ã€ã¯çµã¿èŸŒã¿ã®ããã·ã¥ããŒãã§ãã¹ã±ãžã¥ãŒã«ãå®äºããã¿ã¹ã¯ãæåããã¿ã¹ã¯ãšå€±æããã¿ã¹ã¯ã衚瀺ã§ããŸãã
Hangfireã䜿çšãããšããå®è¡ããŠå¿ããããªã©ã®ã¿ã¹ã¯ãç°¡åã«èå¥ã§ããŸããã¿ã¹ã¯ã«é¢ããæ
å ±ã¯ããŒã¿ããŒã¹ã«ä¿åãããŸãã
BackgroundJob.Enqueue(() => Console.WriteLine("Fire-and-forget"));
ã¿ã¹ã¯ãé
ãããããšãã§ããŸãïŒ
BackgroundJob.Schedule(() => Console.WriteLine("Delayed"), TimeSpan.FromDays(1));
ãŸãã¯ãCRONã¹ã¿ã€ã«ã§ã¿ã¹ã¯ãå®è¡ããŸã
RecurringJob.AddOrUpdate(() => Console.Write("Recurring"), Cron.Daily);
Hangfireã§ã®äœæ¥ã¯éåžžã«äŸ¿å©ã§ãã Hangfireã«ã¯ãå®éã®äŸã«åºã¥ããåªããããã¥ã¡ã³ããš
ãã¥ãŒããªã¢ã«ããããŸãã
Hangfireã¯ãASP.NETã¢ããªã±ãŒã·ã§ã³ã®ããã¯ã°ã©ãŠã³ãã¿ã¹ã¯ãæäœããããã®ãšã³ã·ã¹ãã å
šäœã§ãã
ã©ã€ãã©ãªã¯ããªãŒãã³ãœãŒã¹ãŸãã¯ãã²ããããã±ãŒãžãšããŠå
¥æã§ããŸãã
çµæïŒå人çã«ç§ããïŒ
èªåçšã®ã©ã€ãã©ãªãéžæããäžæ¹ã§ãé©åãªæ©èœãå¿
èŠã«ãªããäžæ¹ã§ãããšãã°å®è¡äžã®ã¿ã¹ã¯ã«é¢ããæ
å ±ãä¿åããããã«ããŒã¿ããŒã¹ã䜿çšããããããŸããã ãã®ãããWebBackgrounderãQueueBackgroundWorkItemãªã©ã®åçŽãªãœãªã¥ãŒã·ã§ã³ãæ€èšããŸããã§ããã
è€æ°ã®ããã»ã¹ãéå§ããå¿
èŠãããããšã¯æ¢ã«ç¥ã£ãŠããŸããããã»ã¹ã¯é·æéïŒQueueBackgroundWorkItemã§ã®å®äºã90ç§ã«å¶éãããïŒåäœããå¯èœæ§ããããŸãã FluentSchedulerã¯ããããã§ããããã£ã𿬲ããã£ãã§ãã Hangfireã¯åªãããœãªã¥ãŒã·ã§ã³ã§ãããã¿ã¹ã¯ãã¥ãŒãæ ŒçŽããã«ã¯ããŒã¿ããŒã¹ãããã«äœ¿çšããå¿
èŠãããããã§ãã ã¯ããå®å
šã«ç¡æã§ã¯ãããŸãããææçããããŸãã
æçµçã«ã¯ããããŸã§ã®ãšããQuartz.NETãéžæããŸãããããªããŸãšããªããã¥ã¡ã³ããããã«äœ¿ãå§ããã®ã«ååãªäŸãããã³ããŒãºã®å¢å ã«å¿ããŠè¿œå ã§ããæ¡åŒµå¯èœãªæ©èœã§ãã
ããã¯ã°ã©ãŠã³ãã¿ã¹ã¯ãèµ·åããããã®ä»ã®ã©ã€ãã©ãªãç¥ã£ãŠãããããã®ãããªåé¡ã解決ããçµéšãããå Žåã¯ãã³ã¡ã³ããå
±æããŠãã ããã