เราควรสร้าง HttpClient อินสแตนซ์ใหม่สำหรับคำขอทั้งหมดหรือไม่


57

เมื่อเร็ว ๆ นี้ฉันได้พบกับบล็อกโพสต์นี้จากอสุรกาย asp.net ซึ่งพูดถึงปัญหาเกี่ยวกับการใช้HttpClientในวิธีต่อไปนี้:

using(var client = new HttpClient())
{
}

ตามโพสต์ในบล็อกถ้าเรากำจัดHttpClientหลังจากทุกคำขอมันสามารถเปิดการเชื่อมต่อ TCP ได้ System.Net.Sockets.SocketExceptionนี้อาจจะนำไปสู่การ

วิธีที่ถูกต้องตามโพสต์คือการสร้างอินสแตนซ์เดียวHttpClientซึ่งจะช่วยลดการสิ้นเปลืองซ็อกเก็ต

จากโพสต์:

หากเราแบ่งปัน HttpClient อินสแตนซ์เดียวเราสามารถลดการใช้ซ็อกเก็ตได้โดยการใช้ซ้ำ:

namespace ConsoleApplication
{
    public class Program
    {
        private static HttpClient Client = new HttpClient();
        public static void Main(string[] args)
        {
            Console.WriteLine("Starting connections");
            for(int i = 0; i<10; i++)
            {
                var result = Client.GetAsync("http://aspnetmonsters.com").Result;
                Console.WriteLine(result.StatusCode);
            }
            Console.WriteLine("Connections done");
            Console.ReadLine();
        }
    }
}

ฉันทิ้งHttpClientวัตถุไว้เสมอหลังจากใช้มันเพราะฉันรู้สึกว่านี่เป็นวิธีที่ดีที่สุดในการใช้มัน แต่การโพสต์บล็อกนี้ทำให้ฉันรู้สึกว่าฉันทำผิดไปตลอด

เราควรสร้างอินสแตนซ์ใหม่ของHttpClientคำขอทั้งหมดหรือไม่ มีข้อผิดพลาดใด ๆ ในการใช้อินสแตนซ์แบบคงที่หรือไม่?


คุณเคยประสบปัญหาใด ๆ ที่คุณได้รับจากการใช้งานหรือไม่?
whatsisname

อาจตรวจสอบคำตอบนี้ และสิ่งนี้ด้วย
John Wu

@ ไม่มีชื่อฉันไม่ได้ แต่ดูที่บล็อกฉันรู้สึกว่าฉันอาจใช้ผิดตลอดเวลา ดังนั้นต้องการที่จะเข้าใจจากนักพัฒนาเพื่อนถ้าพวกเขาเห็นปัญหาใด ๆ ในแนวทางใด
Ankit Vijay

3
ฉันไม่ได้ลองด้วยตัวเอง (ดังนั้นอย่าให้คำตอบนี้) แต่จาก microsoft ณ . NET Core 2.1 คุณควรใช้ HttpClientFactory ตามที่อธิบายไว้ในdocs.microsoft.com/en-us/dotnet/standard/ …
Joeri Sebrechts

(ตามที่ระบุไว้ในคำตอบของฉันเพียงแค่อยากจะทำให้มันชัดเจนมากขึ้นดังนั้นฉันเขียนความคิดเห็นสั้น.) เช่นแบบคงที่ถูกต้องจะจัดการกับการจับมือปิดการเชื่อมต่อ TCP, เมื่อคุณทำหรือเริ่มต้นใหม่Close() Get()หากคุณทิ้งลูกค้าเมื่อเสร็จแล้วจะไม่มีใครจัดการจับมือปิดนั้นและพอร์ตของคุณจะมีสถานะ TIME_WAIT เพราะสิ่งนั้น
Mladen B.

คำตอบ:


39

ดูเหมือนว่าโพสต์บล็อกที่น่าสนใจ อย่างไรก็ตามก่อนที่จะตัดสินใจฉันจะรันการทดสอบเดียวกันกับที่ผู้เขียนบล็อกใช้ก่อน แต่ใช้รหัสของคุณเอง ฉันจะลองและหาข้อมูลเพิ่มเติมเกี่ยวกับ HttpClient และพฤติกรรมของมัน

รัฐโพสต์นี้ :

