ฉันไปงานปาร์ตี้ช้า แต่นี่คือการเรียนรู้ของฉันในหัวข้อที่ซับซ้อนนี้
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