Our .NET app uses 2 AppDomains. The secondary domain needs access to a Logger object that was created in the main appdomain.
This logger is exposed via a WCF service with a named pipe binding.
This is how i create the "client" for this service:
private void InitLogger()
{
if (loggerProxy != null)
{
Logger.Instance.onLogEvent -= loggerProxy.Log;
}
// Connect to the logger proxy.
var ep = new EndpointAddress("net.pipe://localhost/app/log");
var binding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
//Logger.Debug("Creating proxy to Logger object.");
var channelFactory = new ChannelFactory<ILogProvider>(binding, ep);
loggerProxy = channelFactory.CreateChannel();
channelFactory.Faulted += (sender, args) => InitLogger();
channelFactory.Closed += (sender, args) => InitLogger();
Logger.Instance.onLogEvent += loggerProxy.Log;
}
Recently we are getting random CommunicationObjectFaultedException - i suppose this occurs since the channel times out or due to some other reason that i am missing.
This is the reason i have added the handling of the Closed and Faulted events, which seem to not work properly (perhaps i have not used them appropriately).
EDIT: These events are on the Factory object as suggested, so this explains why they are not being raised.
My question is -- how can i avoid these errors?
Our scenario is we need to keep this channel open at all times throughout the application's lifetime, and the access to this Logger service is needed at all times, and shouldn't time out under any circumstance.
Is there any safe practice of handling this type of situation?
Your code is currently handling Closed and Faulted events raised by the ChannelFactory, but it is the state of the Channel itself you need to worry about.
The ChannelFactory is an artefact which encapsulates the translation of the WCF service contract into an instance of the channel runtime: once you have successfully created your channel (loggerProxy), the closing of the ChannelFactory isn't going to affect communications via the channel - the events you are listening for are irrelevant to your problem.
State transitions of the Channel to Closed or Faulted will go unnoticed to this code, with the result that they will surface in Logger.Instance as exceptions thrown when loggerProxy.Log is invoked, and the event you are trying to log will be lost.
Instead of registering loggerProxy.Log directly as the event handler you should consider registering a wrapper function implementing an exception handler and retry loop around the call to loggerProxy.Log. The existing channel should be closed (or if that fails, aborted) in the exception handler, to ensure it is Disposed properly. The retry loop should reinitialise the channel and try the call again.
I'll comment on two things (i) the timeout and (ii) catching the Faulted events.
Firstly the timeout. By default, channels enter the faulted state if they haven't had a communication during the default time period (around 10 minutes). You can either poke the channel frequently with a recurring event, or reset the timeout to something large. I do the latter as follows:
NetNamedPipeBinding binding = new NetNamedPipeBinding();
// Have to set the receive timeout to be big on BOTH SIDES of the pipe, otherwise it gets faulted and can't be used.
binding.ReceiveTimeout = TimeSpan.MaxValue;
DuplexChannelFactory<INodeServiceAPI> pipeFactory =
new DuplexChannelFactory<INodeServiceAPI>(
myCallbacks,
binding,
new EndpointAddress(
"net.pipe://localhost/P2PSN.Node.Service.Pipe"));
myCallbacks is an instance of a class that deals with callbacks in the duplex pipe and INodeServiceAPI is the interface that describes my API.
Secondly you're right in the the factory events will not be fired. You can catch the events on the channel itself. I use the following code.
proxy = pipeFactory.CreateChannel();
if (proxy is IClientChannel)
{
(proxy as IClientChannel).Faulted += new EventHandler(this.proxy_Faulted);
}
Not pleasant, but something I picked up from StackOverflow elsewhere that works. You must include System.ServiceModel to pick up the IClientChannel interface.
HTH.
Related
I'd like to log some actions using OneWay events on WCF callback channel
I've got several methods like:
[OperationContract(IsOneWay = true)]
void LogSomething(String txt);
on my callback channel.
I'm iterating over every subscribed logger instance to send a message, but the whole application hangs up if one of them will not disconnect properly and won't close channel connection. Then even checking:
((ICommunicationObject)callback).State == CommunicationState.Opened
returns true so I can't even check if the channel is not broken.
ConcurrencyMode.Multiple and InstanceContextMode.PerCall are already set.
What is the best way to handle this? I'm using .NET 4.0
From my research I need to set AsyncPattern to true and change my code to:
[OperationContractAttribute(OneWay=true, AsyncPattern=true)]
IAsyncResult BeginLogSomething(String txt, AsyncCallback callback, object asyncState);
void EndLogSomething(IAsyncResult result);
but is there a better way of doing it? If it's possible I would like NOT to change the code on the client side - I still would like to use SynchronizationContext and handle messages one by one.
Do I need to create a separate thread that will send messages in a queue?
I have an application that uses WebSphere MQ to send data through WebSphere to a datacentre in the Cloud. Part of the functionality is that if the server-side subscriber detects that a message has not been received for 30 minutes, the thread is paused for 5 minutes, and the connection is removed. When it restarts, it reconnects.
In practice, I've found that disconnecting has not removed the subscription. When attempting to reconnect, I see this error:
"There may have been a problem creating the subscription due to it being used by another message consumer.
Make sure any message consumers using this subscription are closed before trying to create a new subscription under the same name. Please see the linked exception for more information."
This shows the message handler is still connected, meaning disconnect has failed. Disconnect code for the XmsClient object (part of the library, although one of my colleagues might have changed it) is:
public override void Disconnect()
{
_producer.Close();
_producer.Dispose();
_producer = null;
_consumer.MessageListener = null;
_consumer.Close();
_consumer.Dispose();
_consumer = null;
_sessionRead.Close();
_sessionRead.Dispose();
_sessionRead = null;
_sessionWrite.Close();
_sessionWrite.Dispose();
_sessionWrite = null;
_connection.Stop();
_connection.Close();
_connection.Dispose();
_connection = null;
//GC.Collect();
IsConnected = false;
}
Anyone have any thoughts as to why the connection still exists?
From the error description it looks like server subscriber is creating a durable subscription. Durable subscription continues to receive messages even when subscribing application is not running. To remove a durable subscription you must call Session.Unsubscribe(). Simply closing the consumer does not remove subscription.
If your intention was to close a subscriber without removing the subscription, then issue Connection.Stop() first followed by deregister message listener and then close consumer. Calling connection.Stop method stops message delivery.
I've tried to make an inter-server communication protocol with WCF. But for some reason, when a server disconnects, the Faulted neither the Closed events are called. This is really annoying but I haven't found a solution to it.
private static ServiceHost loginService;
static void Load() {
loginService = new ServiceHost(typeof(LoginService), new Uri[] { new Uri(Settings.Instance.LoginServiceURI) });
loginService.AddServiceEndpoint(typeof(ILoginService), ServiceHelpers.GetBinding(new Uri(Settings.Instance.LoginServiceURI)), Settings.Instance.LoginServiceURI);
loginService.Faulted += new EventHandler(loginService_Faulted);
loginService.Open();
}
static void loginService_Faulted(object sender, EventArgs e)
{
Log.WriteLine(LogLevel.Error, "LoginWCF Faulted. Restarting.");
loginService.Close();
Load();
}
The stupid thing is that only the functions inside the ILoginService interface will throw an exception when the connection died. I thought that TCP had a keep-alive of it's own?
As of now, I haven't find a way to determine whether the channel is faulted or closed until it makes a call and gets the exception. Only when it causes an exception, the status is set to Faulted, because this status is set in CommunicationObject.Fault method that is invoked when there is exception.
The Closed event will be fired out when CommunicationObject.Close is invoked.
Not all, but some wcf bindings (WsHttp & NetTcp for example) support reliable sessions, check http://msdn.microsoft.com/en-us/library/ms733136.aspx. It can be used to detect when the service is going down
I create a WCF SOAP server with an operation that takes some time to perform:
[ServiceContract]
public interface IMyService
{
[OperationContract]
string LongRunningOperation();
}
[ServiceBehavior(
ConcurrencyMode = ConcurrencyMode.Multiple,
UseSynchronizationContext = false,
InstanceContextMode = InstanceContextMode.Single)]
class MyService : IMyService
{
public string LongRunningOperation()
{
Thread.Sleep(20000);
return "Hey!";
}
}
class Program
{
static void Main(string[] args)
{
MyService instance = new MyService();
ServiceHost serviceHost = new ServiceHost(instance);
BasicHttpBinding binding = new BasicHttpBinding();
serviceHost.AddServiceEndpoint(typeof(IMyService), binding, "http://localhost:9080/MyService");
serviceHost.Open();
Console.WriteLine("Service running");
Thread.Sleep(10000);
serviceHost.Close();
Console.WriteLine("Service closed");
Thread.Sleep(30000);
Console.WriteLine("Exiting");
}
}
The ServiceHost is opened, and after 10 seconds I close it.
When calling serviceHost.Close(), all clients currently connected, waiting for LongRunningOperation to finish, are inmediately disconnected.
Is there a wait of closing the ServiceHost in a cleaner way? That is, I want to disable the service listeners, but also wait for all currently connected clients to finish (or specify a maximum timeout).
Im surprised calling ServiceHost.Close is not letting LongRunningOperation complete.
The whole architecture is setup to allow things time to gracefully shut down (e.g. the difference between Close and Abort transitions.). According to MSDN docs:
This method causes a
CommunicationObject to gracefully
transition from any state, other than
the Closed state, into the Closed
state. The Close method allows any
unfinished work to be completed before
returning.
Also there is a CloseTimeout on the ServiceHost for precisely this. Have you tried setting the CloseTimeout to be greater than 20 seconds? (According to Reflector the default CloseTimeout for ServiceHost is 10 seconds...)
In principle, I think something like the following should be possible, though I haven't implemented it to confirm all the details:
Implement a custom IOperationInvoker
wrapping the Dispatcher's normal
OperationInvoker (you'll want an IServiceBehavior to install the wrapped invoker when the service dispatcher runtime is built)
the custom invoker would mostly delegate to the real one, but would also provide
"gate-keeper" functionality to turn away
new requests (e.g. raise a some kind of exception) when the service host is about
to be shut down.
it would also keep track of operation invocations still in
progress and set an event when the last operation invocation finishes or times out.
the main hosting thread would then wait on the invoker's "all finished" event before calling serviceHost.Close().
What you are doing seems all wrong to me. The ServiceHost should never close abruptly. It is a service and should remain available. There is no real way to close gracefully without some participation from the client. When I say close gracefully, this also subjective from a clients perspective.
So I dont think I understand your requirements at all, however one way would be to implement a publish/subscribe pattern and when the host is ready to close, notify all subscribers of this event so that all connections could be closed by each respective client. You can read more about this here http://msdn.microsoft.com/en-us/magazine/cc163537.aspx
Again, this approach to hosting a service is not standard and thats why you are finding it hard to find a solution to this particular problem of yours. If you could elaborate on your use case/usage scenario, it would probably help to find a real solution.
You are describing client side functionality. Sounds like you should wrap the servicehost object and then have your proxy rejecting new requests when it "is closing". You don't close the real servicehost until all calls have been serviced.
You should also take a look at the asynch CTP. To put this kind of logic inside a consumer side "Task" object will be much easier with the upcoming TaskCompletionSource class.
Check this video from dnrtv out. It's not about wcf, but rather about the upcoming language and class support for asynchrony.
I have the following scenario:
My main Application (APP1) starts a Process (SERVER1). SERVER1 hosts a WCF service via named pipe. I want to connect to this service (from APP1), but sometimes it is not yet ready.
I create the ChannelFactory, open it and let it generate a client. If I now call a method on the generated Client I receive an excpetion whitch tells me that the Enpoint was not found:
var factory = new ChannelFactory<T>(new NetNamedPipeBinding(), new EndpointAddress("net.pipe//localhost/myservice");
factory.Open()
var Client = factory.CreateChannel();
Client.Foo();
If I wait a little bit before calling the service, everything is fine;
var Client = factory.CreateChannel();
Thread.Sleep(2000);
Client.Foo();
How can I ensure, that the Service is ready without having to wait a random amount of time?
If the general case is that you are just waiting for this other service to start up, then you may as well use the approach of having a "Ping" method on your interface that does nothing, and retrying until this starts responding.
We do a similar thing: we try and call a ping method in a loop at startup (1 second between retries), recording in our logs (but ultimately ignoring) any TargetInvocationException that occur trying to reach our service. Once we get the first proper response, we proceed onwards.
Naturally this only covers the startup warmup case - the service could go down after a successfull ping, or it we could get a TargetInvocationException for a reason other than "the service is not ready".
You could have the service signal an event [Edited-see note] once the service host is fully open and the Opened event of the channel listener has fired. The Application would wait on the event before using its proxy.
Note: Using a named event is easy because the .NET type EventWaitHandle gives you everything you need. Using an anonymous event is preferable but a bit more work, since the .NET event wrapper types don't give you an inheritable event handle. But it's still possible if you P/Invoke the Windows DuplicateHandle API yourself to get an inheritable handle, then pass the duplicated handle's value to the child process in its command line arguments.
If you're using .Net 4.0 you could use WS-Discovery to make the service announce its presence via Broadcast IP.
The service could also send a message to a queue (MSMQ binding) with a short lifespan, say a few seconds, which your client can monitor.
Have the service create a signal file, then use a FileSystemWatcher in the client to detect when it gets created.
Just while (!alive) try { alive = client.IsAlive(); } catch { ...reconnect here... } (in your service contract, you just have IsAlive() return true)
I have had the same issue and when using net.pipe*://localhost/serviceName*, I solved it by looking at the process of the self-hosted application.
the way i did that was with a utility class, here is the code.
public static class ServiceLocator
{
public static bool IsWcfStarted()
{
Process[] ProcessList = Process.GetProcesses();
return ProcessList.Any(a => a.ProcessName.StartsWith("MyApplication.Service.Host", StringComparison.Ordinal));
}
public static void StartWcfHost()
{
string path = System.IO.Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
var Process2 = new Process();
var Start2 = new ProcessStartInfo();
Start2.FileName = Path.Combine(path, "Service", "MyApplication.Service.Host.exe");
Process2.StartInfo = Start2;
Process2.Start();
}
}
now, my application isn't called MyApplication but you get my point...
now in my client Apps that use the host i have this call:
if (!ServiceLocator.IsWcfStarted())
{
WriteEventlog("First instance of WCF Client... starting WCF host.")
ServiceLocator.StartWcfHost();
int timeout=0;
while (!ServiceLocator.IsWcfStarted())
{
timeout++;
if(timeout> MAX_RETRY)
{
//show message that probably wcf host is not available, end the client
....
}
}
}
This solved 2 issues,
1. The code errors I had wend away because of the race condition, and 2
2. I know in a controlled manner if the Host crashed due to some issue or misconfiguration.
Hope it helps.
Walter
I attached an event handler to client.InnerChannel.faulted, then reduced the reliableSession to 20 seconds. Within the event handler I removed the existing handler then ran an async method to attempt to connect again and attached the event handler again. Seems to work.