อินสแตนซ์ HttpClient คือชุดของการตั้งค่าที่ใช้กับคำขอทั้งหมดที่ดำเนินการโดยอินสแตนซ์นั้น นอกจากนี้ทุกอินสแตนซ์ HttpClient ใช้พูลการเชื่อมต่อของตัวเองโดยแยกคำขอออกจากคำขอที่ดำเนินการโดยอินสแตนซ์ HttpClient อื่น ๆ

ดังนั้นสิ่งที่อาจเกิดขึ้นเมื่อมีการแชร์ HttpClient คือการเชื่อมต่อกำลังถูกนำมาใช้ใหม่ซึ่งเป็นเรื่องปกติถ้าคุณไม่ต้องการการเชื่อมต่อแบบถาวร วิธีเดียวที่คุณจะทราบได้อย่างแน่นอนว่าเรื่องนี้สำหรับสถานการณ์ของคุณหรือไม่ก็คือการทดสอบประสิทธิภาพของคุณเอง

หากคุณขุดคุณจะพบทรัพยากรอื่น ๆ อีกมากมายที่แก้ไขปัญหานี้ (รวมถึงบทความแนวทางปฏิบัติที่ดีที่สุดของ Microsoft) ดังนั้นจึงเป็นความคิดที่ดีที่จะนำไปใช้งานต่อไป (พร้อมข้อควรระวัง)

อ้างอิง

คุณกำลังใช้ Httpclient ผิดและมันทำให้ซอฟต์แวร์ของคุณเสถียร
HttpClient? ระวังพฤติกรรมร้ายแรงนี้และวิธีการแก้ไขได้
รูปแบบไมโครซอฟท์และการปฏิบัติ - การเพิ่มประสิทธิภาพการปฏิบัติงาน: ที่ไม่เหมาะสม Instantiation
เช่นเดียวของนำมาใช้ใหม่ใน HttpClient รหัสตรวจสอบ
ซิงเกิล HttpClient ไม่เคารพเปลี่ยนแปลง DNS (CoreFX)
คำแนะนำทั่วไปสำหรับการใช้ HttpClient


1
นั่นเป็นรายการที่กว้างขวางดี นี่คือสุดสัปดาห์ที่ฉันอ่าน
Ankit Vijay

"ถ้าคุณขุดคุณจะพบทรัพยากรอื่น ๆ อีกมากมายที่จัดการปัญหานี้ ... " คุณหมายถึงว่าปัญหาการเชื่อมต่อ TCP เปิดหรือไม่
Ankit Vijay

คำตอบสั้น: ใช้คง HttpClient หากคุณต้องการรองรับการเปลี่ยนแปลง DNS (จากเว็บเซิร์ฟเวอร์ของคุณหรือเซิร์ฟเวอร์อื่น ๆ ) คุณต้องกังวลเกี่ยวกับการตั้งค่าการหมดเวลา
Jess

3
มันเป็นข้อพิสูจน์ถึงความสับสนของ HttpClient ว่าการใช้มันเป็น "วันหยุดสุดสัปดาห์อ่าน" ตามที่แสดงความคิดเห็นโดย @AnkitVijay
usr

@ Jess นอกเหนือจากการเปลี่ยนแปลง DNS แล้วการส่งทราฟฟิกของลูกค้าของคุณผ่านซ็อกเก็ตเดียวจะทำให้การทำโหลดบาลานซ์เกิดความสมดุลเช่นกัน?
Iain

16

ฉันไปงานปาร์ตี้ช้า แต่นี่คือการเรียนรู้ของฉันในหัวข้อที่ซับซ้อนนี้

1. เราจะหาผู้สนับสนุนอย่างเป็นทางการเกี่ยวกับการนำ HttpClient กลับมาใช้ใหม่ได้ที่ไหน?

ฉันหมายความว่าถ้าการใช้ HttpClient ซ้ำนั้นมีความสำคัญ และการทำเช่นนั้นมีความสำคัญผู้สนับสนุนดังกล่าวจะได้รับการจัดทำเป็นเอกสารที่ดีขึ้นในเอกสาร API ของตนเองแทนที่จะซ่อนอยู่ใน "หัวข้อขั้นสูง", "รูปแบบประสิทธิภาพ (ต่อต้าน)" หรือบล็อกโพสต์อื่น ๆ . มิฉะนั้นผู้เรียนรายใหม่จะรู้ได้อย่างไรก่อนที่จะสายเกินไป

