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.
Related
In my application I have a server which provides a REST-Api where my UI can communicate with.
Now I have to start a long running Process on the Server but I want the client not to wait for the response of the server.
I know I could just fire and forget the Post-Call and not await the response but I need to know at the client that the Process on the server was startet correctly.
So I thought about the following:
[HttpPost]
[Route("startscan")]
public HttpResponseMessage StartScan()
{
Task.Factory.StartNew( () =>
{
//Do long running things here
});
return Request.CreateResponse(HttpStatusCode.OK);
}
So my question now is: Will the task I started be executed to it's end? Background of the question is, that as far as I knwo a controller-instance is created for each call to it. So will the instance of the controller be terminated when the requested finished or will the Task I started run to it's end. The task can take up to 10 minutes or longer sometimes.
A simple approach would be to just use an asynchronous method without awaiting the Task result like so:
[HttpPost]
[Route("startscan")]
public async HttpResponseMessage StartScan()
{
DoLongRunningThings();
return Request.CreateResponse(HttpStatusCode.OK);
}
public async Task DoLongRunningThings()
{
// Do Stuff here
}
However, if you Processing is more complex and requires more resilience you should look into how to use background jobs. Here is a good collection of what you can use: https://stackoverflow.com/a/36098218/16154479
Will the task I started be executed to it's end?
Probably yes, but also possibly no. Sometimes it won't be executed completely. This is the result of misusing an ASP.NET app (which handles HTTP requests) as a background service (which runs outside of a request context).
There is a best practice that avoids possibly-partial execution: a distributed architecture (as I describe on my blog). The idea is that your controller enqueues a message describing the work to be done into a durable queue and then returns. Then there's a separate background service that reads from the queue and does the actual work.
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;
Given:
A legacy non-async API method on an ASP.NET/WCF web service
New async internal library
New async Web API controller that should be used going forward
A "storage provider" object that only has an async interface. Its tests pass when run asynchronously, and when run synchronously outside a request context.
The option "go async all the way" is not on the table, since it would break backward compatibility.
public class Impl {
// This works fine when used asynchronously
public Task<Guid> SaveThingAsync(Thing thingToSave) {
return await _storageProvider.saveAsync(thingToSave);
}
public Guid SaveThing(Thing thingToSave) {
// "Obviously", this code creates a deadlock when called
// from within the request context
// return SaveThingAsync(thingToSave).Result
// Not so obviously, this also creates a deadlock
// return SaveThingAsync(thingToSave)
// .ConfigureAwait(false)
// .GetAwaiter()
// .GetResult()
// This was deadlocking, but magically stopped
// return Task.Run(
// async () => await SaveThingAsync(thingToSave)
// .ConfigureAwait(false)
// ).Result;
// This one works
var saveTask = Task.Run(async () =>
await SaveThingAsync(thingToSave)));
var result = saveTask.ConfigureAwait(false).GetAwaiter().GetResult();
return result;
}
Why?
Task.Run steps "outside" the request context - it just runs on the thread pool context. So, it won't deadlock because SaveThingAsync doesn't resume on the request context.
On a side note, the ConfigureAwait(false) is meaningless there, since there is no await to configure.
On another side note, "async all the way" should still be an option. WebAPI and WCF clients don't care whether the implementation they're calling is synchronous or asynchronous. Changing a WCF method implemented synchronously to a WCF method implemented asynchronously is invisible to client code.
I have a REST WCF Service that allows for the upload and download of information from a webserver. Whilst this works well for very basic actions such as submitting information to the database such as a status update, it doesn’t work so well when we make calls to other web services and IO resources which by their nature are longer running tasks.
It’s not acceptable for the web service to keep the calling client hanging while it does its work, if for example the client has completed its post of data and we have some number crunching in the background to perform.
My intitial thoughts are that while a client has completed the request we can return an HTTP status of 202 / Accepted and the client can get called back (by other means) once were finished.
Ignoring here for the moment various IO issues in writing to the same path at the same time Async would the below code be a good approach with the use of the WCF service in IIS. I have fears that the running methods could be terminated at any time given an app pool recycle.
public class AsyncService : IAsyncService
{
public void WriteMessageToDisk(string value)
{
Thread.Sleep(120000); // Introduce some arbitrary sleep to keep the client waiting.
try
{
using (var writer = new StreamWriter(AppSettings.StorePath, true))
{
writer.WriteLine("{0} - {1}", DateTime.Now.ToString("dd/MM/yyyy hh:mm:ss"), value);
}
}
catch (IOException ex)
{
WriteMessageToDisk(ex.ToString());
}
}
public void WriteMessageToDiskAsync(string value)
{
var task = new Task(() => WriteMessageToDisk(String.Format("Message from Async runner: {0}", value)));
task.Start();
}
}
Would this be the best approach to doing some longer running work Asynchronously? I'm also thinking about the possibility of another application to hand these long running requests to so that it doesn't get messy with IIS.
You don't create Task instances directly.
Instead you use the factory and helper method Task.Run
You should also return a Task or Task(TResult) from the asynchronous method.
For instance:
public Task WriteMessageToDiskAsync(string value)
{
return Task.Run(() => WriteMessageToDisk(String.Format("Message from Async runner: {0}", value)));
}
I am writing a WCF webservice that includes a method that accepts an array of objects and inserts them into the database. This could take a long time, so I can't just expect the client to wait.
My colleague thinks that I don't need to do anything, that it's the client's job to call my service asynchronously. I just write a normal method. This doesn't sound right to me, although I hope it's true because looking at WCF async tutorials and SO questions has just confused me so far.
Is he correct? If not, how do I actually write the method in a way that would allow the client to call the method asynchronously or otherwise avoid hanging?
If he is correct (as appears to be the case), then what is the point of defining an asynchronous method ([OperationContract (AsyncPattern=true)], Begin, End, etc.). Is it a way explicitly handling asynchronous calls, or allowing interactivity, or what?
It should fall on the client's side. They are the ones that have to prevent their app/UI from hanging.
Have your client call your method asynchronously. If they are using a service reference, all methods/events are generated automatically.
myWcfClient.myMethodCompleted
+= new EventHandler<myMethodCompletedEventArgs>(myCallBack);
myWcfClient.myMethodAsync(args);
public void myCallback(object sender, myMethodCompletedEventArgs e)
{
var myResult = e.Result;
}
If your client doesn't care what happens with the service call, you want a simple fire and forget operation and you can do this.
The AsyncPattern property tells the runtime that your operations implement the .NET Framework asynchronous method design pattern. See here. If you want your client application to know what has happened with your service call then you can use this pattern. There are other ways to get the results though.
This is only on the client side, I've skipped the old event driven async bleh pattern and replaced it with the async-await pattern. Not waiting for webmethod calls async, and blocking the UI... doesn't even belong in this century ;)
If you are using .net 4.5+ you are getting the async-await pattern for free (Unless wp8, where you still have to wrap it). The async methods should already be avaliable through the service. I recommend the AsyncBridge if you are using old frameworks, which allows you to use the async-await pattern for cases like this. The alternative is to stick to the old event driven async nightmare. The examples below is only possible if you are using C#5.0 or never.
Ensure to start in a new thread from a non async method.
Task.Factory.StartNew(client.DoSomethingAsync("blabla") ).ContinueWith(...);
The last part is run after your method has completed, check for exceptions to completion code etc.
Or in some async method
public async Task<string> DoSomethingAsync(String text) {
// Exception handling etc
return await client.DoSomethingAsync(text);
}
wrapping APM to async-await pattern:
public class ServiceWrapper : IServiceWrapper
{
readonly YourServiceClient client;
public ServiceWrapper(YourServiceClient client)
{
this.client = client;
}
public async Task<string> DoSomethingAsync(string someParameter)
{
return await Task<string>.Factory.FromAsync(client.BeginDoSomeStuff, client.EndDoSomeStuff, someParameter, new object());
}
}
EDIT
Opening and closing connections in a wrapped service. (I don't have my devbox avaliable right now but this should work).
public class ServiceWrapper : IServiceWrapper
{
EndpointAddress address;
public ServiceWrapper(EndpointAddress clientAddress)
{
address = clientAddress;
}
public async Task<string> DoSomethingAsync(string someParameter)
{
// handle exceptions etc here, can be done some cleaner..
var client = new YourServiceClient();
client.Endpoint.Address = address.Address; // can skip this..
await client.OpenAsync()
var res = await Task<string>.Factory.FromAsync(client.BeginDoSomeStuff, client.EndDoSomeStuff, someParameter, new object());
await client.CloseAsync();
return res;
}
}
One last thing I'm not sure how you generate your proxy, if you are using vs make sure to hook of the checkbox allowing async methods when configuring the service. If you are using svcutil add the appropriate flags.
Async-await pattern
Old event driven async pattern
Hope it helps,
Cheers,
Stian