Classic requirement of checking system state and notifying users. Specifically, I'll be hitting the database every x-amount of time, getting some data, then sending out email notifications based on the results. Heck, this service might not even send out an email, but create a notification record in the database.
Seems like with IOC and configuration there could be a generic windows service that manages all this, along with metrics and management, in a simple manner.
In the past I've done email notifications by:
1) Running scripts as cron (at on Windows) jobs
2) running custom executables as cron/at jobs
3) using something like SQLServer's DatabaseMail.
4) Custom NT Services that run all the time monitoring things.
Is there any open source projects that manages this? It's the type of code I've written many, many times in various platforms, but don't want to spend the few days doing it now.
The only thing I found so far was Quartz.Net
Thanks
I just create a Windows service and use the Reactive Extensions to schedule tasks. If you don't need as much flexibility as cron offers, this works fine. Here's an abstract hourly task runner. (uses log4net)
public abstract class HourlyTask : IWantToRunAtStartup
{
protected readonly ILog Log = LogManager.GetLogger(typeof (HourlyTask).FullName);
private IDisposable _Subscription;
private void ExecuteWithLog()
{
Log.Debug("Triggering " + GetType());
try
{
Execute();
}
catch (Exception exception)
{
Log.Error("Hourly execution failed", exception);
}
}
public abstract void Execute();
public void Run()
{
_Subscription = Observable
.Interval(TimeSpan.FromMinutes(1))
.Select(x => DateTime.Now.Hour)
.DistinctUntilChanged()
.Subscribe(i => ExecuteWithLog());
}
public void Stop()
{
if (_Subscription == null)
{
return;
}
_Subscription.Dispose();
}
}
Then in your start up method you can just resolve all IWantToRunAtStartup instances and call Run() on them.
Related
Spring Cloud Data Flow's documentation describes how to integrate with kubernetes Readiness probes. I'm developing my dataflow locally and running it in a docker-compose configuration, while we wait for our k8s SCDF environment to be stood up.
Is there another way to implement a readiness / do not send data context for SCDF? Upon component spin-up, I need to make a RESTful call and then run some computations on the results. Things attempted unsuccessfully:
use of ApplicationAvailabilityEvents - publishing a ReadinessState.ACCEPTING_TRAFFIC after the load + compilation is complete, after publishing a ReadinessState.REFUSING_TRAFFIC. When Spring completes its own load, it publishes an ACCEPTING_TRAFFIC, and so doesn't wait for mine from my service.
setting up an ApplicationRunner which also serves as an ApplicationListener for custom events which I throw when the computations are complete. Effectively, the run() method looks like:
public class ApplicationStartupRunner implements ApplicationRunner, ApplicationListener {
private boolean sessionLoaded = false;
public void run(ApplicationArguments args) {
doTimeExpensiveThing();
while (!sessionLoaded) {
TimeUnit.MILLISECONDS.sleep(150);
}
}
public void onApplicationEvent(SessionLoadEvent event) {
this.sessionLoaded = true;
}
}
Additional technical note: the Spring Boot application is built as a processor, which is using a function exposed as a Bean to provide its capability, ala
public Function<Flux<ChangeEvent>, Flux<Alert>> processChangeEvents()
Optimally, whatever approach I use which works in docker-compose, I'll wire into an indicator which'll be picked up by k8s and its readiness probe. Given that SCDF can be deployed on k8s, docker-compose (locally), or CloudFoundry, hoping that there's a model I can hook into that I've just overlooked.
Potential answer: instead of using the ApplicationRunner, wait in the processChangeEvents method and do not return the function until startup processing is complete.
In our case, because the doTimeExpensiveThing is an asynchronous activity, I use the technique of watching/waiting for the sessionLoaded flag, but now within the processChangeEvents method itself.
#Configuration
public class ConfigurationForProcessor implements ApplicationEventListener<SessionLoadEvent> {
boolean sessionLoaded;
Function<Flux<ChangeEvent>, Flux<Alert>> processChangeEvents() {
doTimeExpensiveAsynchronousThing();
while (!sessionLoaded) {
TimeUnit.MILLISECONDS.sleep(150);
}
return (Flux<ChangeEvent>) -> ... code which returns a Flux of Alert
}
public void onApplicationEvent(SessionLoadEvent event) {
this.sessionLoaded = true;
}
}
Very open to guidance on other approaches. This appears like it's working, though not sure there aren't gotchas I haven't caught yet.
I am looking into Microsoft Orleans. I have setup a cluster in docker, tt is working perfectly. I am trying to read the documentation on message delivery, but I can't seem to find anything on retry. If I have a grain calling a grain, like this:
public class HelloGrain : Orleans.Grain, IHello
{
private readonly ILogger logger;
private IOtherGrain otherGrain;
public HelloGrain(ILogger<HelloGrain> logger)
{
this.logger = logger;
}
public override async Task OnActivateAsync()
{
otherGrain = GrainFactory.GetGrain(this.GetPrimaryKeyString());
await base.OnActivateAsync();
}
Task<string> IHello.SayHello(string greeting)
{
string otherGrainReturn = await this.otherGrain.MethodAsync();
return Task.FromResult($"\n Client said: '{greeting}', so HelloGrain says: Hello!");
}
}
Is the string otherGrainReturn = await this.otherGrain.MethodAsync(); getting retried, on failure, somehow? Is it only a timeout that decide when the call fails? Is this to be handled as a basic HTTP call, and should i therefore be retrying myself?
It could be awesome with som link to documentation that say something more on the subject (at-least-once retry etc).
You can see here that by design, Orleans doesn't have any kind of retry policy.
But you can add it by yourself with a simple try catch block (Or with a specific library like Polly).
I am very new with orleans and trying to grasp everything with grains and so forth.
What i got is that in my startup.cs file i add the SignalR like this
public IServiceProvider ConfigureServices(IServiceCollection services)
{
Program.WriteConsole("Adding singletons");
services
.AddSingleton(achievementManager)
.AddMvc();
services.AddSingleton(SignalRClient);
return services.BuildServiceProvider();
}
So far everything is fine i can start my host/application and it connects to SignalR as it should. But what i cant wrap my head around is how do i get this down to my grain? if i had a controller i would simply send it down in the constructor on startup but how do i do this with a grain? Or can i even do it like this. Any guidance is appreciated.
In the grain then i want to do something like this
[StatelessWorker]
[Reentrant]
public class NotifierGrain : Grain, INotifierGrain
{
private HubConnection SignalRClient { get; }
public NotifierGrain(HubConnection signalRClient)
{
SignalRClient = signalRClient;
SignalRClient.SendAsync(Methods.RegisterService, Constants.ServiceName);
}
public Task NotifyClients(object message, MessageType type)
{
var registerUserNotification = (RegisterUserNotificationModel)message;
SignalRClient.SendAsync(Methods.RegisterUserToMultipleGroups, registerUserNotification.UserId, registerUserNotification.InfoIds);
}
return Task.CompletedTask;
}
Then i try to call the Notify method from another grain like this
var notifier = GrainFactory.GetGrain<INotifierGrain>(Constants.NotifierGrain);
await notifier.NotifyClients(notification, MessageType.RegisterUser);
But trying to do this ends up with an error like this
InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.SignalR.Client.HubConnection' while attempting to activate 'User.Implementation.Grains.NotifierGrain'.
Orleans supports constructor injection, so you can inject the SignalRClient into your grain constructor. In your code you are already correctly registering the client using services.AddSingleton(SignalRClient), so I will focus on how to inject the type into your grain.
I do not know what the type the SignalR client object is, but in this example I assume that the type is "SignalRClient":
[StatelessWorker]
[Reentrant]
public class NotifierGrain : Grain, INotifierGrain
{
private readonly SignalRClient signalRClient;
public NotifierGrain(SignalRClient signalRClient)
{
this.signalRClient = signalRClient;
}
public async Task NotifyClients(object message, MessageType type)
{
var registerUserNotification = (RegisterUserNotificationModel)message;
await this.signalRClient.SendAsync(
MessageMethods.RegisterUserToMultipleGroups,
registerUserNotification.UserId,
registerUserNotification.infoIds);
}
}
Depends how you're thinking to use SignalR Server, if you're going to host your SignalR server with Microsoft Orleans for sure you need to have backplane to handle the Orleans cluster communications.
You can use SignalR Orleans which has everything done out of the box for you :)
Also if you need a reactive SignalR library for the frontend, you can use Sketch7 SignalR Client
PS I m one of the authors of both libraries.
Below I present a part of an Azure Web App that handles a device notification logic. What I'm doing is invoking the code shown below from a ASP MVC Controller.
But when I run it I get an hung (ever-pending) request from the browser.
I thought I've found a workaround by wrapping the SendAsync call in a BackgroundWorker thread. It's better, but I doesn't work right. For first couple of times (one or two) it works ok, but then it happens again, the wrapped thread hangs.
The code is not far different from the one on MSDN for a console application. What am I missing?
using System.Web.Configuration;
using Microsoft.Azure.Devices;
namespace MyTest
{
public class Sender
{
private readonly string connectionString;
private readonly Microsoft.Azure.Devices.ServiceClient serviceClient;
public Sender()
{
connectionString = WebConfigurationManager.AppSettings["ConnectionString"];
serviceClient = ServiceClient.CreateFromConnectionString(connectionString);
}
public async void SendRequest(string deviceId, string msgText)
{
var message = new Message();
message.Properties.Add("text", msgText));
await serviceClient.SendAsync(deviceId, message);
}
}
}
The problem was caused by inappropriate usage of ASP MVC framework.
It turned out that AsyncController has to be used instead of just Controller when a long running async\await is utilized. The pipeline must be async all the way.
We are currently implementing private messaging in our asp.net mvc app and we have come to the conclusion that we would like to make it work like github does... meaning we would like the user to receive the message both through our site and to his email inbox(this is somewhat easy to achieve with mvcmailer nuget).. but we would also like the option to have the user reply to the email (through his email client) and have that email be sent to our app (if you have used guthubs private messaging you should know what I mean)..... what is a common approach to implementing this feature?
See my comment regarding how to read emails from a mailbox.
I would use a message bus so that you are not limited to just internal and email notifications, for example you may wish to add SMS notifications in the future.
You may want to check out MassTransit or NServiceBus although for something like this it may be easier to just create your own.
public class Message {
public string Title {get;set;}
public string Body {get;set;}
}
public interface IMessageBus {
void Send(Message message);
}
public interface IMessageHandler {
void Handle(Message message);
}
public class InternalMessageHander : IMessageHandler {
public void Handle(Message message) {
// send internal message
}
}
public class EmailMessageHandler : IMessageHandler {
public void Handle(Message message) {
// send email
}
}
Your IMessageBus implementation would need to locate all the handlers (I would use an IoC container for this).
If you need to process large volumes of messages then I would recommend handing these off to a queue and processing these asynchronously (both MassTransit and NServiceBus support message queues but you could just as easily use a simple database table).
As far as reading emails, I would have background task that connects to the mailbox, downloads emails, parses them and updates your content.
Usually you put either something in the subject line or at the end of the body to track the email. Any UID will do.
Hope that helps.