I have an application which offers a "Stop()" OperationContract.
When it is called the application should close() the ServiceHost.
public void Stop()
{
try
{
_serviceHost.Close(TimeSpan.FromSeconds(10));
}
catch (CommunicationException comEx)
{
Console.WriteLine(comEx.ToString());
_serviceHost.Abort();
}
}
Of course this is a problem because the incoming request is still active when trying to close the ServiceHost. How do I circumvent that issue? Separate thread on a short delay?
public void Stop()
{
Task.Factory.StartNew(WaitAndKill);
}
private void WaitAndKill()
{
Thread.Sleep(500);
_managementServiceHost.Close();
}
Related
I have been creating a project with Aspect Oriented Programming paradigm and
I have an "ExceptionLogAspect" class attribute which is used on business methods to log the errors throwing from them.
public class ExceptionLogAspect : MethodInterception
{
private readonly LoggerServiceBase _loggerServiceBase;
private static byte _risk;
public ExceptionLogAspect(Type loggerService, byte risk)
{
if (loggerService.BaseType != typeof(LoggerServiceBase))
{
throw new System.Exception(AspectMessages.WrongLoggerType);
}
_loggerServiceBase = (LoggerServiceBase)Activator.CreateInstance(loggerService);
_risk = risk;
}
protected override void OnException(IInvocation invocation, System.Exception e)
{
var logDetailWithException = GetLogDetail(invocation);
logDetailWithException.ExceptionMessage = e.Message;
_loggerServiceBase.Error(logDetailWithException);
}
}
This Aspect migrates MethodInterception class that I created with Castle.DinamicProxy package. And OnException method included by MethodInterception logs the exception data.
public abstract class MethodInterception:MethodInterceptionBaseAttribute
{
protected virtual void OnBefore(IInvocation invocation){}
protected virtual void OnAfter(IInvocation invocation){}
protected virtual void OnException(IInvocation invocation, System.Exception e){}
protected virtual void OnSuccess(IInvocation invocation){}
public override void Intercept(IInvocation invocation)
{
var isSuccess = true;
OnBefore(invocation);
try
{
invocation.Proceed();//Business Method works here.
}
catch (Exception e)
{
isSuccess = false;
OnException(invocation, e);
throw;
}
finally
{
if(isSuccess)
OnSuccess(invocation);
}
OnAfter(invocation);
}
}
When I run the code and try-catch block doesn't work for Exception. So catch block isn't called and no messages are logged.
If I turn the business method into a syncronous method, exception will be thrown and data will be logged.
How can I solve this asynchronous method problem?
I tried this solution, it works properly.
Intercept method has to be like this to make this process asynchronous.
Otherwise, this method doesn't work properly for async.
There are some other ways, for example Castle CoreAsync Interceptor, you can find it on Github or NuGet.
https://github.com/JSkimming/Castle.Core.AsyncInterceptor
public override void Intercept(IInvocation invocation)
{
var isSuccess = true;
OnBefore(invocation);
try
{
invocation.Proceed(); //Metodu çalıştır
if (invocation.ReturnValue is Task returnValueTask)
{
returnValueTask.GetAwaiter().GetResult();
}
if (invocation.ReturnValue is Task task && task.Exception != null)
{
throw task.Exception;
}
}
catch (Exception e)
{
isSuccess = false;
OnException(invocation, e);
throw;
}
finally
{
if (isSuccess)
OnSuccess(invocation);
}
OnAfter(invocation);
}
I am trying to log any information or exception that occurs during message sending in RabbitMQ, for that I tried to add ConnectionListener on the existing connection factory.
kRabbitTemplate.getConnectionFactory().addConnectionListener(new ConnectionListener() {
#Override
public void onCreate(Connection connection) {
System.out.println("Connection Created");
}
#Override
public void onShutDown(ShutdownSignalException signal) {
System.out.println("Connection Shutdown "+signal.getMessage());
}
});
kRabbitTemplate.convertAndSend(exchange, routingkey, empDTO);
To test the exception scenario, I unbind and even deleted the queue from RabbitMQ console. But I did not get any exception or any shutdown method call.
Although, When I stopped RabbitMQ service, I got
Exception in thread "Thread-5" org.springframework.amqp.AmqpConnectException: java.net.ConnectException: Connection refused: connect
But this exception is not from the listener I added.
I want to know
Why I did not get any exception or call from shutdown method
How can I use ConnectionListner and/or ChannelListner for logging failure/success of message delivery.
Can we use the AMQP appender, if yes how can we do that? (any example / tutorial)
What are the other approaches to ensure the message is sent?
Note: I do not want to use the publisher confirm the approach.
Connection Refused is not a ShutdownSignalException - the connection was never established because the broker is not present on the server/port.
You can't use the listeners to confirm delivery or return of individual messages; use publisher confirms and returns for that.
https://docs.spring.io/spring-amqp/docs/current/reference/html/#publishing-is-async
See the documentation for how to use the appenders.
https://docs.spring.io/spring-amqp/docs/current/reference/html/#logging
EDIT
To get notified of failures to connect, you currently need to use other techniques, depending on whether you are sending or receiving.
Here is an example that shows how:
#SpringBootApplication
public class So66882099Application {
private static final Logger log = LoggerFactory.getLogger(So66882099Application.class);
public static void main(String[] args) {
SpringApplication.run(So66882099Application.class, args);
}
#RabbitListener(queues = "foo")
void listen(String in) {
}
// consumer side listeners for no connection
#EventListener
void consumerFailed(ListenerContainerConsumerFailedEvent event) {
log.error(event + " via event listener");
if (event.getThrowable() instanceof AmqpConnectException) {
log.error("Broker down?");
}
}
// or
#Bean
ApplicationListener<ListenerContainerConsumerFailedEvent> eventListener() {
return event -> log.error(event + " via application listener");
}
// producer side - use a RetryListener
#Bean
RabbitTemplate template(ConnectionFactory cf) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(cf);
RetryTemplate retry = new RetryTemplate();
// configure retries here as needed
retry.registerListener(new RetryListener() {
#Override
public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
return true;
}
#Override
public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback,
Throwable throwable) {
log.error("Send failed " + throwable.getMessage());
}
#Override
public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback,
Throwable throwable) {
}
});
rabbitTemplate.setRetryTemplate(retry);
return rabbitTemplate;
}
#Bean
public ApplicationRunner runner(RabbitTemplate template) {
return args -> {
try {
template.convertAndSend("foo", "bar");
}
catch (Exception e) {
e.printStackTrace();
}
};
}
}
I'm trying to gracefully terminate a ASP.Net Core 3.1 service (which will run in Kubernetes). When Kubernetes stops a service, it will send a SIGTERM event to the application, at which point I want in-flight requests to complete (which may take several seconds) before terminating... I think I can catch this in a hostedservice, as below, and hence not stop immediately.
The following works, but with a timeout of 5 seconds or longer, I receive an OperationCanceledException. Could anyone shed any light on why I get an OperationCanceledException or how shed any light on an alternative way to delay a SIGTERM event, to allow a graceful shutdown?
public static int Main(string[] args)
{
var logger = NLogBuilder
.ConfigureNLog("nlog.config")
.GetCurrentClassLogger();
try
{
CreateHostBuilder(args)
.ConfigureServices((hostBuilderContext, services) => { services.AddHostedService<LifetimeEventsHostedService>(); })
.Build()
.Run();
return 0;
}
catch (Exception e)
{
logger.Fatal(e, "Stopping due to exception");
return -1;
}
finally
{
LogManager.Shutdown();
}
}
This is the hosted service...
internal class LifetimeEventsHostedService : IHostedService
{
private readonly Microsoft.Extensions.Logging.ILogger _logger;
private readonly IHostApplicationLifetime _appLifetime;
public LifetimeEventsHostedService(
ILogger<LifetimeEventsHostedService> logger,
IHostApplicationLifetime appLifetime)
{
_logger = logger;
_appLifetime = appLifetime;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_appLifetime.ApplicationStarted.Register(OnStarted);
_appLifetime.ApplicationStopping.Register(OnStopping);
_appLifetime.ApplicationStopped.Register(OnStopped);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
private void OnStarted()
{
_logger.LogInformation("OnStarted has been called.");
// Perform post-startup activities here
}
private void OnStopping()
{
_logger.LogInformation("OnStopping has been called.");
// Perform on-stopping activities here
// This works, but a timeout of 5 seconds or more subsequently causes an OperationCanceledException
Thread.Sleep(5000);
}
private void OnStopped()
{
_logger.LogInformation("OnStopped has been called.");
// Perform post-stopped activities here
}
}
I'm open to alternative approaches to graceful shutdown with ASP.Net Core 3.1, as it stands, I'm using a hosted service.
Within the .Net Core app, I was setting ShutdownTimeout on the webhost, however, setting the ShutdownTimeout on the generic host, does allow me to gracefully wait a number of seconds (more than the default sigterm, which is 5 seconds) prior to shutdown. The hint from #PmanAce helped me work that out.
As such, the following codes allow me to gracefully terminate. One caveat, the Thread.Sleep in LifetimeEventsHostedService must be less than option.ShutdownTimeout.
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostBuilderContext, services) =>
{
services.AddHostedService<LifetimeEventsHostedService>();
services.Configure<HostOptions>(option =>
{
option.ShutdownTimeout = TimeSpan.FromSeconds(30);
});
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseKestrel();
webBuilder.UseStartup<Startup>();
});
}
The following LifetimeEventsHostedService
public class LifetimeEventsHostedService : IHostedService
{
private readonly IHostApplicationLifetime _hostApplicationLifetime;
public LifetimeEventsHostedService(IHostApplicationLifetime hostApplicationLifetime)
{
_hostApplicationLifetime = hostApplicationLifetime;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_hostApplicationLifetime.ApplicationStarted.Register(OnStarted);
_hostApplicationLifetime.ApplicationStopping.Register(OnStopping);
_hostApplicationLifetime.ApplicationStopped.Register(OnStopped);
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
private void OnStopped()
{
Console.WriteLine("OnStopped");
}
private void OnStopping()
{
Console.WriteLine("OnStopping");
Console.WriteLine(DateTime.Now.ToLongTimeString());
Thread.Sleep(15000);
Console.WriteLine("Sleep finished");
Console.WriteLine(DateTime.Now.ToLongTimeString());
}
private void OnStarted()
{
Console.WriteLine("OnStarted");
}
}
What is the best approach to handle exception of a Task that does not Wait()? I read a couple of blogs which spoke about using ContinueWith because regular try/catch cannot handle Task exception. Below code does not validate that.
Method 1:
public class Service1 : IService1
{
public string GetData(int value)
{
var a = Task.Factory.StartNew(ThrowException);
return string.Format("You entered: {0}", value);
}
private void ThrowException()
{
try
{
Thread.Sleep(6000);
throw new ArgumentException("Hello from exception");
}
catch (Exception)
{
Trace.WriteLine("Log it");
}
}
}
Method 2:
public class Service1 : IService1
{
public string GetData(int value)
{
var a = Task.Factory.StartNew(ThrowException);
a.ContinueWith(c => { Trace.WriteLine("Log it"); },
TaskContinuationOptions.OnlyOnFaulted);
return string.Format("You entered: {0}", value);
}
private void ThrowException()
{
Thread.Sleep(6000);
throw new ArgumentException("Hello from exception");
}
}
Are Method 1 and Method 2 doing the same thing? Is there a better way to implement this.
Edit: Added code snippet for continuewith.
Both methods work and they are equivalent. Choose what you like most. The continuation based one has the advantage that you can make the error handling into an extension method (or some other central helper).
Are you aware that IIS worker processes can suddenly disappear for many reasons? In that case background work is lost. Or, the work faults but the error handler disappears.
It looks like it will work if all you need is to call methods on the Trace class. However, if you need custom exception handling, I would recommend injecting an exception handler:
private void ThrowException(Action<Exception> handleExceptionDelegate)
{
try
{
// do stuff that may throw an exception
}
catch (Exception ex)
{
if (handler != null)
handleExceptionDelegate(ex);
}
}
Then you could do
Task.Factory.StartNew(() =>
{
ThrowException((ex) =>
{
// Handle Exception
});
});
How do I handle an exception thrown in a callback method on the client in a WCF duplex setup?
Currently, the client does not appear to raise the faulted event (unless I'm monitoring it incorrectly?) but any subsequent to call Ping() using the the client fails with CommunicationException: "The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it has been Aborted.".
How do I deal with this and recreate the client etc? My first question is how to find out when it happens. Secondly, how best to deal with it?
My service and callback contracts:
[ServiceContract(CallbackContract = typeof(ICallback), SessionMode = SessionMode.Required)]
public interface IService
{
[OperationContract]
bool Ping();
}
public interface ICallback
{
[OperationContract(IsOneWay = true)]
void Pong();
}
My server implementation:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Single)]
public class Service : IService
{
public bool Ping()
{
var remoteMachine = OperationContext.Current.GetCallbackChannel<ICallback>();
remoteMachine.Pong();
}
}
My client implementation:
[CallbackBehavior(UseSynchronizationContext = false, ConcurrencyMode = ConcurrencyMode.Single)]
public class Client : ICallback
{
public Client ()
{
var context = new InstanceContext(this);
var proxy = new WcfDuplexProxy<IApplicationService>(context);
(proxy as ICommunicationObject).Faulted += new EventHandler(proxy_Faulted);
//First Ping will call the Pong callback. The exception is thrown
proxy.ServiceChannel.Ping();
//Second Ping call fails as the client is in Aborted state
try
{
proxy.ServiceChannel.Ping();
}
catch (Exception)
{
//CommunicationException here
throw;
}
}
void Pong()
{
throw new Exception();
}
//These event handlers never get called
void proxy_Faulted(object sender, EventArgs e)
{
Console.WriteLine("client faulted proxy_Faulted");
}
}
As it turns out, you cannot expect the Faulted event to be raised. So, the best way to re-establish the connection is to do it when the subsequent call to Ping() fails:
I'll keep the code simple here:
public class Client : ICallback
{
public Client ()
{
var context = new InstanceContext(this);
var proxy = new WcfDuplexProxy<IApplicationService>(context);
(proxy.ServiceChannel as ICommunicationObject).Faulted +=new EventHandler(ServiceChannel_Faulted);
//First Ping will call the Pong callback. The exception is thrown
proxy.ServiceChannel.Ping();
//Second Ping call fails as the client is in Aborted state
try
{
proxy.ServiceChannel.Ping();
}
catch (Exception)
{
//Re-establish the connection and try again
proxy.Abort();
proxy = new WcfDuplexProxy<IApplicationService>(context);
proxy.ServiceChannel.Ping();
}
}
/*
[...The rest of the code is the same...]
//*/
}
Obviously, in my example code, the Exception will be thrown again but I hope this is useful to give people an idea of how to re-establish the connection.