ณ ตอนนี้ (พฤษภาคม 2018) ผลการค้นหาแรกเมื่อ googling "c # httpclient" ชี้ไปที่หน้าอ้างอิง API นี้บน MSDNซึ่งไม่ได้กล่าวถึงความตั้งใจนั้นเลย บทเรียนที่ 1 สำหรับผู้เริ่มต้นคือคลิกที่ลิงค์ "รุ่นอื่น ๆ " เสมอหลังจากหัวข้อความช่วยเหลือของ MSDN คุณอาจพบลิงก์ไปยัง "เวอร์ชันปัจจุบัน" ที่นั่น ในกรณี HttpClient นี้มันจะนำคุณไปยังเอกสารล่าสุด ที่นี่ซึ่งมีคำอธิบายเจตนานั้น

ฉันสงสัยว่านักพัฒนาจำนวนมากที่เป็นของใหม่กับหัวข้อนี้ไม่พบหน้าเอกสารที่ถูกต้องอย่างใดอย่างหนึ่งที่ว่าทำไมความรู้นี้ไม่ได้แพร่กระจายอย่างกว้างขวางและคนประหลาดใจเมื่อพวกเขาพบว่ามันออกมา ในภายหลังอาจจะเป็นในทางที่ยาก

2. ความคิด (mis?) ของ using IDisposable

หนึ่งนี้เป็นเพียงเล็กน้อยปิดหัวข้อ แต่ยังคงมีมูลค่าชี้ให้เห็นว่ามันไม่ใช่เรื่องบังเอิญที่จะเห็นคนในบรรดาบล็อกโพสต์ดังกล่าวว่าโทษHttpClientของIDisposableอินเตอร์เฟซที่ทำให้พวกเขามีแนวโน้มที่จะใช้using (var client = new HttpClient()) {...}รูปแบบแล้วนำไปสู่ปัญหา

ผมเชื่อว่ามาลงไปไม่ได้พูด (MIS?) ความคิด: "วัตถุ IDisposable คาดว่าจะเป็นช่วงสั้น ๆ"

อย่างไรก็ตามในขณะที่มันดูเหมือนสั้นเมื่อเราเขียนรหัสในลักษณะนี้:

using (var foo = new SomeDisposableObject())
{
    ...
}

เอกสารอย่างเป็นทางการใน IDisposable ไม่เคยกล่าวถึงIDisposableวัตถุต้องเป็นช่วงสั้น ๆ ตามคำนิยาม IDisposable เป็นเพียงกลไกที่ช่วยให้คุณปล่อยทรัพยากรที่ไม่มีการจัดการ ไม่มีอะไรเพิ่มเติม ในแง่นี้คุณคาดหวังว่าจะทำให้เกิดการกำจัดได้ในที่สุด แต่ก็ไม่ต้องการให้คุณทำเช่นนั้นในช่วงเวลาสั้น ๆ

ดังนั้นจึงเป็นหน้าที่ของคุณที่จะต้องเลือกอย่างถูกต้องเมื่อใดที่จะเรียกใช้การกำจัดโดยยึดตามข้อกำหนดวงจรชีวิตของวัตถุจริง ไม่มีอะไรหยุดคุณจากการใช้ IDisposable ในทางยาว:

