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.
Related
I have a WCF service with a service contract (let's call it IHelloWorldService). Inside the contract implementation (don't ask why...) the constructor creates a System.Windows.Control instance.
Well, with this configuration, the service does not work at all. It seems to start perfectly but when I make a request to the service (even when I ask for the metadata in the browser) the service does not respond.
The funny thing is that if I create the System.Windows.Form in another thread (just inside a task for example), the service works perfectly. The service is a singleton, so, it is not creating new instances per call. The control is created once.
A quick pseudocode of my example would be:
The service host creation:
ServiceHost serviceHost = new ServiceHost(new HelloWorldService(), "callback");
The service:
class HelloWorldService : IHelloWorldService
{
public HelloWorldService() : this(new System.Windows.Control())
{}
public HelloWorldService(System.Windows.Control control){}
}
The previous example does not work. However, if I create the HelloWorldService in another thread, for example, in a Task, it works perfectly.
The quick crude pseudocode example:
ServiceHost serviceHost = new ServiceHost(CreateHelloWorldService(), "callback");
HelloWorldService CreateHelloWorldService()
{
HelloWorldService service;
Task newTask = new Task{ service = new HelloWorldService() };
newTask.Run().Wait();
return service;
}
This example works well. My guess is that maybe the main thread suffers some kind of change when the it creates the control and then the WCF stack is not able to process the request. This problem is the simplification of a bigger one and, although I'm able to solve it with the thread thing, I would like/need to understand what is going on and why is not working properly in order to explain the big issue in the real environment program.
Apologies for my English. In case something is not clear, please, let me know and I will try to clarify it.
Thanks in advance!
A service is not meant to create a Windows forms component. Even if you succeed in achieving the creation somehow, there will be issues interacting with it later on.
Windows form controls are meant to be handled in a background thread, as the thread creating it must be same as the thread interacting with it. All interactions will need to use InvokeRequired and then do BeginInvoke in the pre-TPL time. With TPL, using interaction should be in Task bounded operation
In your code snippet, since the actual thread being used gets disposed after the method call, the form will become a zombie since there will be no reference to control it.. (an example of possible future problems with handling forms in service)
Code wise, as long as we are using Invoke/BeginInvoke, there's NO issue, as illustrated in sample below
public delegate void myDel();
public class Service1 : MyService
{
private WindowsFormsApp1.Form1 form;
public Service1()
{
this.form = new WindowsFormsApp1.Form1();
}
private void SetFormData()
{
this.form.Size = new System.Drawing.Size(100, 500);
}
public void DoSomeFun()
{
this.form.Show();
myDel del = new myDel(SetFormData);
this.form.Invoke(del);
}
}
We are using the following method in a Stateful Service on Service-Fabric. The service has partitions. Sometimes we get a FabricNotReadableException from this peace of code.
public async Task HandleEvent(EventHandlerMessage message)
{
var queue = await StateManager.GetOrAddAsync<IReliableQueue<EventHandlerMessage>>(EventHandlerServiceConstants.EventHandlerQueueName);
using(ITransaction tx = StateManager.CreateTransaction())
{
await queue.EnqueueAsync(tx, message);
await tx.CommitAsync();
}
}
Does that mean that the partition is down and is being moved? Of that we hit a secondary partition? Because there is also a FabricNotPrimaryException that is being raised in some cases.
I have seen the MSDN link (https://msdn.microsoft.com/en-us/library/azure/system.fabric.fabricnotreadableexception.aspx). But what does
Represents an exception that is thrown when a partition cannot accept reads.
mean? What happened that a partition cannot accept a read?
Under the covers Service Fabric has several states that can impact whether a given replica can safely serve reads and writes. They are:
Granted (you can think of this as normal operation)
Not Primary
No Write Quorum (again mainly impacting writes)
Reconfiguration Pending
FabricNotPrimaryException which you mention can be thrown whenever a write is attempted on a replica which is not currently the Primary, and maps to the NotPrimary state.
FabricNotReadableException maps to the other states (you don't really need to worry or differentiate between them), and can happen in a variety of cases. One example is if the replica you are trying to perform the read on is a "Standby" replica (a replica which was down and which has been recovered, but there are already enough active replicas in the replica set). Another example is if the replica is a Primary but is being closed (say due to an upgrade or because it reported fault), or if it is currently undergoing a reconfiguration (say for example that another replica is being added). All of these conditions will result in the replica not being able to satisfy writes for a small amount of time due to certain safety checks and atomic changes that Service Fabric needs to handle under the hood.
You can consider FabricNotReadableException retriable. If you see it, just try the call again and eventually it will resolve into either NotPrimary or Granted. If you get FabricNotPrimary exception, generally this should be thrown back to the client (or the client in some way notified) that it needs to re-resolve in order to find the current Primary (the default communication stacks that Service Fabric ships take care of watching for non-retriable exceptions and re-resolving on your behalf).
There are two current known issues with FabricNotReadableException.
FabricNotReadableException should have two variants. The first should be explicitly retriable (FabricTransientNotReadableException) and the second should be FabricNotReadableException. The first version (Transient) is the most common and is probably what you are running into, certainly what you would run into in the majority of cases. The second (non-transient) would be returned in the case where you end up talking to a Standby replica. Talking to a standby won't happen with the out of the box transports and retry logic, but if you have your own it is possible to run into it.
The other issue is that today the FabricNotReadableException should be deriving from FabricTransientException, making it easier to determine what the correct behavior is.
Posted as an answer (to asnider's comment - Mar 16 at 17:42) because it was too long for comments! :)
I am also stuck in this catch 22. My svc starts and immediately receives messages. I want to encapsulate the service startup in OpenAsync and set up some ReliableDictionary values, then start receiving message. However, at this point the Fabric is not Readable and I need to split this "startup" between OpenAsync and RunAsync :(
RunAsync in my service and OpenAsync in my client also seem to have different Cancellation tokens, so I need to work around how to deal with this too. It just all feels a bit messy. I have a number of ideas on how to tidy this up in my code but has anyone come up with an elegant solution?
It would be nice if ICommunicationClient had a RunAsync interface that was called when the Fabric becomes ready/readable and cancelled when the Fabric shuts down the replica - this would seriously simplify my life. :)
I was running into the same problem. My listener was starting up before the main thread of the service. I queued the list of listeners needing to be started, and then activated them all early on in the main thread. As a result, all messages coming in were able to be handled and placed into the appropriate reliable storage. My simple solution (this is a service bus listener):
public Task<string> OpenAsync (CancellationToken cancellationToken)
{
string uri;
Start ();
uri = "<your endpoint here>";
return Task.FromResult (uri);
}
public static object lockOperations = new object ();
public static bool operationsStarted = false;
public static List<ClientAuthorizationBusCommunicationListener> pendingStarts = new List<ClientAuthorizationBusCommunicationListener> ();
public static void StartOperations ()
{
lock (lockOperations)
{
if (!operationsStarted)
{
foreach (ClientAuthorizationBusCommunicationListener listener in pendingStarts)
{
listener.DoStart ();
}
operationsStarted = true;
}
}
}
private static void QueueStart (ClientAuthorizationBusCommunicationListener listener)
{
lock (lockOperations)
{
if (operationsStarted)
{
listener.DoStart ();
}
else
{
pendingStarts.Add (listener);
}
}
}
private void Start ()
{
QueueStart (this);
}
private void DoStart ()
{
ServiceBus.WatchStatusChanges (HandleStatusMessage,
this.clientId,
out this.subscription);
}
========================
In the main thread, you call the function to start listener operations:
protected override async Task RunAsync (CancellationToken cancellationToken)
{
ClientAuthorizationBusCommunicationListener.StartOperations ();
...
This problem likely manifested itself here as the bus in question already had messages and started firing the second the listener was created. Trying to access anything in state manager was throwing the exception you were asking about.
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?
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.
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.