Is it possible to take advantage of async/await on the ServiceStack's server methods? Googling brings up a fork of ServiceStack which seems to do the trick but it hasn't been updated for few months and there is no info if that code has been merged into the current official version (v3.9.32).
I built a simple service using the async / await pattern and it doesn't behave correctly but I wonder if this is just a configuration issue. Here's the code:
public class PodcastService : Service
{
public async Task<object> Get(Podcast request)
{
var client = new WebClient();
var data = await client.DownloadStringTaskAsync("http://www.google.fi");
return new PodcastResponse() { Date = DateTime.Now };
}
}
And here's the JSON the method returns:
{"Result":{"__type":"WebApplication2.PodcastResponse, WebApplication2","Date":"2012-12-10T17:18:32.1653985+02:00"},"Id":1,"Status":"RanToCompletion","IsCanceled":false,"IsCompleted":true,"CreationOptions":0,"IsFaulted":false}
For comparison, here's the same method without any async/await:
public class PodcastService : Service
{
public object Get(Podcast request)
{
return new PodcastResponse() { Date = DateTime.Now };
}
}
And here's the result:
{"Date":"2012-12-10T17:20:23.0245677+02:00"}
Any ideas? Is the fork still the way to go or is the async/await already implemented in ServiceStack but requires some configuration?
You can check the project status here. The async branch is still in the "Doing" phase.
The first result looks like a serialized Task object, so they definitely haven't added async service support yet.
UPDATE
We can forget about the async/await fork as ServiceStack goes commercial and gets a breaking rewrite for v4. The latest available version, v3.9.62 is a feature-freeze version as development switches to v4.
Related
I'm using IdentityModel.OidcClient's SystemBrowser to request a token in a console app via browser. What I don't understand is, why there is an await Task.Delay(500) in the Dispose method of the LoopbackHttpListener. I see why you would do the Dispose via Task.Run, but not the delay.
Line of code in GitHub: https://github.com/IdentityModel/IdentityModel.OidcClient/blob/46d7b25cd71eb0be5f3c203c2525b1f357e408db/clients/ConsoleClientWithBrowser/SystemBrowser.cs#L131
public LoopbackHttpListener(int port, string path = null)
{
path = path ?? String.Empty;
if (path.StartsWith("/")) path = path.Substring(1);
_url = $"http://127.0.0.1:{port}/{path}";
_host = new WebHostBuilder()
.UseKestrel()
.UseUrls(_url)
.Configure(Configure)
.Build();
_host.Start();
}
public void Dispose()
{
Task.Run(async () =>
{
await Task.Delay(500);
_host.Dispose();
});
}
I don't know why that code is used but I can guess. This could only be answered by the maintainers, assuming they still remember why this was written after 6 years.
Clicking on Blame shows that this specific line is 6 years old, tagged as hacky dispose of Kestrel. Recent commits have messages like Update test client or Update sample client. The csproj file targets .NET 6 directly. If this was library code it would target .NET Standard 2.1 or .NET Core 3.1 as well, which are still supported.
My guess is that this code is used to ensure any pending requests handled by the listener complete before it closes. Since this started as test code, some hacks were used and never fixed, because they didn't directly impact the test's or sample's core behavior.
I don't remember what WebHost or the Generic Host looked back in 2017 (and I'm too lazy to find out). The interfaces have changed a lot between .NET Standard 2.0, .NET Core 3.1 and .NET 6.
In .NET 6 though we can use IAsyncDisposable to allow asynchronous disposal. Inside DisposeAsync we can call WebHost.StopAsync to gracefully shut down the host before disposing it:
public class LoopbackHttpListener : IAsyncDisposable
{
...
public async ValueTask DisposeAsync()
{
await _host.StopAsync(TimeSpan.FromMilliseconds(500));
_host.Dispose();
}
It's possible the LoopbackHttpListener can be replaced by Minimal APIs too.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", (HttpRequest request) =>{
_source.TrySetResult(request.QueryString);
return "<h1>You can now return to the application.</h1>";
});
_task=app.RunAsync(_url);
The test code returns a fixed response on any HTTP GET call and exposes the request's query string through the _source TaskCompletionSource.
I have the following endpoint:
[HttpPost("Submit")]
public String post()
{
_ = _service.SubmitMetric("test", MetricType.Count, 60, 1);
return "done";
}
And the service implementation:
public Task<HttpResponseMessage> SubmitMetric(<params>)
{
// build payload
using (var httpClient = new HttpClient())
{
return httpClient.PostAsync(<params>);
}
}
When I run the code and call the endpoint, the HTTP POST is not triggered. However, if I change my code to:
public async Task<HttpResponseMessage> SubmitMetric(<params>)
{
// build payload
using (var httpClient = new HttpClient())
{
return await httpClient.PostAsync(<params>);
}
}
the POST is submitted as expected. Why is that happening, and what can I do if I don't really care about the HTTP response? I just want to submit it and continue my flow. Shouldn't I be able to use it without awaiting the result? For example:
public void SubmitMetric(<params>)
{
// build payload
using (var httpClient = new HttpClient())
{
httpClient.PostAsync(<params>);
}
}
There are two problems with this code. If either was fixed, there would be no problem:
The HttpClient is used incorrectly. An HttpClient object is thread-safe and meant to be reused, not disposed. Disposing it like this leaks sockets and can result in application crashes or worse, instability. An HttpClient resolves the URL's Host to a socket and caches that socket. The OS also caches opened sockets because opening them is expensive. They're kept alive for a while even if an application closes them because some packets may still be in transit
By not awaiting PostAsync execution exits the using block and the HttpClient instance is disposed before the request had a chance to even start.
In any case, making a POST doesn't take long so there's no need to make the method fire-and-forget. Besides, few applications are OK with losing metrics, especially when things go wrong. That's when metrics are most useful.
Which is why ASP.NET Core 6 adds built-in support for OpenTelemetry tracing and metrics. More on that at the end, but the supporting packages can be used in ASP.NET Framework as well. You may be able to replace your current service with a built-in one.
Use await - not enough
One way to fix this is to use await but that doesn't solve the HttpClient usage problem.
public async Task<HttpResponseMessage> SubmitMetric(<params>)
{
// build payload
using (var httpClient = new HttpClient())
{
return await httpClient.PostAsync(<params>);
}
}
At the very least the HttpClient should be stored in a field. Once that's done though, there's no longer any reason to await, provided the service itself is still around :
HttpClient httpClient = new HttpClient();
public Task<HttpResponseMessage> SubmitMetric(<params>)
{
return httpClient.PostAsync(<params>);
}
Long lived services
Which brings us to keeping the service around. In ASP.NET and ASP.NET Core each request is served by a separate thread, in a new instance of the Controller class. The request itself is used as a GC scope so anything created during a request is disposed once this concludes, including the HttpClient instance.
To keep the Metrics service around we need to either register it as Singleton in ASP.NET Core's DI, make it a BackgroundService or ensure it's a singleton in ASP.NET Framework. We could make the field static, but that leads to the next issue.
Proper HttpClient usage
HttpClient can still cause problems if used as a singleton. The HttpClient caches sockets to specific machines. If that machine goes away, the HttpClient will still try to communicate with it causing errors. This can happen easily when the remote services uses a load balancer or fails over to a new server. To fix this, the HttpClient instance or rather the sockets, need to be recycled periodically.
That's the job of the HttpClientFactory. This class caches and recycles SocketClientHandler instances, the classes that do the actual work in an HttpClient. These are recycled periodically, eg every 10 minutes. When asked for a new HttpClient instance, it creates a new instance wrapping one of the already available handlers.
When you use services.AddHttpClient in ASP.NET Core you're actually configuring an HttpClientFactory. When you add an HttpClient dependency in a controller, the instance will be created by the configured HttpClientFactory.
This means that the following action would work properly :
HttpClient _client;
public MyController(HttpClient client)
{
_client=client;
}
[HttpPost("Submit")]
public String post()
{
await _client.PostAsync(<params>);
return "done";
}
A scoped service with an HttpClient dependency would also work:
MyService _service;
public MyController(MyService service)
{
_service=service;
}
HttpPost("Submit")]
public String post()
{
await _service.SubmitMetric("test", MetricType.Count, 60, 1);
return "done";
}
where MyService is :
class MyService
{
HttpClient _client;
public MyService(HttpClient client)
{
_client=client;
}
public Task<HttpResponseMessage> SubmitMetric(<params>)
{
// build payload
return httpClient.PostAsync(<params>);
}
}
In this case there's no real need to await inside SubmitMetric, that's taken care of by the action.
Using the built-in OpenTelemetry tracing and metrics
ASP.NET Core 6, the upcoming Long-Term-Support version, adds native support for the OpenTelemetry standard for logging, tracing and metrics. This allows using a standard API to push metrics to a lot of different observability applications like Prometheus, Jaeger, Zipking, Elastic and Splunk.
Instead of rolling one's own metrics infrastructure it's better to use the standard API. OpenTelemetry for .NET supports this in ASP.NET Framework 4.6 and later. ASP.NET Core 5 and later are instrumented to publish metrics and tracing to OpenTelemetry providers through the built-in System.Diagnostics namespace and the Activity class.
In fact, Controller is already instrumented so you could get rid of the metrics service, adding any Tags and Baggage to the request's current activity:
[HttpPost("Submit")]
public String post()
{
Activity.Current?.AddTag("test");
...
return "done";
}
Metrics were added in ASP.NET Core 6 Preview 5:
Meter meter = new Meter("my.library.meter.name", "v1.0");
Counter<int> _counter;
public MyController(...)
{
_counter = meter.CreateCounter<int>("Requests");
}
[HttpPost("Submit")]
public String post()
{
counter.Add(60, KeyValuePair.Create<string, object>("request", "test"));
return "done";
}
Don't do it. Await for it even though you discard the result.
Fire and forget is an anti pattern and the context that you are performing the request can be invalidated/killed before the request could be completed, terminating the connection. Just await it, and don't do anything with the result.
httpClient will be disposed while the POST operation is running, probably resulting in killing the socket. If you use await, the object will remain inside the using clause while the operation is running, and it won't be terminated before it finishes.
Note that in your current implementation, you're creating a new connection on each API request, which might eventually lead to socket exhaustion. A better approach would be injecting IHttpClientFactory, which manage the lifetime of network connections for you, and reuses connections from the pool:
public class MyService
{
private readonly IHttpClientFactory _httpClient;
public MyService(IHttpClientFactory httpClient)
{
_httpClient = httpClient;
}
public async Task<HttpResponseMessage> SubmitMetric(/*<params>*/)
{
var httpClient = _httpClient.CreateClient();
return await httpClient.PostAsync(/*<params>*/);
}
}
Note: You need to add services.AddHttpClient() in ConfigureServices in your Startup.cs to enable injection.
I have some C# code that Im actually porting to VB.Net. Now I noticed ConfigureAwait(false) is used everywhere. A requirement is that the code be dependant on .Net 4.0
As you might know... ConfigureAwait(false) appears in .Net 4.5. How would I convert the following code to be compliant with .Net 4.0?
Is there a generic solution as ConfigureAwait occurs everywhere in the code
public async Task<ListResponse> GetResponseAsync(ListRequest request = null)
{
request = request ?? new ListRequest
{
Limit = _limit
};
using (var client = CreateMailClient("lists"))
{
var response = await client.GetAsync(request.ToQueryString()).ConfigureAwait(false);
await response.EnsureSuccessMailChimpAsync().ConfigureAwait(false);
var listResponse = await response.Content.ReadAsAsync<ListResponse>().ConfigureAwait(false);
return listResponse;
}
}
ANSWER:
Reference Microsoft.BCL.Async
This is what you are looking for to be precise.
If you are using ConfigureAwait(), you probably really care about it and want it to actually work. Unfortunately, because async methods might actually complete synchronously, the call to ConfigureAwait() might not affect anything. That means you have to put it on the next async call too, and so on, until it is on every single method in your library.
Reference Microsoft Developers
Alternatives to Configure await
Sample code
I'm pretty new to working with HTTP stuff so I'm rather confused as to what would be the best approach to request data from a HTTP address every few seconds or so.
The API I'm using has - at least to my knowledge no webhook support. So I imagine the way to update my data would be a rather crude way of doing so.
I want this to happen in the background so the GUI does not freeze and become unresponsive. So I know I (probably) need to fiddle with threads.
Best results I've had has been with a async/await Timer. I'm not entirely sure how to work with this and the only way for me to get it to work is to throw an exception after it has elapsed. If I don't - it says that not all nodes return a value and I can't even use return which really, really confuses me.
How should I be doing this?
If it's of any use, I'm working on creating my own RCON tool for a game which has all kinds of server data available via a HTTP API - but documentation for this API is very lackluster.
if you go to .net core you can see my previous answer on: Start multiple background threads inside Self Hosted ASP.NET Core Microservice
for .net framework you have to do a little more yourself. But still very do-able!
in your global.asax you have (or should I say: should) have your dependency injection. Something like:
protected void Application_Start()
{
Bootstrap();
//and do something more
}
private static void Bootstrap()
{
var container = new Container();
container.Register(() => new HttpClient());
container.RegisterSingleton<IApiCaller, ApiCaller>();
container.RegisterInitializer<IApiCaller>(ApiCaller=> apicaller.StartCallingAsync());
// Suppress warnings for HttpClient
var registration = container.GetRegistration(typeof(HttpClient)).Registration;
registration.SuppressDiagnosticWarning(DiagnosticType.DisposableTransientComponent, "Dispose is being called by code.");
registration.SuppressDiagnosticWarning(DiagnosticType.LifestyleMismatch, "Every HttpCient is unique for each dependency.");
container.Verify();
GlobalConfiguration.Configuration.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);
}
In this case, I let SimpleInjector start my background thread to do a lot of work.
In the apicaller you can do your httpcalls.
something like:
public async Task StartCallingAsync(CancellationToken cancellationToken = (default)CancellationToken)
{
while(true)
{
var response = await _httpClient.GetAsync(url);
if (response.IsSuccessStatusCode)
{
//do work
}
await Task.Delay(10000, cancellationToken);
}
}
for the GetAsync there are extension methods that can cast it directly to your object.
can you work with this?
I'm still getting to know the various async features of .NET 4.5, and I've run into something interesting. Given the following in my MVC controller, I get different results when executing (1) and (2)
public ActionResult Index() {
var stuff = SomeExpensiveFunction();
return View(stuff);
}
private byte[] SomeExpensiveFunction() {
string url = "http://some-url.../";
// (1)
var wc = new WebClient();
return wc.DownloadDataTaskAsync(url).Result;
// (2)
var hc = new HttpClient();
return hc.GetAsync(url).Result.Content.ReadAsByteArrayAsync().Result;
}
On the surface, they seem the same - both the WebClient.DownloadDataTaskAsync and HttpClient.GetAsync are async methods which return a Task. The WebClient version returns Task<byte[]> while the HttpClient version returns Task<HttpResponseMessage> which I have to dig the bytes out of, but I'm calling .Result either way, which I would expect to complete prior to leaving the function.
With (1), I get a yellow-screen with An asynchronous operation cannot be started at this time.... With (2), everything works fine.
I can change the whole stack and use an async on the controller method itself and the SomeExpensiveFunction, and everything works fine. But I'm trying to figure out if there's something fundamentally wrong with (1) or with WebClient in general when working with MVC. Any thoughts?
EDIT: I know in this example I can use the synchronous versions of those calls since I'm not really doing anything asynchronously - this is just an example based on a larger codebase.
You've fallen foul of ASP.NET's SynchronizationContext. To make the WebClient example work, you should make the entire controller asynchronous. Try this:
public async Task<ActionResult> IndexAsync() {
string url = "http://some-url.../";
using (var wc = new WebClient())
return View(await wc.DownloadDataTaskAsync(url));
}
See http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4 for a briefing on asynchronous controllers, and http://www.tugberkugurlu.com/archive/asynchronousnet-client-libraries-for-your-http-api-and-awareness-of-async-await-s-bad-effects for an explanation of the deadlocking effect of the async/await pattern.
I'm not 100% sure but this could be due to improper use of Async methods. Perhaps you are seeing this behavior because you are not expected to use Async methods in synchronous fashion by calling Result.