Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 months ago.
The community reviewed whether to reopen this question 9 months ago and left it closed:
Original close reason(s) were not resolved
Improve this question
Our web application is running in .NET Framework 4.0. The UI calls the controller methods through Ajax calls.
We need to consume the REST service from our vendor. I am evaluating the best way to call the REST service in .NET 4.0. The REST service requires a basic authentication scheme and it can return data in both XML and JSON.
There isn't any requirement for uploading/downloading huge data and I don't see anything in future. I took a look at few open source code projects for REST consumption and didn't find any value in those to justify additional dependency in the project. I started to evaluate WebClient and HttpClient. I downloaded HttpClient for .NET 4.0 from NuGet.
I searched for differences between WebClient and HttpClient and this site mentioned that single HttpClient can handle concurrent calls and it can reuse resolved DNS, cookie configuration and authentication. I am yet to see practical values that we may gain due to the differences.
I did a quick performance test to find how WebClient (synchronous calls), HttpClient (synchronous and asynchronous) perform. And here are the results:
I am using the same HttpClient instance for all the requests (minimum - maximum).
WebClient sync: 8 ms - 167 ms
HttpClient sync: 3 ms - 7228 ms
HttpClient async: 985 - 10405 ms
Using a new HttpClient for each request (minimum - maximum):
WebClient sync: 4 ms - 297 ms
HttpClient sync: 3 ms - 7953 ms
HttpClient async: 1027 - 10834 ms
Code
public class AHNData
{
public int i;
public string str;
}
public class Program
{
public static HttpClient httpClient = new HttpClient();
private static readonly string _url = "http://localhost:9000/api/values/";
public static void Main(string[] args)
{
#region "Trace"
Trace.Listeners.Clear();
TextWriterTraceListener twtl = new TextWriterTraceListener(
"C:\\Temp\\REST_Test.txt");
twtl.Name = "TextLogger";
twtl.TraceOutputOptions = TraceOptions.ThreadId | TraceOptions.DateTime;
ConsoleTraceListener ctl = new ConsoleTraceListener(false);
ctl.TraceOutputOptions = TraceOptions.DateTime;
Trace.Listeners.Add(twtl);
Trace.Listeners.Add(ctl);
Trace.AutoFlush = true;
#endregion
int batchSize = 1000;
ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = batchSize;
ServicePointManager.DefaultConnectionLimit = 1000000;
Parallel.For(0, batchSize, parallelOptions,
j =>
{
Stopwatch sw1 = Stopwatch.StartNew();
GetDataFromHttpClientAsync<List<AHNData>>(sw1);
});
Parallel.For(0, batchSize, parallelOptions,
j =>
{
Stopwatch sw1 = Stopwatch.StartNew();
GetDataFromHttpClientSync<List<AHNData>>(sw1);
});
Parallel.For(0, batchSize, parallelOptions,
j =>
{
using (WebClient client = new WebClient())
{
Stopwatch sw = Stopwatch.StartNew();
byte[] arr = client.DownloadData(_url);
sw.Stop();
Trace.WriteLine("WebClient Sync " + sw.ElapsedMilliseconds);
}
});
Console.Read();
}
public static T GetDataFromWebClient<T>()
{
using (var webClient = new WebClient())
{
webClient.BaseAddress = _url;
return JsonConvert.DeserializeObject<T>(
webClient.DownloadString(_url));
}
}
public static void GetDataFromHttpClientSync<T>(Stopwatch sw)
{
HttpClient httpClient = new HttpClient();
var response = httpClient.GetAsync(_url).Result;
var obj = JsonConvert.DeserializeObject<T>(
response.Content.ReadAsStringAsync().Result);
sw.Stop();
Trace.WriteLine("HttpClient Sync " + sw.ElapsedMilliseconds);
}
public static void GetDataFromHttpClientAsync<T>(Stopwatch sw)
{
HttpClient httpClient = new HttpClient();
var response = httpClient.GetAsync(_url).ContinueWith(
(a) => {
JsonConvert.DeserializeObject<T>(
a.Result.Content.ReadAsStringAsync().Result);
sw.Stop();
Trace.WriteLine("HttpClient Async " + sw.ElapsedMilliseconds);
}, TaskContinuationOptions.None);
}
}
}
My Questions
The REST calls return in 3-4 seconds which is acceptable. Calls to REST
service are initiated in the controller methods which gets invoked from
Ajax calls. To begin with, the calls runs in a different thread and doesn't block the UI. So, can I just stick with synchronous calls?
The above code was run in my localbox. In a production setup, DNS and proxy
lookup will be involved. Is there an advantage of using HttpClient over WebClient?
Is HttpClient concurrency better than WebClient? From the test results, I see WebClient synchronous calls perform better.
Will HttpClient be a better design choice if we upgrade to .NET 4.5? Performance is the key design factor.
HttpClient is the newer of the APIs and it has the benefits of
has a good asynchronous programming model
being worked on by Henrik F Nielson who is basically one of the inventors of HTTP, and he designed the API so it is easy for you to follow the HTTP standard, e.g. generating standards-compliant headers
is in the .NET framework 4.5, so it has some guaranteed level of support for the forseeable future
also has the xcopyable/portable-framework version of the library if you want to use it on other platforms - .NET 4.0, Windows Phone, etc.
If you are writing a web service which is making REST calls to other web services, you should want to be using an asynchronous programming model for all your REST calls, so that you don't hit thread starvation. You probably also want to use the newest C# compiler which has async/await support.
Note: It isn't more performant, AFAIK. It's probably somewhat similarly performant if you create a fair test.
HttpClientFactory
It's important to evaluate the different ways you can create an HttpClient, and part of that is understanding HttpClientFactory.
https://learn.microsoft.com/en-us/dotnet/architecture/microservices/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests
This is not a direct answer I know - but you're better off starting here than ending up with new HttpClient(...) everywhere.
When it comes to ASP.NET apps I still prefer WebClient over HttpClient because:
The modern implementation comes with async/awaitable task-based methods
Has smaller memory footprint and 2-5 times faster (other answers already mention that)
It's suggested to "reuse a single
instance of HttpClient for the lifetime of your application". But
ASP.NET has no "lifetime of application", only lifetime of a request. The current guidance for ASP.NET 5 is to use HttpClientFactory, but it can only be used via dependency injection. Some people want a simpler solution.
Most importantly, if you're using one singleton instance of HttpClient through the lifetime of the app like MS suggests - it has known issues. For example the DNS caching issue - HttpClient simply ignores the TTL and caches DNS "forever". There are workarounds, however. If you'd like to learn more about the issues and confusion with HttpClient just read this comment at Microsoft GitHub.
Firstly, I am not an authority on WebClient vs. HttpClient, specifically. Secondly, from your comments above, it seems to suggest that WebClient is synchronous only whereas HttpClient is both.
I did a quick performance test to find how WebClient (synchronous calls), HttpClient (synchronous and asynchronous) perform. And here are the results.
I see that as a huge difference when thinking for future, i.e., long running processes, responsive GUI, etc. (add to the benefit you suggest by .NET framework 4.5 - which in my actual experience is hugely faster on IIS).
Perhaps you could think about the problem in a different way. WebClient and HttpClient are essentially different implementations of the same thing. What I recommend is implementing the Dependency Injection pattern with an IoC Container throughout your application. You should construct a client interface with a higher level of abstraction than the low level HTTP transfer. You can write concrete classes that use both WebClient and HttpClient, and then use the IoC container to inject the implementation via config.
What this would allow you to do would be to switch between HttpClient and WebClient easily so that you are able to objectively test in the production environment.
So questions like:
Will HttpClient be a better design choice if we upgrade to .Net 4.5?
Can actually be objectively answered by switching between the two client implementations using the IoC container. Here is an example interface that you might depend on that doesn't include any details about HttpClient or WebClient.
/// <summary>
/// Dependency Injection abstraction for rest clients.
/// </summary>
public interface IClient
{
/// <summary>
/// Adapter for serialization/deserialization of http body data
/// </summary>
ISerializationAdapter SerializationAdapter { get; }
/// <summary>
/// Sends a strongly typed request to the server and waits for a strongly typed response
/// </summary>
/// <typeparam name="TResponseBody">The expected type of the response body</typeparam>
/// <typeparam name="TRequestBody">The type of the request body if specified</typeparam>
/// <param name="request">The request that will be translated to a http request</param>
/// <returns></returns>
Task<Response<TResponseBody>> SendAsync<TResponseBody, TRequestBody>(Request<TRequestBody> request);
/// <summary>
/// Default headers to be sent with http requests
/// </summary>
IHeadersCollection DefaultRequestHeaders { get; }
/// <summary>
/// Default timeout for http requests
/// </summary>
TimeSpan Timeout { get; set; }
/// <summary>
/// Base Uri for the client. Any resources specified on requests will be relative to this.
/// </summary>
Uri BaseUri { get; set; }
/// <summary>
/// Name of the client
/// </summary>
string Name { get; }
}
public class Request<TRequestBody>
{
#region Public Properties
public IHeadersCollection Headers { get; }
public Uri Resource { get; set; }
public HttpRequestMethod HttpRequestMethod { get; set; }
public TRequestBody Body { get; set; }
public CancellationToken CancellationToken { get; set; }
public string CustomHttpRequestMethod { get; set; }
#endregion
public Request(Uri resource,
TRequestBody body,
IHeadersCollection headers,
HttpRequestMethod httpRequestMethod,
IClient client,
CancellationToken cancellationToken)
{
Body = body;
Headers = headers;
Resource = resource;
HttpRequestMethod = httpRequestMethod;
CancellationToken = cancellationToken;
if (Headers == null) Headers = new RequestHeadersCollection();
var defaultRequestHeaders = client?.DefaultRequestHeaders;
if (defaultRequestHeaders == null) return;
foreach (var kvp in defaultRequestHeaders)
{
Headers.Add(kvp);
}
}
}
public abstract class Response<TResponseBody> : Response
{
#region Public Properties
public virtual TResponseBody Body { get; }
#endregion
#region Constructors
/// <summary>
/// Only used for mocking or other inheritance
/// </summary>
protected Response() : base()
{
}
protected Response(
IHeadersCollection headersCollection,
int statusCode,
HttpRequestMethod httpRequestMethod,
byte[] responseData,
TResponseBody body,
Uri requestUri
) : base(
headersCollection,
statusCode,
httpRequestMethod,
responseData,
requestUri)
{
Body = body;
}
public static implicit operator TResponseBody(Response<TResponseBody> readResult)
{
return readResult.Body;
}
#endregion
}
public abstract class Response
{
#region Fields
private readonly byte[] _responseData;
#endregion
#region Public Properties
public virtual int StatusCode { get; }
public virtual IHeadersCollection Headers { get; }
public virtual HttpRequestMethod HttpRequestMethod { get; }
public abstract bool IsSuccess { get; }
public virtual Uri RequestUri { get; }
#endregion
#region Constructor
/// <summary>
/// Only used for mocking or other inheritance
/// </summary>
protected Response()
{
}
protected Response
(
IHeadersCollection headersCollection,
int statusCode,
HttpRequestMethod httpRequestMethod,
byte[] responseData,
Uri requestUri
)
{
StatusCode = statusCode;
Headers = headersCollection;
HttpRequestMethod = httpRequestMethod;
RequestUri = requestUri;
_responseData = responseData;
}
#endregion
#region Public Methods
public virtual byte[] GetResponseData()
{
return _responseData;
}
#endregion
}
Full code
HttpClient Implementation
You can use Task.Run to make WebClient run asynchronously in its implementation.
Dependency Injection, when done well helps alleviate the problem of having to make low level decisions upfront. Ultimately, the only way to know the true answer is try both in a live environment and see which one works the best. It's quite possible that WebClient may work better for some customers, and HttpClient may work better for others. This is why abstraction is important. It means that code can quickly be swapped in, or changed with configuration without changing the fundamental design of the app.
BTW: there are numerous other reasons that you should use an abstraction instead of directly calling one of these low-level APIs. One huge one being unit-testability.
I have benchmarked between HttpClient, WebClient, and HttpWebResponse, and then called the REST Web API.
And the results:
Call REST Web API Benchmark
---------------------Stage 1 ---- 10 Request
{00:00:17.2232544} ====>HttpClinet
{00:00:04.3108986} ====>WebRequest
{00:00:04.5436889} ====>WebClient
---------------------Stage 1 ---- 10 Request--Small Size
{00:00:17.2232544}====>HttpClinet
{00:00:04.3108986}====>WebRequest
{00:00:04.5436889}====>WebClient
---------------------Stage 3 ---- 10 sync Request--Small Size
{00:00:15.3047502}====>HttpClinet
{00:00:03.5505249}====>WebRequest
{00:00:04.0761359}====>WebClient
---------------------Stage 4 ---- 100 sync Request--Small Size
{00:03:23.6268086}====>HttpClinet
{00:00:47.1406632}====>WebRequest
{00:01:01.2319499}====>WebClient
---------------------Stage 5 ---- 10 sync Request--Max Size
{00:00:58.1804677}====>HttpClinet
{00:00:58.0710444}====>WebRequest
{00:00:38.4170938}====>WebClient
---------------------Stage 6 ---- 10 sync Request--Max Size
{00:01:04.9964278}====>HttpClinet
{00:00:59.1429764}====>WebRequest
{00:00:32.0584836}====>WebClient
WebClient Is faster
var stopWatch = new Stopwatch();
stopWatch.Start();
for (var i = 0; i < 10; ++i)
{
CallGetHttpClient();
CallPostHttpClient();
}
stopWatch.Stop();
var httpClientValue = stopWatch.Elapsed;
stopWatch = new Stopwatch();
stopWatch.Start();
for (var i = 0; i < 10; ++i)
{
CallGetWebRequest();
CallPostWebRequest();
}
stopWatch.Stop();
var webRequesttValue = stopWatch.Elapsed;
stopWatch = new Stopwatch();
stopWatch.Start();
for (var i = 0; i < 10; ++i)
{
CallGetWebClient();
CallPostWebClient();
}
stopWatch.Stop();
var webClientValue = stopWatch.Elapsed;
//-------------------------Functions
private void CallPostHttpClient()
{
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("https://localhost:44354/api/test/");
var responseTask = httpClient.PostAsync("PostJson", null);
responseTask.Wait();
var result = responseTask.Result;
var readTask = result.Content.ReadAsStringAsync().Result;
}
private void CallGetHttpClient()
{
var httpClient = new HttpClient();
httpClient.BaseAddress = new Uri("https://localhost:44354/api/test/");
var responseTask = httpClient.GetAsync("getjson");
responseTask.Wait();
var result = responseTask.Result;
var readTask = result.Content.ReadAsStringAsync().Result;
}
private string CallGetWebRequest()
{
var request = (HttpWebRequest)WebRequest.Create("https://localhost:44354/api/test/getjson");
request.Method = "GET";
request.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
var content = string.Empty;
using (var response = (HttpWebResponse)request.GetResponse())
{
using (var stream = response.GetResponseStream())
{
using (var sr = new StreamReader(stream))
{
content = sr.ReadToEnd();
}
}
}
return content;
}
private string CallPostWebRequest()
{
var apiUrl = "https://localhost:44354/api/test/PostJson";
HttpWebRequest httpRequest = (HttpWebRequest)WebRequest.Create(new Uri(apiUrl));
httpRequest.ContentType = "application/json";
httpRequest.Method = "POST";
httpRequest.ContentLength = 0;
using (var httpResponse = (HttpWebResponse)httpRequest.GetResponse())
{
using (Stream stream = httpResponse.GetResponseStream())
{
var json = new StreamReader(stream).ReadToEnd();
return json;
}
}
return "";
}
private string CallGetWebClient()
{
string apiUrl = "https://localhost:44354/api/test/getjson";
var client = new WebClient();
client.Headers["Content-type"] = "application/json";
client.Encoding = Encoding.UTF8;
var json = client.DownloadString(apiUrl);
return json;
}
private string CallPostWebClient()
{
string apiUrl = "https://localhost:44354/api/test/PostJson";
var client = new WebClient();
client.Headers["Content-type"] = "application/json";
client.Encoding = Encoding.UTF8;
var json = client.UploadString(apiUrl, "");
return json;
}
Related
I want to be able to mirror all gRPC calls for some services to another set of services with same interface, in C#.
I think about writing extension method for GrpcClientFactory.CreateClient, to return class which would do two calls in parallel. Result from mirror is not needed, so I would return result from first call.
Is it best aproach for the task, or it is possible to make it better?
Pretty much what you described, in your case you can make use of IHttpClientFactory for creating http clients
public class GrpResponse
{
public string client { get; set; }
public HttpResponseMessage response { get; set; }
}
private async void ExecuteGrpRequest()
{
var payLoad = new HttpRequestMessage();
var grpTasks = new List<Task<GrpResponse>>
{
SendAsyc("real", payLoad),
SendAsyc("mock", payLoad)
};
var responses = await Task.WhenAll(grpTasks);
}
private async Task<GrpResponse> SendAsyc(string client, HttpRequestMessage message )
{
// IHttpClientFactory is _httpClientFacotry; injected from ctor
var httpClient = _httpClientFacotry.CreateClient(client);
return new GrpResponse
{
client = client,
response = await httpClient.SendAsync(new HttpRequestMessage())
};
}
We have a problem where we need a request/response pattern while using the TPL Dataflow library. Our problem is we have a .NET core API that calls a dependent service. The dependent service limits concurrent requests. Our API does not limit concurrent requests; therefore, we could receive thousands of requests at a time. In this case, the dependent service would reject requests after reaching its limit. Therefore, we implemented a BufferBlock<T> and a TransformBlock<TIn, TOut>. The performance is solid and works great. We tested our API front end with 1000 users issuing 100 requests/sec with 0 problems. The buffer block buffers requests and the transform block executes in parallel our desired amount of requests. The dependency service receives our requests and responds. We return that response in the transform block action and all is well. Our problem is the buffer block and transform block are disconnected which means requests/responses are not in sync. We are experiencing an issue where a request will receive the response of another requester (please see the code below).
Specific to the code below, our problem lies in the GetContent method. That method is called from a service layer in our API which ultimately is called from our controller. The code below and the service layer are singletons. The SendAsync to the buffer is disconnected from the transform block ReceiveAsync so that arbitrary responses are returned and not necessarily the request that was issued.
So, our question is: Is there a way using the dataflow blocks to correlate request/responses? The ultimate goal is a request comes in to our API, gets issued to the dependent service, and is returned to the client. The code for our data flow implementation is below.
public class HttpClientWrapper : IHttpClientManager
{
private readonly IConfiguration _configuration;
private readonly ITokenService _tokenService;
private HttpClient _client;
private BufferBlock<string> _bufferBlock;
private TransformBlock<string, JObject> _actionBlock;
public HttpClientWrapper(IConfiguration configuration, ITokenService tokenService)
{
_configuration = configuration;
_tokenService = tokenService;
_bufferBlock = new BufferBlock<string>();
var executionDataFlowBlockOptions = new ExecutionDataflowBlockOptions
{
MaxDegreeOfParallelism = 10
};
var dataFlowLinkOptions = new DataflowLinkOptions
{
PropagateCompletion = true
};
_actionBlock = new TransformBlock<string, JObject>(t => ProcessRequest(t),
executionDataFlowBlockOptions);
_bufferBlock.LinkTo(_actionBlock, dataFlowLinkOptions);
}
public void Connect()
{
_client = new HttpClient();
_client.DefaultRequestHeaders.Add("x-ms-client-application-name",
"ourappname");
}
public async Task<JObject> GetContent(string request)
{
await _bufferBlock.SendAsync(request);
var result = await _actionBlock.ReceiveAsync();
return result;
}
private async Task<JObject> ProcessRequest(string request)
{
if (_client == null)
{
Connect();
}
try
{
var accessToken = await _tokenService.GetTokenAsync(_configuration);
var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post,
new Uri($"https://{_configuration.Uri}"));
// add the headers
httpRequestMessage.Headers.Add("Authorization", $"Bearer {accessToken}");
// add the request body
httpRequestMessage.Content = new StringContent(request, Encoding.UTF8,
"application/json");
var postRequest = await _client.SendAsync(httpRequestMessage);
var response = await postRequest.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<JObject>(response);
}
catch (Exception ex)
{
// log error
return new JObject();
}
}
}
What you have to do is tag each incoming item with an id so that you can correlate the data input to the result output. Here's an example of how to do that:
namespace ConcurrentFlows.DataflowJobs {
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
/// <summary>
/// A generic interface defining that:
/// for a specified input type => an awaitable result is produced.
/// </summary>
/// <typeparam name="TInput">The type of data to process.</typeparam>
/// <typeparam name="TOutput">The type of data the consumer expects back.</typeparam>
public interface IJobManager<TInput, TOutput> {
Task<TOutput> SubmitRequest(TInput data);
}
/// <summary>
/// A TPL-Dataflow based job manager.
/// </summary>
/// <typeparam name="TInput">The type of data to process.</typeparam>
/// <typeparam name="TOutput">The type of data the consumer expects back.</typeparam>
public class DataflowJobManager<TInput, TOutput> : IJobManager<TInput, TOutput> {
/// <summary>
/// It is anticipated that jobHandler is an injected
/// singleton instance of a Dataflow based 'calculator', though this implementation
/// does not depend on it being a singleton.
/// </summary>
/// <param name="jobHandler">A singleton Dataflow block through which all jobs are processed.</param>
public DataflowJobManager(IPropagatorBlock<KeyValuePair<Guid, TInput>, KeyValuePair<Guid, TOutput>> jobHandler) {
if (jobHandler == null) { throw new ArgumentException("Argument cannot be null.", "jobHandler"); }
this.JobHandler = JobHandler;
if (!alreadyLinked) {
JobHandler.LinkTo(ResultHandler, new DataflowLinkOptions() { PropagateCompletion = true });
alreadyLinked = true;
}
}
private static bool alreadyLinked = false;
/// <summary>
/// Submits the request to the JobHandler and asynchronously awaits the result.
/// </summary>
/// <param name="data">The input data to be processd.</param>
/// <returns></returns>
public async Task<TOutput> SubmitRequest(TInput data) {
var taggedData = TagInputData(data);
var job = CreateJob(taggedData);
Jobs.TryAdd(job.Key, job.Value);
await JobHandler.SendAsync(taggedData);
return await job.Value.Task;
}
private static ConcurrentDictionary<Guid, TaskCompletionSource<TOutput>> Jobs {
get;
} = new ConcurrentDictionary<Guid, TaskCompletionSource<TOutput>>();
private static ExecutionDataflowBlockOptions Options {
get;
} = GetResultHandlerOptions();
private static ITargetBlock<KeyValuePair<Guid, TOutput>> ResultHandler {
get;
} = CreateReplyHandler(Options);
private IPropagatorBlock<KeyValuePair<Guid, TInput>, KeyValuePair<Guid, TOutput>> JobHandler {
get;
}
private KeyValuePair<Guid, TInput> TagInputData(TInput data) {
var id = Guid.NewGuid();
return new KeyValuePair<Guid, TInput>(id, data);
}
private KeyValuePair<Guid, TaskCompletionSource<TOutput>> CreateJob(KeyValuePair<Guid, TInput> taggedData) {
var id = taggedData.Key;
var jobCompletionSource = new TaskCompletionSource<TOutput>();
return new KeyValuePair<Guid, TaskCompletionSource<TOutput>>(id, jobCompletionSource);
}
private static ExecutionDataflowBlockOptions GetResultHandlerOptions() {
return new ExecutionDataflowBlockOptions() {
MaxDegreeOfParallelism = Environment.ProcessorCount,
BoundedCapacity = 1000
};
}
private static ITargetBlock<KeyValuePair<Guid, TOutput>> CreateReplyHandler(ExecutionDataflowBlockOptions options) {
return new ActionBlock<KeyValuePair<Guid, TOutput>>((result) => {
RecieveOutput(result);
}, options);
}
private static void RecieveOutput(KeyValuePair<Guid, TOutput> result) {
var jobId = result.Key;
TaskCompletionSource<TOutput> jobCompletionSource;
if (!Jobs.TryRemove(jobId, out jobCompletionSource)) {
throw new InvalidOperationException($"The jobId: {jobId} was not found.");
}
var resultValue = result.Value;
jobCompletionSource.SetResult(resultValue);
}
}
}
Also see this answer for reference.
Doing a simple throttling is not a particularly enticing use case for the TPL Dataflow library, and using instead a SemaphoreSlim seems simpler and more attractive. But if you wanted more features, like for example enforcing a minimum duration for each request, or having a way for waiting all the pending requests to complete, then the TPL Dataflow could have something to offer that the SemaphoreSlim couldn't. The basic idea is to avoid passing naked input values to the block, and trying later to associate them with the produced results. It is much safer to create tasks immediately upon request, send the tasks to an ActionBlock<Task>, and let the block activate and await asynchronously these tasks using its specified MaxDegreeOfParallelism. This way the input value and its result will be unambiguously tied together forever.
public class ThrottledExecution<T>
{
private readonly ActionBlock<Task<Task<T>>> _actionBlock;
private readonly CancellationToken _cancellationToken;
public ThrottledExecution(int concurrencyLevel, int minDurationMilliseconds = 0,
CancellationToken cancellationToken = default)
{
if (minDurationMilliseconds < 0) throw new ArgumentOutOfRangeException();
_actionBlock = new ActionBlock<Task<Task<T>>>(async task =>
{
try
{
var delay = Task.Delay(minDurationMilliseconds, cancellationToken);
task.RunSynchronously();
await task.Unwrap().ConfigureAwait(false);
await delay.ConfigureAwait(false);
}
catch { } // Ignore exceptions (errors are propagated through the task)
}, new ExecutionDataflowBlockOptions()
{
MaxDegreeOfParallelism = concurrencyLevel,
CancellationToken = cancellationToken,
});
_cancellationToken = cancellationToken;
}
public Task<T> Run(Func<Task<T>> function)
{
// Create a cold task (the function will be invoked later)
var task = new Task<Task<T>>(function, _cancellationToken);
var accepted = _actionBlock.Post(task);
if (!accepted)
{
_cancellationToken.ThrowIfCancellationRequested();
throw new InvalidOperationException(
"The component has been marked as complete.");
}
return task.Unwrap();
}
public void Complete() => _actionBlock.Complete();
public Task Completion => _actionBlock.Completion;
}
Usage example:
private ThrottledExecution<JObject> throttledExecution
= new ThrottledExecution<JObject>(concurrencyLevel: 10);
public Task<JObject> GetContent(string request)
{
return throttledExecution.Run(() => ProcessRequest(request));
}
I appreciate the answer provided by JSteward. His is a perfectly acceptable approach; however, I ended up doing this by using a SemaphoreSlim. SemaphoreSlim provides two things that allow this to be a powerful solution. First, it provides a constructor overload where you can send in a count. This count refers to the number of concurrent items that will be able to get past the semaphore waiting mechanism. The waiting mechanism is provided by a method called WaitAsync. With the below approach where the Worker class is as a Singleton, concurrent requests come in, are limited to 10 at a time executing the http request, and the responses are all returned to the correct requests. So an implementation might look like the following:
public class Worker: IWorker
{
private readonly IHttpClientManager _httpClient;
private readonly ITokenService _tokenService;
private readonly SemaphoreSlim _semaphore;
public Worker(IHttpClientManager httpClient, ITokenService tokenService)
{
_httpClient = httpClient;
_tokenService = tokenService;
// we want to limit the number of items here
_semaphore = new SemaphoreSlim(10);
}
public async Task<JObject> ProcessRequestAsync(string request, string route)
{
try
{
var accessToken = await _tokenService.GetTokenAsync(
_timeSeriesConfiguration.TenantId,
_timeSeriesConfiguration.ClientId,
_timeSeriesConfiguration.ClientSecret);
var cancellationToken = new CancellationTokenSource();
cancellationToken.CancelAfter(30000);
await _semaphore.WaitAsync(cancellationToken.Token);
var httpResponseMessage = await _httpClient.SendAsync(new HttpClientRequest
{
Method = HttpMethod.Post,
Uri = $"https://someuri/someroute",
Token = accessToken,
Content = request
});
var response = await httpResponseMessage.Content.ReadAsStringAsync();
return response;
}
catch (Exception ex)
{
// do some logging
throw;
}
finally
{
_semaphore.Release();
}
}
}
I have a function that returns a Stream with data of a file being downloaded using HttpContent that is the content of a HttpResponseMessage (using Owin)
For those interested I'll add the code at the end.
The interface to get the stream is:
async Task<Stream> DownloadLogFileAsync(...);
The caller of this function wants to create a file containing the data of this Stream. According to StackOverFlow: How do I save a stream to File I should use Stream.CopyTo to save the contents of the Stream in a file. Something like this:
(simplified: not using CancellationToken)
using(Stream downloadStream = await DownloadLogFileAsync(...) )
{
using (var fileStream = System.IO.File.Create(fullFileName))
{
await downloadStream.CopyToAsync(fileStream);
}
}
Question:
Would it improve performance if the FileStream has the same buffer size as the downloadStream? How do I get the buffer size of the download stream?
End of Question
Not related to the question, only for those interested in the OWIN / ASP file download:
I have a WCF service with a function that returns data. Creation of this data takes a considerable amount of time. The size of the returned data might be huge. Therefore it is decided to split this function into two functions:
Request creation of the file. Return a unique file handle
Request a stream containing the data of the created file.
Of course my WCF service needs proper functions to cancel creation, delete the created file and do some cleanup if the file is getting old and client forgot to request deletion.
All functions can be done using WCF. The only one that needs OWIN / ASP is the request for the file stream.
class OwinFileClient
{
private const string routePrefix = "SipLoggerFile/";
private readonly WebApiClient webApiClient = new WebApiClient() {...}
// Function to download the created file:
public async Task<Stream> DownloadSipLogFileAsync(Guid fileHandle)
{
var httpClient = this.webApiClient.CreateHttpClient();
string requestStreamFile = routePrefix + #"DownloadFile?fileHandle="
+ fileHandle.ToString("N");
var response = await httpClient.GetAsync(requestStreamFile)
.ConfigureAwait(false);
if (!response.IsSuccessStatusCode)
{
string content = await response
.Content.ReadAsStringAsync()
.ConfigureAwait(false);
throw new System.Net.Http.HttpRequestException(
$"{response.StatusCode} [{(int)response.StatusCode}]: {content}");
}
// if here: success: deserialize the data
return await response.Content.ReadAsStreamAsync()
.ConfigureAwait(false);
}
}
And the WebApiClient:
class WebApiClient
{
public Uri baseAddress { get; set; }
public TimeSpan Timeout { get; set; }
public ICredentials Credentials { get; set; }
public IWebProxy Proxy { get; set; }
public HttpClient CreateHttpClient()
{
return new HttpClient(CreateHttpMessageHandler())
{
BaseAddress = this.baseAddress,
Timeout = this.Timeout,
};
}
private HttpMessageHandler CreateHttpMessageHandler()
{
return new HttpClientHandler()
{
AutomaticDecompression = System.Net.DecompressionMethods.GZip
| System.Net.DecompressionMethods.Deflate,
Credentials = this.Credentials,
PreAuthenticate = this.Credentials != null,
Proxy = this.Proxy,
UseProxy = this.Proxy != null,
};
}
I discovered that a single HttpClient could be shared by multiple requests. If shared, and the requests are to the same destination, multiple requests could reuse the connections. WebRequest needs to recreate the connection for each request.
I also looked up some documentation on other ways to use HttpClient in examples.
The following article summarizes the high-speed NTLM-authenticated connection sharing: HttpWebRequest.UnsafeAuthenticatedConnectionSharing
Possible implementations that I tried out are shown below
A)
private WebRequestHandler GetWebRequestHandler()
{
CredentialCache credentialCache = new CredentialCache();
credentialCache.Add(ResourceUriCanBeAnyUri, "NTLM", CredentialCache.DefaultNetworkCredentials);
WebRequestHandler handler = new WebRequestHandler
{
UnsafeAuthenticatedConnectionSharing = true,
Credentials = credentialCache
};
return handler;
}
using (HttpClient client = new HttpClient(GetWebRequestHandler(), false))
{
}
B)
using (HttpClient client = new HttpClient)
{
}
C)
HttpWebRequest req = (HttpWebRequest)WebRequest.Create("some uri string")
I would appreciate any help in making me understand which approach I should take so as to achieve max performance, minimizing connections and making sure security is not impacted.
If you use either of them with async it should be good for the performance point of view as it will not block the resources waiting for the response and you will get good throughput.
HttpClient is preferred over HttpWebRequest due to async methods available out of the box and you would not have to worry about writing begin/end methods.
Basically when you use async call (using either of the class), it will not block the resources waiting for the response and any other request would utilise the resources to make further calls.
Another thing to keep in mind that you should not be using HttpClient in the 'using' block to allow reuse of same resources again and again for other web requests.
See following thread for more information
Do HttpClient and HttpClientHandler have to be disposed?
This is my ApiClient which creates the HttpClient for only once. Register this object as singleton to your dependency injection library. It's safe to reuse because it's stateless. Do NOT recreate HTTPClient for each request. Reuse Httpclient as much as possible
using System;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
//You need to install package Newtonsoft.Json > https://www.nuget.org/packages/Newtonsoft.Json/
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
public class MyApiClient : IDisposable
{
private readonly TimeSpan _timeout;
private HttpClient _httpClient;
private HttpClientHandler _httpClientHandler;
private readonly string _baseUrl;
private const string ClientUserAgent = "my-api-client-v1";
private const string MediaTypeJson = "application/json";
public MyApiClient(string baseUrl, TimeSpan? timeout = null)
{
_baseUrl = NormalizeBaseUrl(baseUrl);
_timeout = timeout ?? TimeSpan.FromSeconds(90);
}
public async Task<string> PostAsync(string url, object input)
{
EnsureHttpClientCreated();
using (var requestContent = new StringContent(ConvertToJsonString(input), Encoding.UTF8, MediaTypeJson))
{
using (var response = await _httpClient.PostAsync(url, requestContent))
{
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
}
public async Task<TResult> PostAsync<TResult>(string url, object input) where TResult : class, new()
{
var strResponse = await PostAsync(url, input);
return JsonConvert.DeserializeObject<TResult>(strResponse, new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
}
public async Task<TResult> GetAsync<TResult>(string url) where TResult : class, new()
{
var strResponse = await GetAsync(url);
return JsonConvert.DeserializeObject<TResult>(strResponse, new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
}
public async Task<string> GetAsync(string url)
{
EnsureHttpClientCreated();
using (var response = await _httpClient.GetAsync(url))
{
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
public async Task<string> PutAsync(string url, object input)
{
return await PutAsync(url, new StringContent(JsonConvert.SerializeObject(input), Encoding.UTF8, MediaTypeJson));
}
public async Task<string> PutAsync(string url, HttpContent content)
{
EnsureHttpClientCreated();
using (var response = await _httpClient.PutAsync(url, content))
{
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
public async Task<string> DeleteAsync(string url)
{
EnsureHttpClientCreated();
using (var response = await _httpClient.DeleteAsync(url))
{
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
}
public void Dispose()
{
_httpClientHandler?.Dispose();
_httpClient?.Dispose();
}
private void CreateHttpClient()
{
_httpClientHandler = new HttpClientHandler
{
AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip
};
_httpClient = new HttpClient(_httpClientHandler, false)
{
Timeout = _timeout
};
_httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(ClientUserAgent);
if (!string.IsNullOrWhiteSpace(_baseUrl))
{
_httpClient.BaseAddress = new Uri(_baseUrl);
}
_httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(MediaTypeJson));
}
private void EnsureHttpClientCreated()
{
if (_httpClient == null)
{
CreateHttpClient();
}
}
private static string ConvertToJsonString(object obj)
{
if (obj == null)
{
return string.Empty;
}
return JsonConvert.SerializeObject(obj, new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
}
private static string NormalizeBaseUrl(string url)
{
return url.EndsWith("/") ? url : url + "/";
}
}
The usage;
using ( var client = new MyApiClient("http://localhost:8080"))
{
var response = client.GetAsync("api/users/findByUsername?username=alper").Result;
var userResponse = client.GetAsync<MyUser>("api/users/findByUsername?username=alper").Result;
}
There is a problem in your implementation 'A'. The lifetime of the instance returned from GetWebRequestHandler() is shortlived (maybe just for the sake of the example?). If this was done on purpose, it negates the passing of false for the 2nd param of HttpClient constructor. The value of false tells the HttpClient to not dispose the underlying HttpMessageHandler (which helps with scaling since it does not close the port for the request). This is, of course, assuming that the lifetime of the HttpMessageHandler is long enough for you to take advantage of the benefit of not opening/closing ports (which is a big impact on the scalability of your server). Thus, I have a recommendation below of option 'D'.
There is also an option 'D' that you do not list above - to make the Httpclient instance static and re-used across all api calls. This is much more efficient from a memory allocation and GC perspective - as well as the opening of ports on the client. You don't have the overhead of allocating memory for and creating HttpClient instances (and all of its underlying objects) and thus avoid the cleanup via GC for them too.
Please refer to my answer provided about a similar question - What is the overhead of creating a new HttpClient per call in a WebAPI client?
Because Post requests to APIs need to run asynchronously on windows phone, I am struggling to create a lean easy to use library to interact with an API.
The issue is that people using the library will always need to supply a callback function.
Let's take a look at some pseudo code:
PostRequest Class to help me with POST requests:
class PostRequest
{
private Action<MemoryStream> Callback;
public PostRequest(string urlPath, string data, Action<MemoryStream> callback)
{
Callback = callback;
// Form the URI
UriBuilder fullUri = new UriBuilder(urlPath);
if (!string.IsNullOrEmpty(data))
fullUri.Query = data;
// Initialize a new WebRequest
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(fullUri.Uri);
request.Method = "POST";
// Set up the state object for the async request
DataUpdateState dataState = new DataUpdateState();
dataState.AsyncRequest = request;
// Start the asynchronous request
request.BeginGetResponse(new AsyncCallback(HandleResponse),
dataState);
}
private void HandleResponse(IAsyncResult asyncResult)
{
// Get the state information
DataUpdateState dataState = (DataUpdateState)asyncResult.AsyncState;
HttpWebRequest dataRequest = (HttpWebRequest)dataState.AsyncRequest;
// End the async request
dataState.AsyncResponse = (HttpWebResponse)dataRequest.EndGetResponse(asyncResult);
if (dataState.AsyncResponse.StatusCode.ToString() == "OK")
{
// Create a stream from the response
Stream response = dataState.AsyncResponse.GetResponseStream();
TextReader textReader = new StreamReader(response, true);
string jsonString = textReader.ReadToEnd();
MemoryStream stream = new MemoryStream(Encoding.UTF8.GetBytes(jsonString));
// Send the stream through to the callback function
Callback(stream);
}
}
}
public class DataUpdateState
{
public HttpWebRequest AsyncRequest { get; set; }
public HttpWebResponse AsyncResponse { get; set; }
}
The API Access Object classes:
class APIAuthenticationCredentials
{
public String Username { get; set; }
public String Password { get; set; }
}
class APIAO
{
private String AuthUrl = "http://api.example.com/";
public static Auth Auth = new Auth();
//...
public static void Authenticate( String data, APIAuthenticationCredentials credentials, Action<MemoryStream> callback )
{
PostRequest request = new PostRequest(AuthURL, data, callback);
}
//...
}
You will notice I have to pass a callback function all the way through this so that once the data is returned by the HandleResponse method in my PostRequest class, the data is forwarded onto some controller that makes the screen do something with the data. At the moment, it's not ultra horrid to use:
private void DisplayData(MemoryStream stream)
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Auth));
APIAO.Auth = (Auth)serializer.ReadObject(stream);
}
//...
APIAuthenticationCredentials credentials = new APIAuthenticationCredentials {
Username = "whatever",
Password = "whatever"
}
APIAO.Authenticate( credentials, DisplayData );
//...
The problem is I want to create some kind of repository style pattern... Let's say the API returned different json models, one call returned an array of products... the problem is that I want to create one lovely repository call eg:
IProductRepository productRepository = new ProductRepository();
productRepository.GetAll();
But I've gotta put some GOSH DARN callback function in it too and that means every repository method of any object type returned by the API is going to have this MemoryStream callback... and if I ever want to change that functionality, I've gotta update that stuff everywhere yo. :(
Has anyone seen a better way of doing this crap.
This is starting to become far too complex
--crying
A simpler answer using newer language constructs would be:
public static Task<string> GetData(string url, string data)
{
UriBuilder fullUri = new UriBuilder(url);
if (!string.IsNullOrEmpty(data))
fullUri.Query = data;
WebClient client = new WebClient();
client.Credentials = CredentialCache.DefaultCredentials;//TODO update as needed
return client.DownloadStringTaskAsync(fullUri.Uri);
}
In a 4.0 project you can use a TaskCompletionSource to translate a non-Task asynchronous model into a Task:
public static Task<string> GetData2(string url, string data)
{
UriBuilder fullUri = new UriBuilder(url);
if (!string.IsNullOrEmpty(data))
fullUri.Query = data;
WebClient client = new WebClient();
client.Credentials = CredentialCache.DefaultCredentials;//TODO update as needed
var tcs = new TaskCompletionSource<string>();
client.DownloadStringCompleted += (s, args) =>
{
if (args.Error != null)
tcs.TrySetException(args.Error);
else if (args.Cancelled)
tcs.TrySetCanceled();
else
tcs.TrySetResult(args.Result);
};
client.DownloadStringAsync(fullUri.Uri);
return tcs.Task;
}
The caller now has a Task<string> that represents the results of this asynchronous operation. They can wait on it synchronously and get the result using the Result property, they can add a callback that will execute when the operation finishes using ContinueWith, or they can await the task in an async method which, under the hood, will wire up the remainder of that method as a continuation of that task, but without creating a new method or even a new scope, i.e.
public static async Task Foo()
{
string result = await GetData("http://google.com", "");
Console.WriteLine(result);
}
This will start the asynchronous task, add a callback (or continuation) to that task so that when it runs it will continue executing code where it left off, at which point it will then write the results to the console and mark the Task that this method returns as completed, so that any continuations to this method will then execute (allowing for composition of async methods).