using System;
namespace HelloWorld
{
    class Hello
    {
        static void Main()
        {
            Console.WriteLine("Hello World!");

            using (var client = new HttpClient())
            {
                for (...) { ... }  // A really long loop

                // Or you may even somehow start a daemon here

            }

            // Keep the console window open in debug mode.
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
    }
}

ด้วยความเข้าใจใหม่นี้ตอนนี้เรากลับมาที่โพสต์บล็อกเราสามารถสังเกตเห็นได้อย่างชัดเจนว่า "แก้ไข" เริ่มต้นHttpClientครั้งเดียว แต่ไม่เคยกำจัดนั่นคือเหตุผลที่เราสามารถเห็นได้จากการส่งออก netstat ว่าการเชื่อมต่อยังคงอยู่ในสถานะ ESTABLISHED ซึ่งหมายความว่า ปิดไม่สนิท หากถูกปิดสถานะของมันจะอยู่ใน TIME_WAIT แทน ในทางปฏิบัติมันไม่ใช่เรื่องใหญ่ที่จะรั่วไหลเพียงหนึ่งการเชื่อมต่อที่เปิดหลังจากที่โปรแกรมทั้งหมดของคุณสิ้นสุดลงและผู้โพสต์บล็อกยังคงเห็นประสิทธิภาพที่เพิ่มขึ้นหลังจากการแก้ไข; แต่ถึงกระนั้นมันเป็นแนวคิดที่ไม่ถูกต้องที่จะตำหนิ IDisposable และเลือกที่จะไม่ทิ้งมัน

3. เราต้องใส่ HttpClient เป็นคุณสมบัติคงที่หรือแม้แต่วางไว้เป็นซิงเกิลหรือไม่?

จากความเข้าใจในหัวข้อก่อนหน้านี้ฉันคิดว่าคำตอบที่นี่ชัดเจน: "ไม่จำเป็น" มันขึ้นอยู่กับว่าคุณจะจัดระเบียบรหัสของคุณอย่างไรตราบใดที่คุณใช้ HttpClient AND (ในอุดมคติ) จะกำจัดมันในที่สุด

อย่างสนุกสนานไม่ใช่แม้แต่ตัวอย่างใน ข้อสังเกตของเอกสารอย่างเป็นทางการในปัจจุบันที่ ทำอย่างถูกต้อง มันกำหนดคลาส "GoodController" ที่มีคุณสมบัติ HttpClient คงที่จะไม่ถูกกำจัด; ซึ่งไม่เชื่อฟังสิ่งที่อีกตัวอย่างหนึ่งในส่วนตัวอย่าง เน้น: "จำเป็นต้องโทรทิ้ง ... เพื่อให้แอปไม่รั่วไหลของทรัพยากร"

และท้ายที่สุดซิงเกิลไม่ได้อยู่ในความท้าทายของตัวเอง

"มีคนคิดว่าตัวแปรทั่วโลกเป็นความคิดที่ดีหรือไม่ไม่มีใคร

มีกี่คนที่คิดว่าซิงเกิลตันเป็นความคิดที่ดี? จำนวนน้อย.

สิ่งที่ช่วยให้? Singletons เป็นเพียงตัวแปรทั่วโลก "

- ยกมาจากการพูดคุยที่สร้างแรงบันดาลใจ"Global State และ Singletons"

PS: SqlConnection

สิ่งนี้ไม่เกี่ยวข้องกับคำถามและคำตอบในปัจจุบัน แต่อาจเป็นสิ่งที่ดีที่จะรู้ รูปแบบการใช้งานของ SqlConnection แตกต่างกัน คุณไม่จำเป็นต้องใช้ SqlConnection ซ้ำเพราะมันจะจัดการกับพูลการเชื่อมต่อได้ดีกว่า

ความแตกต่างเกิดจากวิธีการใช้งานของพวกเขา แต่ละอินสแตนซ์ HttpClient ใช้พูลการเชื่อมต่อของตนเอง (อ้างอิงจาก ที่นี่ ); แต่ SqlConnection ตัวเองมีการจัดการโดยสระว่ายน้ำการเชื่อมต่อกลางตามนี้

และคุณยังต้องกำจัด SqlConnection เหมือนกับที่คุณควรทำกับ HttpClient


14

HttpClientฉันไม่ทดสอบบางอย่างเห็นการปรับปรุงประสิทธิภาพการทำงานกับแบบคงที่ ฉันใช้โค้ดด้านล่างสำหรับการทดสอบของฉัน:

namespace HttpClientTest
{
    using System;
    using System.Net.Http;

    class Program
    {
        private static readonly int _connections = 10;
        private static readonly HttpClient _httpClient = new HttpClient();

        private static void Main()
        {
            TestHttpClientWithStaticInstance();
            TestHttpClientWithUsing();
        }

        private static void TestHttpClientWithUsing()
        {
            try
            {
                for (var i = 0; i < _connections; i++)
                {
                    using (var httpClient = new HttpClient())
                    {
                        var result = httpClient.GetAsync(new Uri("http://bing.com")).Result;
                    }
                }
            }
            catch (Exception exception)
            {
                Console.WriteLine(exception);
            }
        }

