Asked  7 Months ago    Answers:  5   Viewed   56 times

What should be the HttpClient lifetime of a WebAPI client?
Is it better to have one instance of the HttpClient for multiple calls?

What's the overhead of creating and disposing a HttpClient per request, like in example below (taken from http://www.asp.net/web-api/overview/web-api-clients/calling-a-web-api-from-a-net-client):

using (var client = new HttpClient())
{
    client.BaseAddress = new Uri("http://localhost:9000/");
    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

    // New code:
    HttpResponseMessage response = await client.GetAsync("api/products/1");
    if (response.IsSuccessStatusCode)
    {
        Product product = await response.Content.ReadAsAsync<Product>();
        Console.WriteLine("{0}t${1}t{2}", product.Name, product.Price, product.Category);
    }
}

 Answers

19

HttpClient has been designed to be re-used for multiple calls. Even across multiple threads. The HttpClientHandler has Credentials and Cookies that are intended to be re-used across calls. Having a new HttpClient instance requires re-setting up all of that stuff. Also, the DefaultRequestHeaders property contains properties that are intended for multiple calls. Having to reset those values on each request defeats the point.

Another major benefit of HttpClient is the ability to add HttpMessageHandlers into the request/response pipeline to apply cross cutting concerns. These could be for logging, auditing, throttling, redirect handling, offline handling, capturing metrics. All sorts of different things. If a new HttpClient is created on each request, then all of these message handlers need to be setup on each request and somehow any application level state that is shared between requests for these handlers also needs to be provided.

The more you use the features of HttpClient, the more you will see that reusing an existing instance makes sense.

However, the biggest issue, in my opinion is that when a HttpClient class is disposed, it disposes HttpClientHandler, which then forcibly closes the TCP/IP connection in the pool of connections that is managed by ServicePointManager. This means that each request with a new HttpClient requires re-establishing a new TCP/IP connection.

From my tests, using plain HTTP on a LAN, the performance hit is fairly negligible. I suspect this is because there is an underlying TCP keepalive that is holding the connection open even when HttpClientHandler tries to close it.

On requests that go over the internet, I have seen a different story. I have seen a 40% performance hit due to having to re-open the request every time.

I suspect the hit on a HTTPS connection would be even worse.

My advice is to keep an instance of HttpClient for the lifetime of your application for each distinct API that you connect to.

Tuesday, June 1, 2021
 
alez
answered 7 Months ago
22

c#

