انجام کارهای زمانبندی شده در برنامههای ASP.NET توسط DNT Scheduler
نویسنده: وحید نصیری
تاریخ: ۱۳۹۲/۱۲/۲۶ ۱۱:۴۰
آدرس: www.dntips.ir
| مطالب | ۳۶۹۴ |
| نویسندگان | ۲۷۶ |
| گروههای مطالب | ۱۰۲۴ |
| نقشههای راه | ۱۱۹ |
| دورهها | ۱۴ |
| اشتراکها | ۱۷۹۱۴ |
public class SendEmailsTask : ScheduledTaskTemplate
using System;
namespace DNTScheduler.TestWebApplication.WebTasks
{
public class SendEmailsTask : ScheduledTaskTemplate
{
/// <summary>
/// اگر چند جاب در یک زمان مشخص داشتید، این خاصیت ترتیب اجرای آنها را مشخص خواهد کرد
/// </summary>
public override int Order
{
get { return 1; }
}
public override bool RunAt(DateTime utcNow)
{
if (this.IsShuttingDown || this.Pause)
return false;
var now = utcNow.AddHours(3.5);
return now.Minute % 2 == 0 && now.Second == 1;
}
public override void Run()
{
if (this.IsShuttingDown || this.Pause)
return;
System.Diagnostics.Trace.WriteLine("Running Send Emails");
}
public override string Name
{
get { return "ارسال ایمیل"; }
}
}
} public override bool RunAt(DateTime utcNow)
{
if (this.IsShuttingDown || this.Pause)
return false;
var now = utcNow.AddHours(3.5);
return now.Hour == 23 && now.Minute == 33 && now.Second == 1;
} using System;
using System.Net;
namespace DNTScheduler.TestWebApplication.WebTasks
{
public static class ScheduledTasksRegistry
{
public static void Init()
{
ScheduledTasksCoordinator.Current.AddScheduledTasks(
new SendEmailsTask(),
new DoBackupTask());
ScheduledTasksCoordinator.Current.OnUnexpectedException = (exception, scheduledTask) =>
{
//todo: log the exception.
System.Diagnostics.Trace.WriteLine(scheduledTask.Name + ":" + exception.Message);
};
ScheduledTasksCoordinator.Current.Start();
}
public static void End()
{
ScheduledTasksCoordinator.Current.Dispose();
}
public static void WakeUp(string pageUrl)
{
try
{
using (var client = new WebClient())
{
client.Credentials = CredentialCache.DefaultNetworkCredentials;
client.Headers.Add("User-Agent", "ScheduledTasks 1.0");
client.DownloadData(pageUrl);
}
}
catch (Exception ex)
{
//todo: log ex
System.Diagnostics.Trace.WriteLine(ex.Message);
}
}
}
} using System;
using System.Configuration;
using DNTScheduler.TestWebApplication.WebTasks;
namespace DNTScheduler.TestWebApplication
{
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
ScheduledTasksRegistry.Init();
}
protected void Application_End()
{
ScheduledTasksRegistry.End();
//نکته مهم این روش نیاز به سرویس پینگ سایت برای زنده نگه داشتن آن است
ScheduledTasksRegistry.WakeUp(ConfigurationManager.AppSettings["SiteRootUrl"]);
}
}
} <?xml version="1.0"?>
<configuration>
<appSettings>
<add key="SiteRootUrl" value="http://localhost:10189/Default.aspx" />
</appSettings>
</configuration> var jobsList = ScheduledTasksCoordinator.Current.ScheduledTasks.Select(x => new
{
TaskName = x.Name,
LastRunTime = x.LastRun,
LastRunWasSuccessful = x.IsLastRunSuccessful,
IsPaused = x.Pause,
}).ToList();
همچنین Application Initialization Module نیز برای اجرا خودکار برنامه پس از ریاستارت سرور طراحی شده.
var urlToWakeup = Request.Url.Scheme + Uri.SchemeDelimiter + Request.Url.Host +
(Request.Url.IsDefaultPort ? "" : ":" + Request.Url.Port); namespace TestApp
{
public static class App
{
public static string SiteRootUrl;
}
public class TestApplication : HttpApplication
{
protected void Application_BeginRequest(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(App.SiteRootUrl))
{
App.SiteRootUrl = Request.Url.GetLeftPart(UriPartial.Authority) + Request.ApplicationPath;
}
}
protected void Application_End()
{
// use App.SiteRootUrl
}
}
} ScheduledTasksCoordinator.Current.AddScheduledTasks(
new SendEmailsTask(),
new DoBackupTask()); public void Start()
{
_timer.OnTimerCallback = () =>
{
var now = DateTime.UtcNow;
var taskToRun = _tasks.Where(x => !x.IsRunning && x.RunAt(now)).OrderBy(x => x.Order).ToList();
if (_isShuttingDown || !taskToRun.Any())
return;
HostingEnvironment.QueueBackgroundWorkItem(x => taskAction(taskToRun));
};
} سلام. ممنون.
دوستان برای من وقتی به صورت ساعت میدم درست کار نمیکنه.مثلا من میخوام در ساعت دو و بیست و چهار دقیقه بامداد یه کاری رو انجام بده اینجوری نوشتم:در صورتی اگه بگم دو دقیقه دو دقیقه درست انجام میشه.به نظرتون مشکل از کجاست؟ممنون
var now = utcNow.AddHours(3.5);
return now.Hour == 2 && now.Minute == 24 && now.Second == 1; ممنون. مفید بود. اگه بخواهیم یه کاری مثلا هر سه روز یکبار انجام بشه باید چه جوری زمان رو تعیین کنیم؟ ممنون
public override bool RunAt(DateTime utcNow)
{
if (this.IsShuttingDown || this.Pause)
return false;
var now = utcNow.AddHours(3.5);
return (now.Day % 3 == 0) && (now.Hour == 0 && now.Minute == 1 && now.Second == 1);
} return now.Second % 1 == 0 && now.Second == 1;
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="DNTScheduler.TestWebApplication.Default" %>
Error 1 Could not load type 'DNTScheduler.TestWebApplication.Default'. C:\Users\KING\Desktop\DNTScheduler\DNTScheduler.TestWebApplication\Default.aspx 1
return now.Second % 15 == 0 && now.Second == 1;
<add key="SiteRootUrl" value="http://localhost:10189/Default.aspx" />
DependencyResolver.Current.GetService<IService>()
public override void Run()
{
if (this.IsShuttingDown || this.Pause)
return;
var _myService = StructureMapObjectFactory.Container.GetInstance<IMyService>();
var sendingEmailsData = _myService.GetSendingEmailsData();
if (sendingEmailsData.Count > 0)
{
var reader = new StreamReader(SiteCoreManager.SCHEDULE_BODY_PATTERN_FILE_PATH);
string bodyPattern = reader.ReadToEnd();
foreach (var sendingEmailItem in sendingEmailsData)
{
MailSender.Send(bodyPattern, sendingEmailItem );
// ایجاد تاخیر
Task.Delay(1000);
}
}
} - یک جاب تعریف کن که بازهی اجراش یک ساعت یکبار باشه. هر بار هم که اجرا شد، یک حلقهی 40 تایی رو اجرا کن.
- یک فیلد وضعیت در دیتابیس براش تعریف کن. هر بار که جاب اجرا شد، اول چک کن که این فیلد، فعال بودن یا نبودنش تنظیم شده یا نه.
public void RemoveTask(string taskName)
{
lock (_syncLock)
{
var task = _tasks.FirstOrDefault( x => x.Name == taskName);
if (task != null)
{
_tasks.Remove(task);
}
}
} protected void Application_EndRequest(object sender, EventArgs e)
{
HttpContextLifecycle.DisposeAndClearAll();
ScheduledTasksRegistry.End();
////نکته مهم این روش نیاز به سرویس پینگ سایت برای زنده نگه داشتن آن است
ScheduledTasksRegistry.WakeUp(ConfigurationManager.AppSettings["SiteRootUrl"]);
} ScheduledTasksRegistry.End();
////نکته مهم این روش نیاز به سرویس پینگ سایت برای زنده نگه داشتن آن است
ScheduledTasksRegistry.WakeUp(ConfigurationManager.AppSettings["SiteRootUrl"]); Install-Package DNTScheduler
public override Task RunAsync()
{
MainAsync().Wait();
return base.RunAsync();
}
private static async Task MainAsync()
{
var smsService = ObjectFactory.Container.GetInstance<ISmsService>();
await smsService.CheckForDelivery();
} public override Task RunAsync()
{
var smsService = ObjectFactory.Container.GetInstance<ISmsService>();
smsService.CheckForDelivery();
return base.RunAsync();
} public override async Task RunAsync()
{
var smsService = ObjectFactory.Container.GetInstance<ISmsService>();
await smsService.CheckForDelivery();
return base.RunAsync();
} Error1Since 'RegisterCompany.Web.DNTScheduler.JobsTask.CheckForDeliveryTask.RunAsync()' is an async method that returns 'Task', a return keyword must not be followed by an object expression. Did you intend to return 'Task<T>'?
public override async Task RunAsync()
{
var smsService = ObjectFactory.Container.GetInstance<ISmsService>();
await smsService.CheckForDelivery();
await base.RunAsync();
} using System;
using System.Text;
using Elmah;
namespace Common.WebToolkit
{
public static class ElmahLogEx
{
public static void LogException(this string ex)
{
if (string.IsNullOrWhiteSpace(ex))
return;
LogException(new Exception(ex));
}
public static void LogException(this Exception ex)
{
if (ex == null) return;
try
{
ErrorSignal.FromCurrentContext().Raise(ex);
}
catch
{
ErrorLog.GetDefault(null).Log(new Error(ex));
}
}
}
} ScheduledTasksCoordinator.Current.OnUnexpectedException =
(exception, scheduledTask) => (scheduledTask.Name + ":" + exception).LogException(); <add key="SiteRootUrl" value="http://xxx.com" />
PM> Install-Package DNTScheduler
PM> Update-Package
ScheduledTasksCoordinator.Current.AddScheduledTasks( new Listener(), new Sender() );
public static class IoCWrapper
{
public static void RunAndDispose(Action action)
{
try
{
action();
}
finally
{
// اگر در درخواست وب بودیم، به صورت خودکار در پایان کار همه چیز تمام میشود
if (!HttpContextLifecycle.HasContext())
{
new HybridLifecycle().FindCache(null).DisposeAndClear();
}
}
}
public static T GetInstance<T>()
{
return SmObjectFactory.Container.GetInstance<T>();
}
} public override void Run()
{
IoCWrapper.RunAndDispose(() =>
{
var draftsService = IoCWrapper.GetInstance<IBlogPostDraftsService>();
draftsService.RunConvertDraftsToPostsJob();
});
} IoCWrapper.RunAndDispose(() =>
{
var draftsService = IoCWrapper.GetInstance<IBlogPostDraftsService>();
draftsService.RunConvertDraftsToPostsJob();
}); IoCWrapper.RunAndDispose(async () =>
{
var draftsService = IoCWrapper.GetInstance<IBlogPostDraftsService>();
await draftsService.RunConvertDraftsToPostsJobAsync();
}); System.ObjectDisposedException HResult=0x80131622 Message=The ObjectContext instance has been disposed and can no longer be used for operations that require a connection. Source=EntityFramework
public static Task RunAndDispose(Func<Task> action)
public static Task RunAndDispose(Func<Task> action)
{
try
{
action();
}
finally
{
System.Diagnostics.Trace.WriteLine("Finaly");
if (!HttpContextLifecycle.HasContext())
{
new HybridLifecycle().FindCache(null).DisposeAndClear();
}
}
return Task.FromResult(0);
} public override async Task RunAsync()
{
await SemaphoreSlim.WaitAsync();
try
{
if (this.IsShuttingDown || this.Pause)
return;
await IoCWrapper.RunAndDispose(async () =>
{
var draftsService = IoCWrapper.GetInstance<IBlogPostDraftsService>();
await draftsService.RunConvertDraftsToPostsJobAsync();
});
}
finally
{
SemaphoreSlim.Release();
}
} ActivatedEventTimeDurationThread
Activated Historical Code ContextException thrown: 'System.ObjectDisposedException' in mscorlib.dll ("The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.") Exception thrown: 'System.ObjectDisposedException' in mscorlib.dll ("The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.") Hyperlink: Activate Historical Debugging346.49s[14880] Worker Thread // فقط یک ترد امکان دسترسی به کد را داشته باشد
private static readonly SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1,1);
public override async Task RunAsync()
{
// شروع تمام تردها معلق خواهند شد تا این سمافور به پایان برسد
// پس از پایان کار ترد جاری، فقط یک ترد در حال انتظار، مجوز دسترسی به قطعه کد بعدی را خواهد یافت و به همین ترتیب برای سایر تردها
await _semaphoreSlim.WaitAsync();
try
{
await Task.Delay(20000); // Your code here
}
finally
{
_semaphoreSlim.Release();
}
} return now.Hour % 3 == 0 && now.Second == 1;
return now.Hour % 3 == 0 && now.Minute == 0 && now.Second == 1;
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Metric","time":"2019-01-08T11:23:01.0000000Z","tags":{"ai.cloud.roleInstance":"BAHARI-PC","ai.internal.sdkVersion":"m-agg2:2.8.1-22898"},"data":{"baseType":"MetricData","baseData":{"ver":2,"metrics":[{"name":"Dependency duration","kind":"Aggregation","value":4143.777,"count":1,"min":4143.777,"max":4143.777,"stdDev":0}],"properties":{"_MS.MetricId":"dependencies/duration","_MS.IsAutocollected":"True","_MS.AggregationIntervalMs":"60000","Dependency.Type":"Http","DeveloperMode":"true","Dependency.Success":"True"}}}}
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Metric","time":"2019-01-08T11:23:01.0000000Z","tags":{"ai.cloud.roleInstance":"BAHARI-PC","ai.internal.sdkVersion":"m-agg2:2.8.1-22898"},"data":{"baseType":"MetricData","baseData":{"ver":2,"metrics":[{"name":"Server response time","kind":"Aggregation","value":4136.7653,"count":1,"min":4136.7653,"max":4136.7653,"stdDev":0}],"properties":{"_MS.MetricId":"requests/duration","_MS.IsAutocollected":"True","_MS.AggregationIntervalMs":"60000","DeveloperMode":"true","Request.Success":"True"}}}}
Application Insights Telemetry (unconfigured): {"name":"Microsoft.ApplicationInsights.Dev.Metric","time":"2019-01-08T11:23:01.0000000Z","tags":{"ai.cloud.roleInstance":"BAHARI-PC","ai.internal.sdkVersion":"m-agg2:2.8.1-22898"},"data":{"baseType":"MetricData","baseData":{"ver":2,"metrics":[{"name":"Dependency duration","kind":"Aggregation","value":3972.6929,"count":3,"min":12.8297,"max":2687.0447,"stdDev":1092.34881420812}],"properties":{"_MS.MetricId":"dependencies/duration","_MS.IsAutocollected":"True","_MS.AggregationIntervalMs":"60000","Dependency.Type":"SQL","DeveloperMode":"true","Dependency.Success":"True"}}}} #region Using
using Autofac;
using CHK.ServiceLayer.Interfaces;
using DNTScheduler;
using System;
#endregion
namespace CHK.Web.Scheduler
{
public class InventoryTask : ScheduledTaskTemplate
{
#region Properties
public override int Order => 1;
public override string Name => "Inventory-DiscountCoupon-GiftCard";
public IContainer Container { get; set; }
#endregion
#region Methods
public override bool RunAt(DateTime utcNow)
{
if (IsShuttingDown || Pause) return false;
var currentDateTime = utcNow.AddHours(3.5);
return (currentDateTime.Minute % 15 == 0 && currentDateTime.Second > 5 && currentDateTime.Second < 15);
}
public override void Run()
{
if (IsShuttingDown || Pause) return;
Pause = true;
using (var scope = Container.BeginLifetimeScope())
{
var schedulerService = scope.Resolve<ISchedulerService>();
schedulerService.UpdateInventory();
}
Pause = false;
}
#endregion
}
} ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true; ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
await _client.GetStringAsync(SiteRootUrl);
var logger = loggerFactory.CreateLogger(typeof(Startup));
app.UseDNTScheduler(onUnexpectedException: (ex, name) =>
{
logger.LogError(0, ex, $"Failed running {name}");
}); | وضعیت پشتیبانی از TLS 1.2 | نگارش دات نت |
| پشتیبانی نمیشود. راه حلی هم ندارد. | NET 3.5. یا قبل از آن |
پشتیبانی نمیشود. اما اگر نگارش بالاتری نصب است، قطعه کد زیر را استفاده کنید:ServicePointManager.SecurityProtocol = (SecurityProtocolType)3072; | NET 4.0. |
پشتیبانی میشود، اما حالت پیشفرض نیست و باید دستی انتخاب شود:ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; | NET 4.5. |
| حالت پیشفرض است و نیاز به تنظیمات خاصی ندارد. | NET 4.6. و یا بالاتر |
Category: DNTScheduler.Core.ScheduledTasksCoordinator EventId: 0 Failed running DNTScheduler.Core.PingTask Exception: System.AggregateException: One or more errors occurred. (The SSL connection could not be established, see inner exception.) ---> System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception. ---> System.Security.Authentication.AuthenticationException: The remote certificate is invalid according to the validation procedure.
services.AddDNTScheduler(options =>
{
// DNTScheduler needs a ping service to keep it alive. Set it to false if you don't need it.
options.AddPingTask = true;
options.AddScheduledTask<DoFillDataTask>(
runAt: utcNow =>
{
var schedulerTask = _config.GetSection("SchedulerTask");
var now = utcNow.AddHours(4.5);
return now.Hour == 10 && now.Minute == 0 && now.Second == 0;
},
order: 1);
}); " {context.Request.Scheme}://{context.Request.Host.Value}" https://domain.com/index.html
public bool AddTask()
{
_tasksStorage.Value.AddScheduledTask<DoEnableProductTasks>(
runAt: utcNow =>
{
var now = utcNow.AddHours(4.5);
return now.Hour == 18 && now.Minute == 57 && now.Second == 1;
},
order: 1);
return true;
} services.AddTransient<DoEnableProductTasks>();