        private static void TestHttpClientWithStaticInstance()
        {
            try
            {
                for (var i = 0; i < _connections; i++)
                {
                    var result = _httpClient.GetAsync(new Uri("http://bing.com")).Result;
                }
            }
            catch (Exception exception)
            {
                Console.WriteLine(exception);
            }
        }
    }
}

สำหรับการทดสอบ:

  • ฉันรันรหัสด้วยการเชื่อมต่อ 10, 100, 1,000 และ 1,000
  • วิ่งทดสอบแต่ละครั้ง 3 ครั้งเพื่อหาค่าเฉลี่ย
  • ดำเนินการครั้งละหนึ่งวิธี

ฉันพบว่าการปรับปรุงประสิทธิภาพระหว่าง 40% ถึง 60% uon โดยใช้สแตติกHttpClientแทนที่จะทิ้งไว้สำหรับการHttpClientร้องขอ ฉันได้ใส่รายละเอียดของผลการทดสอบประสิทธิภาพในบล็อกโพสต์ที่นี่


1

ในการปิดการเชื่อมต่อ TCPอย่างถูกต้องเราจำเป็นต้องดำเนินการตามลำดับแพ็คเก็ต FIN - FIN + ACK - ACK (เช่น SYN - SYN + ACK - ACK เมื่อเปิดการเชื่อมต่อ TCP ) หากเราเพิ่งเรียกใช้เมธอด .Close () (มักเกิดขึ้นเมื่อHttpClientกำลังกำจัด) และเราไม่รอให้ฝั่งระยะไกลยืนยันการปิดคำขอของเรา (ด้วย FIN + ACK) เราจะจบด้วยสถานะ TIME_WAIT พอร์ต TCP ในตัวเครื่องเนื่องจากเรากำจัดผู้ฟัง (HttpClient) และเราไม่เคยมีโอกาสรีเซ็ตสถานะพอร์ตให้เป็นสถานะปิดที่เหมาะสมเมื่อเพียร์ระยะไกลส่งแพ็กเก็ต FIN + ACK ให้เรา

วิธีที่เหมาะสมในการปิดการเชื่อมต่อ TCP คือการเรียกใช้วิธีการ. ปิด () และรอให้เหตุการณ์ปิดจากอีกด้านหนึ่ง (FIN + ACK) มาถึงด้านข้างของเรา เท่านั้นจากนั้นเราสามารถส่ง ACK สุดท้ายของเราและกำจัด HttpClient

เพียงเพิ่มจะทำให้การเชื่อมต่อ TCP เปิดหากคุณกำลังดำเนินการร้องขอ HTTP เนื่องจากส่วนหัว HTTP "การเชื่อมต่อ: Keep-Alive" นอกจากนี้คุณอาจขอให้รีโมตเพียร์ปิดการเชื่อมต่อแทนโดยการตั้งค่าส่วนหัว HTTP "การเชื่อมต่อ: ปิด" ด้วยวิธีนี้พอร์ตภายในเครื่องของคุณจะปิดอย่างถูกต้องเสมอแทนที่จะอยู่ในสถานะ TIME_WAIT


1

นี่คือไคลเอนต์ API พื้นฐานที่ใช้ HttpClient และ HttpClientHandler ได้อย่างมีประสิทธิภาพ เมื่อคุณสร้าง HttpClient ใหม่เพื่อขอมีค่าใช้จ่ายจำนวนมาก อย่าสร้าง HttpClient ขึ้นมาใหม่สำหรับแต่ละคำขอ ใช้ HttpClient ให้มากที่สุดเท่าที่จะทำได้ ...

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 + "/";
    }
}

การใช้งาน:

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;
}

-5

ไม่มีวิธีการใช้คลาส HttpClient กุญแจสำคัญคือการออกแบบแอปพลิเคชันของคุณในแบบที่เหมาะสมกับสภาพแวดล้อมและข้อ จำกัด

HTTP เป็นโปรโตคอลที่ยอดเยี่ยมที่จะใช้เมื่อคุณต้องการเปิดเผย API สาธารณะ นอกจากนี้ยังสามารถใช้อย่างมีประสิทธิภาพสำหรับ latency ต่ำที่มีน้ำหนักเบาบริการภายใน - แม้ว่ารูปแบบคิวข้อความ RPC มักจะเป็นตัวเลือกที่ดีกว่าสำหรับบริการภายใน

มีความซับซ้อนมากในการทำ HTTP ให้ดี

พิจารณาสิ่งต่อไปนี้:

  1. การสร้างซ็อกเก็ตและการสร้างการเชื่อมต่อ TCP ใช้แบนด์วิดท์เครือข่ายและเวลา
  2. HTTP / 1.1 รองรับการร้องขอไปป์ไลน์ในซ็อกเก็ตเดียวกัน ส่งคำขอหลายรายการทีละรายการโดยไม่ต้องรอคำตอบก่อนหน้านี้ - นี่อาจเป็นสาเหตุของการปรับปรุงความเร็วที่รายงานโดยโพสต์บล็อก
  3. การแคชและ load balancer - หากคุณมี load balancer อยู่ด้านหน้าเซิร์ฟเวอร์จากนั้นตรวจสอบให้แน่ใจว่าคำขอของคุณมีส่วนหัวแคชที่เหมาะสมสามารถลดภาระบนเซิร์ฟเวอร์ของคุณและรับการตอบสนองจากลูกค้าได้เร็วขึ้น
  4. ไม่ต้องสำรวจทรัพยากรโดยใช้ HTTP chunking เพื่อส่งคืนการตอบกลับเป็นระยะ

เหนือสิ่งอื่นใดทดสอบวัดและยืนยัน หากมันไม่ได้ทำงานตามที่ได้รับการออกแบบเราสามารถตอบคำถามเฉพาะเกี่ยวกับวิธีการบรรลุผลลัพธ์ที่คาดหวังของคุณ


4
นี่ไม่ได้ตอบคำถามที่ถาม
whatsisname

คุณคิดว่ามีวิธีที่ถูกต้องเพียงวิธีเดียว ฉันไม่คิดว่าจะมี ฉันรู้ว่าคุณต้องใช้มันอย่างเหมาะสมจากนั้นทดสอบและวัดว่ามันทำงานอย่างไรและปรับวิธีการของคุณจนกว่าคุณจะมีความสุข
Michael Shaw

คุณเขียนเล็กน้อยเกี่ยวกับการใช้ว่าจะใช้ HTTP หรือไม่ในการสื่อสาร OP ถามถึงวิธีการใช้ส่วนประกอบไลบรารีที่ดีที่สุด
whatsisname

1
@MichaelShaw: การดำเนินการHttpClient IDisposableดังนั้นจึงไม่มีเหตุผลที่จะคาดหวังว่ามันจะเป็นวัตถุอายุสั้นที่รู้วิธีทำความสะอาดหลังตัวเองเหมาะสำหรับการห่อusingคำสั่งทุกครั้งที่คุณต้องการ น่าเสียดายที่นั่นไม่ใช่วิธีการใช้งานจริง บล็อกโพสต์ที่ OP เชื่อมโยงอย่างชัดเจนแสดงให้เห็นว่ามีทรัพยากร (โดยเฉพาะการเชื่อมต่อซ็อกเก็ต TCP) ที่มีชีวิตอยู่ได้นานหลังจากที่usingคำสั่งไม่ได้อยู่ในขอบเขตและHttpClientวัตถุได้ถูกกำจัด
Robert Harvey

1
ฉันเข้าใจกระบวนการคิดนั้น เป็นเพียงถ้าคุณกำลังคิดเกี่ยวกับ HTTP จากมุมมองสถาปัตยกรรมและตั้งใจที่จะทำการร้องขอจำนวนมากไปยังบริการเดียวกัน - จากนั้นคุณจะคิดถึงการแคชและการวางท่อจากนั้นความคิดในการทำให้ HttpClient เป็นวัตถุที่มีอายุสั้น เพียงแค่รู้สึกผิด ในทำนองเดียวกันหากคุณทำการร้องขอไปยังเซิร์ฟเวอร์ที่แตกต่างกันและจะไม่ได้รับประโยชน์จากการรักษาซ็อกเก็ตให้ใช้งานอยู่ดังนั้นการทิ้งวัตถุ HttpClient หลังจากการใช้งานจะสมเหตุสมผล
Michael Shaw
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.