Configurable Timer Triggers - Azure Web Jobs - c#

I'm building a job to trigger at some regular interval (say 1 minute). I've successfully used triggered web jobs with time-span hard coded in the functions.
public void foo([TimerTrigger("00:01:00")] TimerInfo timer)
Now if I ever want to change the trigger time from 1-min to 2-min I've to redeploy the code. Instead is there a way to make the TimeTrigger Configurable, from a config file.
Note that replacing the string with a dynamically read value isn't possible as the TimerTrigger Attribute is a const string expression or a Type.

After much digging, I've realized that this can be done with an SDK extension class TimerSchedule.
For it, you would need a base class that you'll be able to use for multiple triggers.
class CustomTimerTriggerBase: TimerSchedule
{
TimeSpan timer;
public CustomTimerTriggerBase(string triggerConfigKey)
{
timer=TimeSpan.Parse(ConfigurationManager.AppSettings[triggerConfigKey]);
}
public override DateTime GetNextOccurrence(DateTime now)
{
return now.Add(timer);
}
}
Use this Base to generate your timers...
public sealed class FooTimer : CustomTimerTriggerBase
{
public FooTimer() : base("FooTimerKey") {}
}
In your, App.config have a key for "FooTimer"
<add key="FooTimerKey" value="00:02:00" />
Use this FooTimer class in your webjob functions.
public void foo([TimerTrigger(typeof(FooTimer)] TimerInfo timer)
Now you can simply change the value in app config instead of redeploying the code.
NOTE: Since you are using Timespan to parse, the string can be of any format you need as defined in TimeSpan formats.
UPDATE
As pointed by l--''''''---------'''''''''''' and Andy Dobedoe
now (as of 2019) it is much simpler to achieve this.
public static async Task RunAsync([TimerTrigger("%MYCRON%")]TimerInfo myTimer
Finds the setting called MYCRON and uses the cron expression from there

You can do this like so:
public static void Run([TimerTrigger("%MYSCHEDULE%")] TimerInfo myTimer, ILogger log)
where MYSCHEDULE is an environment variable which you can store in your local.settings.json file as well as in your application settings in the portal.
An example value for MYSCHEDULE would be:
"MYSCHEDULE": "0 */2 * * * *"

It turns out, this is pretty easy nowadays. Just put the app setting in as your cron schedule expression and it will look it up for you.
e.g.
public static async Task RunAsync([TimerTrigger("%MYCRON%")]TimerInfo myTimer
Finds the setting called MYCRON and uses the cron expression from there

AFAIK, you need to specific the scheduleExpression parameter for TimerTrigger in your code or implement your WeeklySchedule or DailySchedule described in this sample TimerSamples.cs. For changing the schedule without re-deploy your code, I assume that you could leverage Azure Scheduler to trigger your webjob on some schedule and you could change the schedule settings as you expected without re-deploy your webjob. For more details, you could refer to the section about adding a scheduler job in this tutorial.

Related

ASPBoilerplate BackgroundJobs database pooling

I am using ABP framework to develop a web application. By default, the default background job manager pools the database every five seconds to find possible existing defined jobs. According to ABP documents and its implementation I find that it's set to five seconds by default. However, I am not able to update the timer to update this. Even if I tried to disable it using the code below, it was unsuccessful and it is still running.
if (DebugHelper.IsDebug)
{
Configuration.BackgroundJobs.IsJobExecutionEnabled = false;
}
To continue, is there a way to replace this background job to quartz only. I think in the case of adding Quartz integration, they both run concurrently.
Any help will be appreciated.
According to its implementation the default value has been set to five seconds and this can be configured and overridden at the PreInitialize method of the CoreModule. All it needs is below:
public override void PreInitialize()
{
...
BackgroundJobManager.JobPollPeriod = 20000; //20 seconds
...
}

Running an azure function on a specific time for only once?

So I'm working on sending mobile app push notifications and I've already set up Azure Notification hub, but I wanted to do scheduling in it, I understand there's an option in build in azure to do that, since it costs 200$ i decided to create Azure Functions to handle it,
I've gone through the NCRON Expressions, now I wanted to know how to schedule a job to run once on a specific date, all I could find is repetition based ones and also is it possible to run a job dynamically as in the date would vary
public static class Function1
{
[FunctionName("Function1")]
public static void Run([TimerTrigger("0 0 15 2 Jan")]TimerInfo myTimer, ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
}
}
I was trying to do something like this, running something on 2nd of Jan at 15:00Hrs, it doesn't seem to work, am I missing something here and how do I make TimerTrigger dynamic?
To describe clearly, I reedit the whole answer. Hope this time I can explain clearly.
OK. Fist of all, you need to know, azure Function has a declaration section and a configuration section.
On local, the declaration section is ([TimerTrigger("* * * * * *")]TimerInfo myTimer, ILogger log), and the configuration section is local.settings.json file.
When you deploy to Azure. It changes. Declaration section turns to function.json, and the Application Settings becomes the configuration section.
To your requirement, you can add a key in the configuration section and get it in your function.
For example,
On local:
function.cs:
using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host;
using Microsoft.Extensions.Logging;
namespace TimeTrigger
{
public static class Function1
{
[FunctionName("Function1")]
public static void Run([TimerTrigger("%Schedule%")]TimerInfo myTimer, ILogger log)
{
log.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");
}
}
}
local.settings.json:
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
"FUNCTIONS_WORKER_RUNTIME": "dotnet",
"Schedule": "* * * * * *"
}
}
You can change how the timetrigger is triggered by changing the value of the key in the json.(For example, using powershell to modify the value. Or you can use code to modify.)
On portal:
And similar as on local, you can do this on portal:
Declaration section:
Configuration Section:
You can generate the detailed CRON expression using below link:
http://corntab.com/?c=0_15_2_1_*
0 0 15 2 Jan * is the resulting CRON expression which will run it every 2nd day of January at 1500 hours for any years.
As rightly suggested by #Bowman, please check and validate the expression from above link and see if it helps.
Firstly the main problem you have now suppose is your expression, the right expression format you could refer to the doc:NCRONTAB expressions.
{second} {minute} {hour} {day} {month} {day-of-week}
Then about your requirement about run a job dynamically. You can put the schedule expression in an app setting and set this property to the app setting name wrapped in % signs, as in this example: %ScheduleAppSetting%. You could check it in the configuration.
Even with this expression it will show an error, however it will still works.
Why using a timer function if you need to run it only once?
I would say a better solution is to send a message to a queue so it runs at a scheduled time using the attribute ScheduledEnqueueTimeUtc.
https://learn.microsoft.com/en-us/azure/service-bus-messaging/message-sequencing#scheduled-messages

Debug Azure WebJob locally

I've been creating an Azure WebJob, it works aparently fine but I need to create a new function and I need test locally before upload to production site, I run on Debug the console program and this recognize all functions but I can't trigger any function.
Documentation say next trigger is every minute.... (https://github.com/Azure/azure-webjobs-sdk-extensions#timertrigger)
My code:
public static async void ProcessAugustEndowments([TimerTrigger("0 */1 * * * *", RunOnStartup = true)] TimerInfo timerInfo)
{
Console.WriteLine("Endowments process tried");
await endowmentNotification();
}
Output:
I run on Debug the console program and this recognize all functions but I can't trigger any function.
Based on your code, I tested it on my side and found that when my firstly debug the application, then I could get the following output:
But, when I restarted the application, I found that it would take some time for the function to be triggered.
Please make sure that you have installed the latest version packages of "Microsoft.Azure.WebJobs" and "Microsoft.Azure.WebJobs.Extensions". For more details, you could follow this tutorial.
And please try to reduce the time interval in your TimerTrigger and wait for a period when the job host has started, then try to find out whether the function could be triggered on your side. Here is my code sample, you could refer to it.
Program.cs
static void Main()
{
JobHostConfiguration config = new JobHostConfiguration();
// Add Triggers and Binders for Timer Trigger.
config.UseTimers();
JobHost host = new JobHost(config);
//host.RunAndBlock();
host.Start();
Console.WriteLine("[{0}] Job Host started!!!", DateTime.Now);
Console.ReadLine();
}
Function.cs
//Function triggered by a timespan schedule every 5 sec.
public static async void ProcessAugustEndowments([TimerTrigger("*/5 * * * * *", RunOnStartup = true)] TimerInfo timerInfo)
{
Console.WriteLine("Endowments process tried");
await endowmentNotification();
}
private static async Task endowmentNotification()
{
//sleep for 2 sec to simulate processing business logic
await Task.Delay(TimeSpan.FromSeconds(2));
}
Additionally, if the TimerTrigger could not meet your requirement, you could refer to this official tutorial to create a schedule WebJob, also you could refer to this blog.
If your functions are not triggered, I presume it is because you didn't configure your JobHost you use TimerTrigger:
var config = new JobHostConfiguration();
config.UseTimers();
How the JobHost works with Triggers :
When the JobHost starts, it discovers and indexes the functions with some *TriggerAttribute.
By Specifying the config.UseTimers(); you tell to the JobHost to index functions with TimerTriggerAttribute.
This is also valid for others types of triggers like ServiceBusTrigger that need config.UseServiceBus() to work

how to make scheduled process in asp.net/C# with mvc5

I have a MVC5 application that has a function to send e-mail to user after subscribing the newsletter. Now i have a requirement to send e-mail to all those users whose are running 1 month ahead of expiry date of their subscription. In this case i need to implement a background process that will run every day at a specific time on the web server. How can i do that?
Thanks
You can create a Windows Service to make that work for you.
You should follow this tutorial.
You can store the date/time on the project web.config/app.config which when you want your service to be executed. When the service executes, you validate the time and call a generic function that will do what you want. Follow this example:
public partial class Service1 : ServiceBase
{
YourServiceClass service;
private Timer serviceTimer;
public Service1()
{
InitializeComponent();
service = new YourServiceClass();
}
protected override void OnStart(string[] args)
{
TimerCallback timerDelegate = new TimerCallback(service.GetData); // You should add this function to your class. You have an example below
string time = System.Configuration.ConfigurationSettings.AppSettings["SceduleTime"]; // Gets time from app.config like 12:50
string[] timeS = time.Split(':');
DateTime DateIni = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, Convert.ToInt32(timeS[0]), Convert.ToInt32(timeS[1]), 0);
TimeSpan diff = DateTime.Now - DateIni;
if (diff.TotalSeconds < 0)
diff = DateIni - DateTime.Now;
else
diff = DateIni.AddDays(1) - DateTime.Now;
// create timer and attach our method delegate to it
serviceTimer = new Timer(timerDelegate, service, diff, new TimeSpan(1, 0, 0, 0));
}
protected override void OnStop()
{
serviceTimer.Dispose();
}
}
And on your YourServiceClass class, you add this function:
public void GetData(object state){
// Do something...
}
Hope it helps!
Personally, I'd create a separate dedicated Windows Service to periodically check for expiry, and then call the code to send the emails.
An alternative is to create a simple console application to run the task, and call it using the Windows Task Scheduler.
A less secure method is to use a ping service, to periodically hit a page with an obfuscated URL which processes the emails.
A slightly old, but relevant blog post, detailing the issues with recurrent background tasks in .net sites can be read here.
I recommend you to use Quartz library. Your process should be independent from your web application since the web app will recycle from time to time based on the user traffic which will trigger Application_End event and will start once a new request is sent which will trigger Application_Start so you wont be able to manage correctly your schedule start and end.
It really depends on your requirements, and whether you want this update to be truly independent on the server, in a web application or bundled with your app.
I recently found a great third party library for ASP.NET called Hang Fire.
It looks like it can be as simple as:
RecurringJob.AddOrUpdate(
() => Console.WriteLine("Transparent!"),
Cron.Daily);
It supports:
Delayed Jobs
Recurring Tasks
Automatic Retries
And it has a nicely themed Management/Reporting interface
I'm planning to use it for some scheduled batch tasks I need to run on a CRM system. No experience with it yet, but it does seem to have some great features and I think I've seen it recommended a few times around Stack Overflow!
There are a ton of different ways to approach what you want (Scheduled Task on the server is another off the top of my head). This is a nice little package that lets me bundle it with my web applications.

System.Timer as a Singleton

I have data which is constantly being read by many threads. This data needs to be updated daily.
My approach has been to use a ReaderWriterLockSlim to manage access to the data. Every night the first thread to detect the day change applies a WriteLock to the data and updates it.
In order to avoid the constant check for the day change event. I would ideally like to create a System.Timer object as a singleton and have it automatically start and then execute every 24hrs thereafter.
This has been my approach:
First I extended System.Timers to execute the callback on init.
using System.Timers;
namespace Utilities
{
class AutoStartTimer : Timer
{
public AutoStartTimer(ElapsedEventHandler callback, int period):base(period)
{
callback(null, null);
AutoReset = true;
Elapsed += callback;
Enabled = true;
}
}
}
Then I declared it at a singleton where I needed it.
private static AutoStartTimer _loadDataTimer =
new AutoStartTimer(DataLoader, 86400000); // Daily
This approach is working for me so far. However I would like to know if there are any better ways to implement a Singleton Timer which executes once on initialisation and then for a set period afterwards or if anyone has managed to do this more efficiently without extending the Timer class.
I need to use many of these in my current project so I want to make sure I am using a good approach.
Thanks.
Using a static class:
public static class DayManager
{
public static readonly object SyncRoot = new object();
private static readonly Timer dayTimer;
static DayManager()
{
dayTimer = new Timer { AutoReset = true; Enabled = true; Interval = 86400000d };
dayTimer.Elapsed += OnDayTimerElapsed;
}
protected void OnDayTimerElapsed(object sender, ElapsedEventArgs e)
{
if(DayPassedEvent != null)
{
DayPassedEvent(this, null);
}
}
public event EventHandler DayPassedEvent;
}
Now, in each of the threads you should subscribe to the DayPassedEvent and use Monitor.TryEnter(DayManager.SyncRoot) to acquire a lock on the timer managing class. This means that only one thread should go on to try to update the data and the rest should fail to get the lock and continue with their lives. I'll leave the exact implementation of this up to you.
Alternatively, you could remove the SyncRoot from the timer managing class here and use another as you're already doing, I just provided it for reference only.
I have better approach for you. Use Codeplex's Lightweight Scheduler library.
A lightweight task scheduling library that allows you to easily schedule the invocation of callback methods at specified times or intervals. Supports .NET 3.5 and Silverlight.
The library allows you to include flexibule scheduling functionality into your application with just a few lines of code, and provides a fluent API to configure jobs:
Link:
http://scheduling.codeplex.com/
Other approaches:
Task Scheduler Class in .NET:
http://msdn.microsoft.com/en-us/library/system.threading.tasks.taskscheduler.aspx
http://taskscheduler.codeplex.com/
http://www.codeproject.com/Articles/1763/Task-Scheduler-Library-for-NET

Categories