Implementing our interface

If you remember what we talked about earlier with inheritance, we now need to implement the interface methods we are getting from IBaseMicroService. Let's begin with our Start method. This method basically configures and starts our timer for us:

public virtual bool Start(HostControl hc)
{_host = hc;
Console.WriteLine(_name + string.Intern("Service Started."));
Assumes.True(_timer!= null, string.Intern("_timer is null"));
_timer.AutoReset = true;
_timer.Enabled = true;
_timer.Start();
return true;
}

Next comes our Stop method. This method will log that our service has stopped, stop our timer, and close our RabbitMQ connections and channels for proper cleanup:

public virtual bool Stop()
{
Assumes.True(_log != null, string.Intern("_log is null"));
_log?.Info(_name + string.Intern(" Service is Stopped"));
Assumes.True(_timer != null, string.Intern("_timer is null"));
_timer.AutoReset = false;
_timer.Enabled = false;
return true;
}

The final few methods will remain empty for now aside from logging that they were received. As minor as this may seem, there have been many cases where we suspected a microservice was hung in memory with the process still running. Being able to see that a stop message was given, that power was low, are all invaluable should you ever need to debug any code:

public virtual bool Resume()
{
using (var scope = IOCContainer?.BeginLifetimeScope())
{
var logger = scope?.Resolve<MSBaseLogger>();
logger?.LogInformation(Name + " Microservice Resuming");
}
return true;
}
Our Pause method
public virtual bool Pause()
{
using (var scope = IOCContainer?.BeginLifetimeScope())
{
var logger = scope?.Resolve<MSBaseLogger>();
logger?.LogInformation(Name + " Microservice Pausing");
}
return true;
}
Our Continue method
public virtual bool Continue()
{
using (var scope = IOCContainer?.BeginLifetimeScope())
{
var logger = scope?.Resolve<MSBaseLogger>();
logger?.LogInformation(Name + " Microservice Continuing");
}
return true;
}
Our Shutdown method
public virtual bool Shutdown()
{
using (var scope = IOCContainer?.BeginLifetimeScope())
{
var logger = scope?.Resolve<MSBaseLogger>();
logger?.LogInformation(Name + " Microservice Shutting Down");
}
return true;
}
Last but not least we need to look at our Stop method.
public virtual bool Stop()
{
using (var scope = IOCContainer?.BeginLifetimeScope())
{
var logger = scope?.Resolve<MSBaseLogger>();
logger?.LogInformation(Name + " Microservice Stopping");
}
Assumes.True(_log != null, string.Intern("_log is null"));
_log?.Info(_name + string.Intern(" Service is Stopped"));
Assumes.True(_timer != null, string.Intern("_timer is null"));
_timer.AutoReset = false;
_timer.Enabled = false;
_timer.Stop();
return true;
}

This method will be called when our service is stopped. We will log the message and stop our timer.

With all this complete, we now need to implement our TryRequest function. These functions will take an action, wrap it in a circuit breaker, and handle any exceptions that might occur. They are basically wrapper functions that we can use to apply the circuit breaker pattern to our microservices, but we don't have to worry about the elegant details:

public void TryRequest(Action action, int maxFailures, int startTimeoutMS = 100, int resetTimeout = 10000, Action<Exception> OnError = null)
{
try
{
Requires.True(maxFailures >= 1, "maxFailures must be >= 1");
Requires.True(startTimeoutMS >= 1, "startTimeoutMS must be >= 1");
Requires.True(resetTimeout >= 1, "resetTimeout must be >= 1");
// Initialize the circuit breaker
var circuitBreaker = new CircuitBreaker(
TaskScheduler.Default,
maxFailures: maxFailures,
invocationTimeout: TimeSpan.FromMilliseconds(startTimeoutMS),
circuitResetTimeout: TimeSpan.FromMilliseconds(resetTimeout));
circuitBreaker.Execute(() => action);
}
catch (CircuitBreakerOpenException e1)
{
OnError?.Invoke(e1);
Console.WriteLine(e1.Message);
}
catch (CircuitBreakerTimeoutException e2)
{
OnError?.Invoke(e2);
Console.WriteLine(e2.Message);
}
catch (Exception e3)
{
OnError?.Invoke(e3);
Console.WriteLine(e3.Message);
}}

In this function, we are creating our circuit breaker using the default task scheduler, allowing maxFailures failure attempts (usually set to two or three), and specifying when to start and when to reset. After that, we execute the action and handle any exception. In a production application, you would probably want to log any exceptions to a database and send a message to your health monitor application when the circuit breaker opens and closes (more on that later).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset