[C#] WebAPI 클라이언트에서 호출 당 새로운 HttpClient를 작성하는 오버 헤드는 무엇입니까?

HttpClientWebAPI 클라이언트 의 수명은 어떻게됩니까? 여러 통화에 대해
하나의 인스턴스를 갖는 것이 더 낫 HttpClient습니까?

HttpClient아래 예와 같이 요청 당 생성 및 처리의 오버 헤드는 무엇입니까 ( 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);
    }
}



답변

HttpClient여러 통화에 대해 다시 사용할 수 있도록 설계되었습니다 . 여러 스레드에서도 가능합니다. 는 HttpClientHandler다시 사용 통화를 통해 의도하는 자격 증명과 쿠키가 있습니다. 새로운 HttpClient인스턴스를 가지려면 모든 것을 재설정해야합니다. 또한이 DefaultRequestHeaders속성에는 여러 호출을위한 속성이 포함되어 있습니다. 각 요청에서 해당 값을 재설정하면 요점을 잃습니다.

또 다른 주요 이점은 요청 / 응답 파이프 라인에 HttpClient추가 HttpMessageHandlers하여 교차 절단 문제를 적용 할 수 있다는 것입니다. 로깅, 감사, 제한, 리디렉션 처리, 오프라인 처리, 메트릭 캡처에 사용될 수 있습니다. 모든 종류의 다른 것들. 각 요청에 대해 새로운 HttpClient가 작성되는 경우, 각 메시지 핸들러는 각 요청마다 설정되어야하며 이러한 핸들러에 대한 요청간에 공유되는 애플리케이션 레벨 상태도 제공되어야합니다.

의 기능을 많이 사용 HttpClient할수록 기존 인스턴스를 재사용하는 것이 더 합리적임을 알 수 있습니다.

그러나 가장 큰 문제는 HttpClient클래스가 폐기되면을 삭제 HttpClientHandler하고 TCP/IP에서 관리하는 연결 풀에서 연결 을 강제로 닫는다는 것입니다 ServicePointManager. 이는 새로운 요청이있을 때마다 HttpClient새로운 TCP/IP연결을 다시 설정해야 함을 의미 합니다.

내 테스트에서 LAN에서 일반 HTTP를 사용하면 성능 저하가 거의 무시할 수 있습니다. 연결 HttpClientHandler을 닫으려고 할 때도 연결을 열어두고있는 기본 TCP keepalive가 있기 때문에 이것이 의심 됩니다.

인터넷을 통한 요청에서 나는 다른 이야기를 보았습니다. 매번 요청을 다시 열어야하기 때문에 40 %의 성능 저하를 경험했습니다.

HTTPS연결 에 대한 적중 이 더 나빠질 것으로 생각 됩니다.

내 조언은 연결하는 각 개별 API에 대해 애플리케이션 수명 동안 HttpClient 인스턴스유지하는 것입니다.


답변

응용 프로그램을 확장하려면 차이가 큽니다! 부하에 따라 성능 수치가 매우 다릅니다. Darrel Miller가 언급했듯이 HttpClient는 여러 요청에서 재사용되도록 설계되었습니다. 이것은 BCL 팀의 직원들에 의해 확인되었습니다.

내가 최근에 수행 한 프로젝트는 매우 유명한 대형 온라인 컴퓨터 소매 업체가 일부 새로운 시스템의 Black Friday / holiday 트래픽으로 확장 할 수 있도록 돕는 것이 었습니다. HttpClient 사용과 관련된 몇 가지 성능 문제가 발생했습니다. 그것은 구현하기 때문에 개발자 IDisposable는 인스턴스를 생성하고 using()명령문 안에 배치하여 일반적으로하는 일을했습니다 . 우리가로드 테스트를 시작하면 앱이 서버를 무릎 꿇게했습니다. 예, 서버는 앱만이 아닙니다. 그 이유는 모든 HttpClient 인스턴스가 서버에서 포트를 열기 때문입니다. GC의 결정적이지 않은 마무리와 여러 OSI 계층에 걸쳐있는 컴퓨터 리소스를 사용하고 있기 때문에 네트워크 포트를 닫는 데 시간이 오래 걸릴 수 있습니다. 실제로 Windows OS 자체포트를 닫는 데 최대 20 초가 소요될 수 있습니다 (Microsoft 당). 우리는 닫힐 수있는 것보다 더 빨리 포트를 열었습니다. 서버 포트 소진으로 인해 CPU가 100 %로 떨어졌습니다. 내 문제는 HttpClient를 정적 인스턴스로 변경하여 문제를 해결하는 것이 었습니다. 예, 일회용 자원이지만 성능의 차이로 인해 오버 헤드가 훨씬 더 큽니다. 앱의 작동 방식을 확인하기 위해로드 테스트를 수행하는 것이 좋습니다.

https://www.asp.net/web-api/overview/advanced/calling-a-web-api-from-a-net-client 에서 설명서 및 예제에 대한 WebAPI 지침 페이지를 확인할 수도
있습니다.

이 문구에 특히주의하십시오 :

HttpClient는 한 번 인스턴스화되어 응용 프로그램 수명 동안 재사용됩니다. 특히 서버 애플리케이션에서 모든 요청에 ​​대해 새 HttpClient 인스턴스를 작성하면로드가 많은 경우 사용 가능한 소켓 수가 소모됩니다. SocketException 오류가 발생합니다.

HttpClient다른 헤더, 기본 주소 등 을 사용하여 정적을 사용해야하는 경우 에는HttpRequestMessage 수동으로 해당 값을 설정해야합니다 HttpRequestMessage. 그런 다음HttpClient:SendAsync(HttpRequestMessage requestMessage, ...)

.NET Core 용 업데이트 : IHttpClientFactoryvia Dependency Injection을 사용하여 HttpClient인스턴스 를 만들어야 합니다. 수명을 관리하므로 명시 적으로 폐기 할 필요가 없습니다. ASP.NET Core에서 IHttpClientFactory를 사용하여 HTTP 요청 만들기를 참조하십시오.


답변

다른 답변 상태 HttpClient는 재사용을 의미합니다. 그러나, 하나의 재사용 HttpClient다중 스레드 응용 프로그램의 수단을 통해 인스턴스를 당신이 좋아, 그 상태 속성 값을 변경할 수 없습니다 BaseAddressDefaultRequestHeaders (따라서 응용 프로그램에서 일정한 경우에만 사용할 수 있음).

이 제한을 주변에 얻기를위한 한 가지 방법은 포장입니다 HttpClient모든 복제 클래스와 HttpClient당신이 필요로하는 방법을 ( GetAsync, PostAsync싱글로 등) 위임을 HttpClient. 그러나 그것은 매우 지루합니다 ( 확장 메소드 도 래핑해야합니다 ). 다행히도 새로운 인스턴스를 계속 생성 하지만 기본을 재사용하는 또 다른 방법이 있습니다. 핸들러를 폐기하지 마십시오.HttpClientHttpClientHandler

HttpClientHandler _sharedHandler = new HttpClientHandler(); //never dispose this
HttpClient GetClient(string token)
{
    //client code can dispose these HttpClient instances
    return new HttpClient(_sharedHandler, disposeHandler: false)
    {
       DefaultRequestHeaders =
       {
            Authorization = new AuthenticationHeaderValue("Bearer", token)
       }
    };
}


답변

대용량 웹 사이트와 관련이 있지만 HttpClient와 직접 관련이 없습니다. 모든 서비스에 아래 코드 스 니펫이 있습니다.

        // number of milliseconds after which an active System.Net.ServicePoint connection is closed.
        const int DefaultConnectionLeaseTimeout = 60000;

        ServicePoint sp =
                ServicePointManager.FindServicePoint(new Uri("http://<yourServiceUrlHere>"));
        sp.ConnectionLeaseTimeout = DefaultConnectionLeaseTimeout;

에서 ) https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(System.Net.ServicePoint.ConnectionLeaseTimeout);k(TargetFrameworkMoniker-.NETFramework,Version%3Dv4.5.2; k (DevLang-csharp) & rd = true

“ServicePoint 개체의 활성 연결이 무기한으로 열려 있지 않도록이 속성을 사용할 수 있습니다.이 속성은로드 밸런싱 시나리오와 같이 연결을 주기적으로 삭제하고 재설정해야하는 시나리오를위한 것입니다.

기본적으로 요청에 대해 KeepAlive가 true 인 경우 MaxIdleTime 속성은 비활성으로 인해 ServicePoint 연결을 닫는 시간 제한을 설정합니다. ServicePoint에 활성 연결이있는 경우 MaxIdleTime은 영향을 미치지 않으며 연결은 무기한으로 열려 있습니다.

ConnectionLeaseTimeout 속성이 -1 이외의 값으로 설정되고 지정된 시간이 경과하면 요청에서 KeepAlive를 false로 설정하여 요청을 처리 한 후 활성 ServicePoint 연결이 닫힙니다. 이 값을 설정하면 ServicePoint 개체가 관리하는 모든 연결에 영향을줍니다. “

장애 조치하려는 CDN 또는 기타 엔드 포인트 뒤에 서비스가있는 경우이 설정은 발신자가 사용자를 새로운 목적지로 안내합니다. 이 예에서는 장애 조치 후 60 초 후에 모든 발신자가 새 끝점에 다시 연결해야합니다. 종속 서비스 (전화하는 서비스) 및 해당 엔드 포인트를 알아야합니다.


답변

Simon Timms의이 블로그 게시물을 참조 할 수도 있습니다. https://aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong/

그러나 HttpClient다릅니다. IDisposable인터페이스를 구현하지만 실제로는 공유 객체입니다. 즉, 덮개 아래에 재진입이 가능하며 나사산이 안전합니다. HttpClient각 실행마다 새 인스턴스를 생성하는 대신 HttpClient애플리케이션의 전체 수명 동안 단일 인스턴스를 공유해야 합니다. 왜 그런지 보자.


답변

“사용하지 마십시오”블로그 참고 사항 중 어느 것도 고려할 필요가없는 BaseAddress 및 DefaultHeader가 아니라는 점을 지적해야합니다. HttpClient를 정적으로 만들면 요청을 통해 전달되는 내부 상태가 있습니다. 예 : HttpClient를 사용하여 FedAuth 토큰을 얻기 위해 HttpClient를 사용하여 타사에 인증하고 있는데 (OAuth / OWIN / etc를 사용하지 않는 이유는 무시) 응답 메시지에 FedAuth에 대한 Set-Cookie 헤더가 있으며 이는 HttpClient 상태에 추가됩니다. 각 요청에서 이러한 쿠키를 관리하지 않는 한 API에 로그인 한 다음 사용자는 마지막 사람의 FedAuth 쿠키를 보냅니다.


답변

첫 번째 문제로,이 클래스는 일회용이지만 using명령문 과 함께 사용하는 것은 처분 할 때도 최선의 선택이 아닙니다HttpClient 객체 기본 소켓이 즉시 해제되지 않고 ‘소켓 고갈’이라는 심각한 문제가 발생할 수 있으므로 이 아닙니다.

그러나 HttpClient싱글 톤 또는 정적 객체로 사용할 때 가질 수 있는 두 번째 문제가 있습니다. 이 경우 싱글 톤 또는 정적 HttpClient은 존중하지 않습니다.DNS 변경 사항을 .

.net 코어 에서 HttpClientFactory로 다음과 같은 작업을 수행 할 수 있습니다 .

public interface IBuyService
{
    Task<Buy> GetBuyItems();
}
public class BuyService: IBuyService
{
    private readonly HttpClient _httpClient;

    public BuyService(HttpClient httpClient)
    {
        _httpClient = httpClient;
    }

    public async Task<Buy> GetBuyItems()
    {
        var uri = "Uri";

        var responseString = await _httpClient.GetStringAsync(uri);

        var buy = JsonConvert.DeserializeObject<Buy>(responseString);
        return buy;
    }
}

서비스 구성

services.AddHttpClient<IBuyService, BuyService>(client =>
{
     client.BaseAddress = new Uri(Configuration["BaseUrl"]);
});

여기에 문서와 예제