The Background
I have a WCF client and service with a one-to-one relationship (i.e. one service host per client). I use a callback contract to pass messages from the service to the client.
[ServiceContract(CallbackContract = typeof(IMessageTarget)]
public interface IMessageService
{
[OperationContract]
void StartMessages();
}
public interface IMessageTarget
{
[OperationContract]
Task SendAsync(Message message);
[OperationContract(IsOneWay = true)]
void Complete();
[OperationContract(IsOneWay = true)]
void Fault(ExceptionDetail exception);
}
On the client, I set up a duplex connection to begin communication. MessageTarget.Completion completes when the Complete() or Fault(ExceptionDetail) methods are called.
IMessageTarget target = new MessageTarget();
var channelFactory = new DuplexChannelFactory<IMessageService>(target, new NetNamedPipeBinding());
IMessageService service = channelFactory.CreateChannel(new EndpointAddress(EndpointUri));
service.StartMessages();
await target.Completion;
The service sends messages to the client as soon as they become available. The client is able to delay further messages by making SendAsync() take a long time to return.
IMessageTarget client = OperationContext.Current.GetCallbackChannel<IMessageTarget>();
// later, whenever a message is created
await client.SendAsync(message);
// finally
client.Complete();
The Problem
I discovered that the client hangs if the service crashes. Unfortunately, WCF provides no reliable way to detect if the service is still up, other than calling a service method and catching CommunicationException. Therefore, I added a new operation
[OperationContract]
Task PingAsync();
so the client can monitor if the service goes down.
service.StartMessages();
while (!target.Completion.IsCompleted)
{
await service.PingAsync();
await Task.Delay(TimeSpan.FromSeconds(0.5));
}
await target.Completion;
The Question
Doesn't this defeat the purpose of using callbacks? I'm struggling to see how this "listen for callbacks while constantly pinging" strategy is better than simply polling for the latest messages (with no duplex required). Did I over-engineer this, or is there some other benefit to callbacks?
Most WCF related exceptions are derived from CommunicationsException, so what you did is good in that sense. As you noted, pinging / polling defeats the purpose of the callback implementation. Bottom line, you need to just call the service and be ready to deal with any exceptions that happen during the service invocation. There's really no reliable way to see if the service is available.
If you are not already doing so, you can also look at the IClientChannel events to monitor what happens with the connection.
client.InnerChannel.Closed += OnChannelClosed;
client.InnerChannel.Opening += OnChannelOpening;
client.InnerChannel.Opened += OnChannelOpened;
client.InnerChannel.Closing += OnChannelClosing;
client.InnerChannel.Faulted += OnChannelFaulted;
client.InnerChannel.UnknownMessageReceived += OnChannelUnknownMessageReceived;
Related
I have a publisher / subscriber pattern WCF Duplex ServiceHost that is hosted by a Windows Service. The Windows Service receives events from a separate process. OnEvent I would like to force my WCF Host to publish that data to all subscribed clients. Typically if a Client is calling this is straight forward. But when my Service Host needs to do this - I can't get my head around HOW to do that.
I have 2 questions:
1: I do not know how to create a Channel in WCFHost from my Windows Service so that it can use to publish to the Subscribers.
2: I read Creating WCF ChannelFactory so I do know I am creating a DuplexChannelFactory (2 per second ) which might be too much overhead.
Any help examples, hints are greatly appreciated. I am not a WCF expert and currently know more about it than I thought I should have to know in order to use it.
I had read on SO
Can I call a Method in a self hosted wcf host locally?
So then I have created a method inside my WCFHost like so:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession,
AutomaticSessionShutdown = false,
IncludeExceptionDetailInFaults = true)]
[CallbackBehavior(UseSynchronizationContext = false, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class ServerHost<TService> : ServiceHost where TService : class
{
public T GetDuplexClientChannel<T, Cback>(BindingType bindingType, EndpointAddress endPointAddress) where T : class
{
ServiceEndpoint sep = GetContractServiceEndPoint<T>(bindingType, endPointAddress);
lock (_syncRoot)
{
DuplexChannelFactory<T> factory = new DuplexChannelFactory<T>(typeof(Cback), sep);
return factory.CreateChannel(endPointAddress);
}
}
}
I get an error of course that there is no InstanceContext because I am constructing using typeof(Cback) ..
"This CreateChannel overload cannot be called on this instance of DuplexChannelFactory, as the DuplexChannelFactory was initialized with a Type and no valid InstanceContext was provided."
So I am not sure how I can go about performing this ?
And for those that say read the error : yes I read the error.
Now how to do that with an InstanceContext that does not exist as OperationContext.Current does not exist at this point as I am calling this method form my Hosting Process into my WCFHost.
So if I could have a nice example of how to do this - even if I must use the code example on the 2nd link (of course implementing the DuplexChannelFactory) I would greatly appreciate it.
EDIT
Basically the windows Service is doing some heavy work monitoring other services, about 2 times a second it then must publish that to "Subscribed" Clients via WCF.
I think you have got very confused about how everything is wired together and are mixing concepts from the client in with the service. You haven't provided much concrete information about your scenario to go on so I'm going to provide a small example and hopefully you will be able to apply the ideas to your problem.
[ServiceContract(CallbackContract=typeof(IMyServiceCallback))]
public interface IMyService
{
[OperationContract]
void Register();
}
public interface IMyServiceCallback
{
[OperationContract]
void ReceiveData(string data);
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class MyService : IMyService
{
static HashSet<IMyServiceCallback> s_allClients = new HashSet<IMyServiceCallback>();
static object s_lockobj = new object();
public void Register()
{
lock(s_lockobj)
{
_allClients.Add(OperationContext.Current.GetCallbackChannel<IMyServiceCallback>());
}
}
public static void SendDataToClients(string data)
{
HashSet<IMyServiceCallback> tempSet;
lock(s_lockobj)
{
tempSet = new HashSet<IMyServiceCallback>(_allClients);
}
foreach(IMyServiceCallback cb in tempSet)
{
try
{
cb.ReceiveData(data);
}
catch(Exception)
{
lock(s_lockobj)
{
_allClients.Remove(cb);
cb.Abort();
cb.Dispose();
}
}
}
}
}
In your OnEvent method, you would call something similar to this inside your event method.
MyService.SendDataToClients(mydata);
This uses static data to store the list of clients. If you wanted to do something like segment your clients for different endpoints, you would need to do something different. There is a potential out of order message and scaling problem with this code if your OnEvent method can be called again while the previous call hasn't completed. For example, if you receive 2 messages, the first being large and the second being small, you could potentially send the second smaller message to clients later in the HashSet iteration order before they have been sent the first message. Also this won't scaled to a large number of clients as you could block timing out on one client holding up messages being sent to other clients. You could use something similar to Task's to dispatch multiple message deliveries. If this needs to scale, I would suggest looking at Reactive Extensions for .Net
I have been reading and experimenting with WCF and trying to understand the workings in simple terms. So my questions are for verification and validation of what I believe to be correct but I need to be sure:
In a typical Publish-Subscribe Duplex service.
1: Service Contract - this is the communication path that clients must make to the service.
2: Callback contract - this is the communication methods back to the client.
3: Setting the IsOneWay = true property on a callback contract means the Client will not get anything back from the server.
4: setting IsOneWay = true on the ServiceContract means the server will not get anything back from the client.
5: void return methods still send a reply back, if IsOneWay=true, the reply is is ignored, if false error and soap info can be obtained. ^
for brevity I have looked at the following and then Some^ⁿ:
Understanding WCF
WCF issue with using IsOneWay attribute
Understanding WCF Client to Server
https://msdn.microsoft.com/en-us/library/system.servicemodel.operationcontractattribute.isoneway(v=vs.110).aspx
Take a look at this tutorial for WCF, and this MSDN Article on duplex services.
You're almost there with your definitions, I would define the above myself as:
Service Contract - The interface that defines the operations a web service exposes to clients.
Callback Contract - Similar to a service contract, but as you note, for the client side. This is defines how a web service can communicate to client as a separate call. (As opposed to simply returning data from the calls in the service contract). These are often used for returning values from long-running web service calls, or event signaling.
IsOneWay = true on the service contract - This specifies that the service operation is returns no value, and hence the client will simply "fire and forget". The call to the webservice will not block the client until it completes, but instead return immediately. For this reason, operations with IsOneWay = true can only return void.
IsOneWay = true on the callback contract - This is much the same as it is on the service contract. When the server calls the operation on the callback contract, it will return immediately and not block until the operation completes.
Void returns - If IsOneWay is not set to true, the call will still block until the operation completes, a SOAP message will still be returned, but with no data (unless you are passing back faults). If you wish to actually return values, you can either do so exactly as you would with normal methods, setting the return type of the operation i.e
[ServiceContract]
public interface IService
{
[OperationContract]
DateTime GetDateTime();
}
public class Service : IService
{
public DateTime GetDateTime()
{
return DateTime.Now;
}
}
Alternatively, you could create a duplex service, with a callback contract, and IsOneWay = true
[ServiceContract(CallbackContract = typeof(ICallbackService))]
public interface IService
{
[OperationContract(IsOneWay = true)]
void GetDateTime();
}
public interface ICallbackService
{
[OperationContract(IsOneWay = true)]
void GetDateTimeCompleted(DateTime dateTime);
}
public class Service : IService
{
public void GetDateTime()
{
// Do long action here.
...
Callback.GetDateTimeCompleted(DateTime.Now);
}
ICallbackService Callback
{
return OperationContext.Current.GetCallbackChannel<ICallbackService>();
}
}
Using this method:
The call to the webservice GetDateTime() operation would return immediately
The "Very long operation" would execute on the server
The GetDateTimeCompleted(DateTime dateTime) on the client would get triggered when the server completes.
Please note that the above duplex example is not complete, you'll need to ensure you're handling things like sessions correctly.
You're definitely on the right track. I'd recommend following the tutorials linked above, (along with any others you find) and experimenting. You'll soon get a good feel for what is possible.
I have a WCF service that implements long-polling. However, I see no way to have each service call spawn a new thread upon being called.
As it stands, the long-polled contract is waiting for an event to occur and is blocking any other contracts from being called.
What is the recommended way to have one contract run asynchronously from another contract in WCF?
I thought of keeping a static thread pool but I'm not quite sure if that solution scales.
Thanks!
In the context of your question, I assume long-polling is some kind of an operation which is periodically issuing an HTTP request to a 3rd party resource, until a desired response has been returned, or until timed out.
To implement it efficiently, you can use .NET 4.5, TAP pattern and async/await.
Example (untested):
// contract
[ServiceContract]
public interface IService
{
//task-based asynchronous pattern
[OperationContract]
Task<bool> DoLongPollingAsync(string url, int delay, int timeout);
}
// implementation
public class Service : IService
{
public async Task<bool> DoLongPollingAsync(
string url, int delay, int timeout)
{
// handle timeout via CancellationTokenSource
using (var cts = new CancellationTokenSource(timeout))
using (var httpClient = new System.Net.Http.HttpClient())
using (cts.Token.Register(() => httpClient.CancelPendingRequests()))
{
try
{
while (true)
{
// do the polling iteration
var data = await httpClient.GetStringAsync(url).ConfigureAwait(false);
if (data == "END POLLING") // should we end polling?
return true;
// pause before the next polling iteration
await Task.Delay(delay, cts.Token);
}
}
catch (OperationCanceledException)
{
// is cancelled due to timeout?
if (!cts.IsCancellationRequested)
throw;
}
// timed out
throw new TimeoutException();
}
}
}
This scales well, because most of the time the DoLongPolling is in the pending state, asynchronously awaiting the result of HttpClient.GetStringAsync or Task.Delay calls. Which means it doesn't block a thread from ThreadPool, so the WCF service can serve more DoLongPolling requests concurrently. Check "There Is No Thread" by Stephen Cleary for more details on how this works behind the scene.
On the client side, you can call your WCF service asynchronously, too. Tick "Allow generation of asynchronous operations" when you create the WCF service proxy and select "Generate task-based operations".
If you need to target .NET 4.0, you can find some alternative options in "Asynchronous Operations in WCF" by Jaliya Udagedara.
I’m writing a service that has a call that is relatively long running. The client needs to be able to make successive requests that run in parallel to each other and for some reason my service will not execute them concurrently unless the calls are executed from separate clients. I'm trying to figure out what configuration setting(s) I'm missing.
I’m using the netTcpBinding. My throttling configuration is:
<serviceThrottling maxConcurrentInstances="10" maxConcurrentCalls="10" maxConcurrentSessions="10"/>
The service contract:
[ServiceContract(CallbackContract=typeof(ICustomerServiceCallback))]
public interface ICustomerService
{
[OperationContract(IsOneWay = true)]
void PrintCustomerHistory(string[] accountNumbers,
string destinationPath);
}
[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerCall)]
public class CustomerService : ICustomerService
{
public void PrintCustomerHistory(string[] accountNumbers,
string destinationPath)
{
//Do Stuff..
}
}
In the client, I’m making two successive asynchronous calls:
openProxy();
//call 1)
proxy.PrintCustomerHistory(customerListOne, #"c:\DestinationOne\");
//call 2)
proxy.PrintCustomerHistory(customerListTwo, #"c:\DestinationTwo\");
On the service, the second operation begins only after the first one ends. However, if I execute both calls from separate clients, they both execute concurrently by the service.
What am I missing? I had assumed that by marking my service class as “PerCall” that call 1 and call 2 each would receive their own InstanceContext and therefore execute concurrently on separate threads.
You need to make the client calls asynchronous. If you're using VS 2012, you can enable the Task based asynchronous calls in the service reference, then call via:
var task1 = proxy.PrintCustomerHistoryAsync(customerListOne, #"c:\DestinationOne\");
var task2 = proxy.PrintCustomerHistoryAsync(customerListTwo, #"c:\DestinationTwo\");
// The two tasks are running, if you need to wait until they're done:
await Task.WhenAll(task1, task2);
I am having some trouble implementing the right patterns for a work project and I don't want to precede until I am satisfied with the right design strategy.
The project is based around Genesys Computer Telephony Integration (CTI) Platform. Essentially, utilizing a SDK provided by Genesys, a single client subscribes to a number of Genesys services (or TServers) running remotely. The client then registers a whole heap of Directory Numbers (DN's) associated to a particular TServer and waits for call events. When an event occurs, it is captured by the client and stored in a database. A number of other operations are executed, which is irrelevant at this stage. A lot of the communication work is handled by the Genesys ProtocolManager object, so a single event handler captures event data across all clients, which in turn is handled by a EventBrokerService. Here is a simple code to illustrate the connection process, registration of a single DN and the event function:
EventBrokerService eventBrokerService;
using (var client = new TServerProtocol(
new Endpoint(
new Uri("tcp://tserver01:11234"))))
{
client.Open();
eventBrokerService = BrokerServiceFactory.CreateEventBroker(client);
eventBrokerService.Activate();
eventBrokerService.Register(this.OnEvent);
RequestRegisterAddress requestRegisterAddress =
RequestRegisterAddress.Create("977845873",
RegisterMode.ModeMonitor,
ControlMode.RegisterDefault,
AddressType.DN);
IMessage response = client.Request(requestRegisterAddress);
}
and then we listen for events (there are many different events):
private void OnEvent(IMessage response)
{
switch (response.Id)
{
case EventACK.MessageId:
//do something
break;
case EventLinkConnected.MessageId:
var ev = response as EventLinkConnected;
//Insert event into DB and perform some other operations...
break;
}
}
The Genesys Platform, comes with another component called a Genesys Configuration server. The config server holds all of the TServer details, including the DN information and a whole bunch of other "objects". It is really just a fancy DBMS. The difference is, you can also subscribe to the config server and register for CRUD events (i.e. CreateEvent, UpdateEvent etc...). Without illustrating the code, the concept is similar to the one above. (i.e. You can register to a number of different Configuration Servers and listen for CRUD events).
For the most part, I have covered the above well and I am satisfied with the implementation so far. What I am trying to achieve is as follows:
I am attempting to implement a distributed system. In a nutshell, the system will consist of 2 components. Monitoring Services and Dispatcher Service components (they will all be Windows Services)
Monitoring Service Component
The "Monitoring Service(s)" connect to 1 or many T Servers to monitor for call events
The monitoring service will ALSO subscribe to a dispatcher service
Dispatcher Service Component
The "Dispatcher Service" connects to 1 or more Configuration Servers and waits for CRUD events.
Once an event occurs (i.e. a new DN was added on the config server), the dispatcher captures the creation event, and notifies all monitoring service subscribers. Subsequently, the dispatcher will also update a local database, so the DN information is preserved for redundancy (in case dispatcher can not connect to a Configuration Server).
The monitoring subscriber, to whom the newly created DN belongs (distinguished by a unique DBID and TServerID identifiers) will accept the DN, and register it for listening events (similarly illustrated in the first code snippet). The monitoring subscriber who does not possess the required TServer connection will drop the received request, naturally.
The Dispatcher can also receive newly added TServers, but this time around, it will make the decision which monitoring service it want's to utilize in order for that monitoring service to make ANOTHER connection. This will be determined by factors such as the number of current sessions running on a monitoring service or the how much memory a single service is chewing up at the time.
I have come up with some basic concepts and here is some of the code to illustrate what I have done thus far:
The communication method I have chosen is WCF with NetTcpBinding, so for the simple part, I have exposed an interface:
[ServiceContract(Namespace = "urn:Netwatch",
SessionMode = SessionMode.Required,
CallbackContract = typeof(IDisMonServiceCallback))]
public interface IDisMonService
{
[OperationContract]
bool Subscribe(string uid);
[OperationContract(IsOneWay = true)]
void Unsubscribe(string uid);
}
[ServiceContract(Namespace="urn:Netwatch")]
public interface IDisMonServiceCallback
{
[OperationContract]
bool DNRegistered(int tServerId, string dnEntry);
}
and on the dispatcher, I have implemented it:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple)]
public class DisMonService : IDisMonService
{
private ConcurrentDictionary<string, IDisMonServiceCallback> subscribers = new ConcurrentDictionary<string, IDisMonServiceCallback>();
public IDisMonServiceCallback this[string uid]
{
get
{
IDisMonServiceCallback callback;
if (!subscribers.TryGetValue(uid, out callback))
return null;
return callback;
}
}
public List<IDisMonServiceCallback> GetAllServiceCallbacks()
{
return new List<IDisMonServiceCallback>(subscribers.Values);
}
public bool Subscribe(string uid)
{
IDisMonServiceCallback callback = GlobalHelper.Callback<IDisMonServiceCallback>();
if (!subscribers.ContainsKey(uid))
if (!subscribers.TryAdd(uid, callback))
return false;
return true;
}
public void Unsubscribe(string uid)
{
IDisMonServiceCallback callback;
if (subscribers.ContainsKey(uid))
if (!subscribers.TryRemove(uid, out callback))
return;
return;
}
}
From the code above, it is obvious that each subscribing monitoring service has a unique identifier, that way the right service callback context is retrieved (in case I decide to do some other funky operations).
This is where my dilemma essentially begins. To cut the long story short, my question(s) are as follow:
How do I deal with DisMonService class when attempting to pass on messages to all subscribers from within the Dispatcher service. i.e. new DN has been added, let us call the DisMonService class and notify all subscribers.
What would be the most optimal pattern to implement in dealing with updates to all subscribers from within DisMonServie
At the moment my dummy client connects to the dispatcher, and it registers itself. Moving forward, what is the best way to access the DisMonService class.
I hope I am not confusing anybody at what I am trying to ask. I guess what I am really trying to find is best way to implement the above system, any suggestions and such. Some code samples and snippets would really be helpful.
This is my first post here so I apologise to anybody if I haven't explained myself to the forum's standards.