public class ValuesController : ApiController
{
    // POST api/values
    [HttpPost] // added attribute
    public IHttpActionResult Post([FromBody] string filterName) // added FromBody as this is how you are sending the data
    {
        return new JsonResult<string>(filterName, new JsonSerializerSettings(), Encoding.UTF8, this);
    }

JavaScript

$.ajax(
{
    url: "/api/Values/", // be consistent and case the route the same as the ApiController
    type: "POST",
    dataType: 'json',
    data: "=Dirty Deeds", // add an = sign
    success: function (result) {
        console.log(result);
    },
    error: function (xhr, status, p3, p4) {
        var err = "Error " + " " + status + " " + p3;
        if (xhr.responseText && xhr.responseText[0] == "{")
            err = JSON.parse(xhr.responseText).message;
        console.log(err);
    }
});

Explanation

Because you are only sending a single value add the = sign in front of it so it will be treated like forms encoding. You can also add the content type if you want to make it clear that this is what you are doing to the ajax call.

contentType: 'application/x-www-form-urlencoded'

Alternatively you could also send the content via URL OR wrap the content in an object on the server as well as in the ajax call and stringify it.

public class Filter {
    public string FilterName {get;set;}
}

public class ValuesController : ApiController
{
    // POST api/values
    [HttpPost] // added attribute
    public IHttpActionResult Post([FromBody] Filter filter) // added FromBody as this is how you are sending the data
    {
        return new JsonResult<string>(filter.FilterName, new JsonSerializerSettings(), Encoding.UTF8, this);
    }

JavaScript

$.ajax(
{
    url: "/api/Values/", // be consistent and case the route the same as the ApiController
    type: "POST",
    dataType: 'json',
    contentType: 'application/json',
    data: JSON.stringify({FilterName: "Dirty Deeds"}), // send as json
    success: function (result) {
        console.log(result);
    },
    error: function (xhr, status, p3, p4) {
        var err = "Error " + " " + status + " " + p3;
        if (xhr.responseText && xhr.responseText[0] == "{")
            err = JSON.parse(xhr.responseText).message;
        console.log(err);
    }
});
Wednesday, July 28, 2021
 
Avicinnian
answered 5 Months ago
48

There are often way too many factors at play... And it's easy to demonstrate both ways:

For example, splitting the following loop results in almost a 2x slow-down (full test code at the bottom):

for (int c = 0; c < size; c++){
    data[c] *= 10;
    data[c] += 7;
    data[c] &= 15;
}

And this is almost stating the obvious since you need to loop through 3 times instead of once and you make 3 passes over the entire array instead of 1.

On the other hand, if you take a look at this question: Why are elementwise additions much faster in separate loops than in a combined loop?

for(int j=0;j<n;j++){
    a1[j] += b1[j];
    c1[j] += d1[j];
}

The opposite is sometimes true due to memory alignment.


What to take from this?

Pretty much anything can happen. Neither way is always faster and it depends heavily on what's inside the loops.

And as such, determining whether such an optimization will increase performance is usually trial-and-error. With enough experience you can make fairly confident (educated) guesses. But in general, expect anything.

"Each additional for-loop would cost the equivalent of two int allocations."

You are correct that it's not that simple. In fact it's so complicated that the numbers don't mean much. A loop iteration may take X cycles in one context, but Y cycles in another due to a multitude of factors such as Out-of-order Execution and data dependencies.

Not only is the performance context-dependent, but it also vary with different processors.


Here's the test code:

#include <time.h>
#include <iostream>
using namespace std;

int main(){

    int size = 10000;
    int *data = new int[size];


    clock_t start = clock();

    for (int i = 0; i < 1000000; i++){
#ifdef TOGETHER
        for (int c = 0; c < size; c++){
            data[c] *= 10;
            data[c] += 7;
            data[c] &= 15;
        }
#else
        for (int c = 0; c < size; c++){
            data[c] *= 10;
        }
        for (int c = 0; c < size; c++){
            data[c] += 7;
        }
        for (int c = 0; c < size; c++){
            data[c] &= 15;
        }
#endif
    }

    clock_t end = clock();
    cout << (double)(end - start) / CLOCKS_PER_SEC << endl;

    system("pause");
}

Output (one loop): 4.08 seconds
Output (3 loops): 7.17 seconds

Tuesday, August 10, 2021
 
avon_verma
answered 4 Months ago
14

Yes you can avoid the "

public class ValuesController : ApiController
{
        public string Get()
        {
            return "qwerty";
        }
}

now check http://localhost:3848/api/values

and response

<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">qwerty</string>

The result with <string> tag but without quotes :)

EDIT

If you don't like this approach,try this.It returns just text

public HttpResponseMessage Get()
{
    string result = "Your text";
    var resp = new HttpResponseMessage(HttpStatusCode.OK);
    resp.Content = new StringContent(result, System.Text.Encoding.UTF8, "text/plain");
    return resp;
}
Friday, August 13, 2021
 
Rajat Gupta
answered 4 Months ago
13

Regarding complexity, returning or passing a reference is just like passing a pointer. Its overhead is equivalent to passing an integer the size of a pointer, plus a few instructions. In short, that is as fast as is possible in nearly every case. Builtin types (e.g. int, float) less than or equal to the size of a pointer are the obvious exception.

At worst, passing/returning a reference can add a few instructions or disable some optimizations. Those losses rarely exceed the costs of returning/passing objects by value (e.g. calling a copy constructor + destructor is much higher, even for a very basic object). Passing/returning by reference is a good default unless every instruction counts, and you have measured that difference.

Therefore, using references has incredibly low overhead.

One can't really quantify how much faster it would be without knowing the complexity of your types and their constructor/destructor, but if it is not a builtin type, then holding a local and returning it by reference will be fastest in most cases - it all depends on the complexity of the object and its copy, but only incredibly trivial objects could come close the speed of the reference.

Friday, November 5, 2021
 
Bálint Molnár
answered 1 Month ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share