สังเกตการณ์พูลพร็อกซี: เมตริก log การแจ้งเตือน และการวินิจฉัยความเสื่อมประสิทธิภาพในหนึ่งนาที
บทความ
- บทนำ: ถ้าไม่มีเมตริก ปัญหาจะดูเหมือน "ของเราทุกอย่างช้าไปหมด"
- สี่สัญญาณสำหรับพร็อกซี: ความสำเร็จ ความหน่วง การ retry ทราฟฟิก
- เขียนอะไรลง log ต่อคำขอ และเขียนอะไรไม่ได้
- เปอร์เซ็นไทล์แทนค่าเฉลี่ย: ทำไมค่าเฉลี่ยซ่อนปัญหา
- การติดป้ายตามมิติ: ประเทศ โอเปอเรเตอร์ ประเภทพร็อกซี
- การแจ้งเตือนที่ไม่รบกวน: เกณฑ์ หน้าต่าง และ hysteresis
- วินิจฉัยเร็วบนแดชบอร์ด: สามรูปแบบที่พบบ่อย
- ข้อผิดพลาดที่พบบ่อยซึ่งลบล้างประโยชน์ของการสังเกตการณ์
- สแตกขั้นต่ำโดยไม่ซับซ้อนเกินไป
- เคส: การสังเกตการณ์ในทางปฏิบัติเป็นอย่างไร
- Faq: คำถามที่พบบ่อยเกี่ยวกับการสังเกตการณ์พูลพร็อกซี
- บทสรุป: หนึ่งนาทีแทนหนึ่งชั่วโมง
บทนำ: ถ้าไม่มีเมตริก ปัญหาจะดูเหมือน "ของเราทุกอย่างช้าไปหมด"
ฉากที่คุ้นเคย ในแชททีมมีข้อความขึ้นว่า "พร็อกซีช้า" ห้านาทีต่อมา ข้อความที่สอง: "และ error เยอะด้วย" จากนั้นสิ่งที่วิศวกรเรียกกันเองว่าการขุดซากก็เริ่มขึ้น ใครสักคนเปิดคอนโซลแล้วรัน curl ผ่านที่อยู่สุ่มสามแห่งด้วยมือ ใครอีกคนดูกราฟการใช้งาน CPU ซึ่งไม่เกี่ยวข้องกับพร็อกซีเลย ใครอีกคนเขียนอีเมลถึงฝ่ายสนับสนุนของผู้ให้บริการในทำนองว่า "ของคุณมีอะไรไม่ถูกต้อง" โดยไม่มีตัวเลขสักตัวในมือ ผ่านไปหนึ่งชั่วโมง ปรากฏว่าเสื่อมประสิทธิภาพเพียงหนึ่งเซกเมนต์ของพูล ในประเทศเดียว และเริ่มมาตั้งแต่สองวันก่อน แค่ไม่มีใครสังเกต
ความต่างระหว่างทีมที่ใช้เวลาหนึ่งชั่วโมงกับเรื่องแบบนี้ กับทีมที่ใช้เวลาหนึ่งนาที ไม่ได้อยู่ที่ประสบการณ์หรือพรสวรรค์ ความต่างอยู่ที่ทีมหลังตัดสินใจล่วงหน้าว่าจะวัดอะไร จะเขียนอะไรลง log และจะตั้งการแจ้งเตือนที่จุดไหน เมื่อคุณมีกราฟอัตราความสำเร็จของคำขอแยกตามประเทศและโอเปอเรเตอร์ ประโยค "ทุกอย่างช้าไปหมด" จะกลายเป็นข้อความที่ชัดเจนว่า "ตั้งแต่ 14:32 น. ค่า p95 ของเวลาถึงไบต์แรกผ่านพร็อกซีมือถือของโอเปอเรเตอร์ A ในภูมิภาค B เพิ่มจาก 1.8 เป็น 6.4 วินาที อัตราข้อผิดพลาดไม่เปลี่ยน แต่อัตราการ retry เพิ่มขึ้นสามเท่า" ด้วยข้อความแบบนี้ก็ทำงานต่อได้แล้ว: กระจายโหลดใหม่ เปิด ticket ถึงผู้ให้บริการพร้อมข้อมูลแทนอารมณ์ หรือเข้าใจว่าปัญหาไม่ได้อยู่ที่พร็อกซีเลย แต่อยู่ที่ปลายทาง
ในคู่มือนี้ เราจะสำรวจการสังเกตการณ์รอบพูลพร็อกซีตั้งแต่ต้นจนจบ คุณจะได้รู้ว่าสัญญาณสี่อย่างให้ข้อมูลที่มีประโยชน์เกือบทั้งหมดอย่างไร ฟิลด์ใดต้องมีในทุกบรรทัดของ log และอะไรที่ไม่ควรอยู่ในนั้นเด็ดขาด ทำไมเวลาเฉลี่ยถึงโกหก และจะคำนวณเปอร์เซ็นไทล์ให้ถูกต้องได้อย่างไร จะติดป้ายข้อมูลตามมิติเพื่อมองเห็นความเสื่อมประสิทธิภาพของสไลซ์เฉพาะได้อย่างไร จะสร้างการแจ้งเตือนที่ทำงานน้อยแต่ตรงประเด็นได้อย่างไร และจะแยกสามรูปแบบปัญหาบนแดชบอร์ดได้ในหนึ่งนาทีได้อย่างไร ปิดท้ายด้วยสแตกเทคโนโลยีขั้นต่ำ เคสจริงพร้อมตัวเลข และตอบคำถามที่พบบ่อย
ขอบเขตชัดเจนตั้งแต่แรก การเลือก IP เจาะจงจากพูล การตรวจสอบสุขภาพพร็อกซี และการกักกันที่อยู่ที่เสียหาย เราจะไม่ลงรายละเอียดในที่นี้ เพราะมีบทความแยกต่างหาก ที่นี่พูดถึงเพียงสองเรื่อง: การวัดและการแจ้งเตือน ทุกสิ่งที่อธิบายด้านล่างใช้ได้กับสถานการณ์วิศวกรรมที่ถูกกฎหมาย: การมอนิเตอร์ราคาบนแพลตฟอร์มสาธารณะ การตรวจสอบการแสดงโฆษณาในแต่ละภูมิภาค การทดสอบเนื้อหาที่ขึ้นกับภูมิศาสตร์ของบริการตัวเอง และการเก็บข้อมูลเปิดโดยปฏิบัติตามกฎของแหล่งข้อมูล
พื้นฐานเล็กน้อย: การสังเกตการณ์ในบริบทของพร็อกซีคืออะไร
คำจำกัดความแบบคลาสสิกบอกว่า การสังเกตการณ์ (observability) คือคุณสมบัติของระบบที่ทำให้เข้าใจสถานะภายในจากเอาต์พุตภายนอกได้ แปลเป็นภาษา engineering: คุณสามารถถามคำถามใหม่กับระบบที่ไม่ได้เตรียมไว้ล่วงหน้า และได้คำตอบโดยไม่ต้อง deploy โค้ดใหม่ การมอนิเตอร์ตอบคำถามที่รู้คำตอบอยู่แล้ว ("บริการยังอยู่ไหม?") การสังเกตการณ์ตอบคำถามที่ไม่รู้ ("ทำไมคำขอไปยังโดเมนนี้ผ่านที่อยู่มือถือถึงช้าลงเฉพาะวันอังคาร?"))
เสาหลักสามอย่างของการสังเกตการณ์เป็นที่รู้จักดี: เมตริก (อนุกรมเวลาตัวเลข เก็บถูก รวมง่าย) log (เหตุการณ์พร้อมบริบท เก็บแพง แต่มีรายละเอียด) และ trace (เส้นทางของคำขอหนึ่งผ่านคอมโพเนนต์ต่างๆ) สำหรับพูลพร็อกซี ภาพจะเฉพาะตัวขึ้นเล็กน้อย ระหว่างโค้ดของคุณกับเว็บปลายทางมีโครงสร้างพื้นฐานภายนอกที่คุณควบคุมไม่ได้: ที่อยู่ขาออก ผู้ให้บริการเครือข่าย เส้นทางไปยังปลายทาง คุณไม่สามารถติดตั้ง agent บนเสาสัญญาณมือถือได้ ดังนั้นจุดเดียวที่คุณวัดทุกอย่างได้คือไคลเอนต์ของคุณเองที่ส่งคำขอผ่านพร็อกซี ทุกคำขอผ่านพูลควรทิ้งร่องรอยสองอย่างไว้: การเพิ่มขึ้นของเมตริกและบรรทัด log แบบมีโครงสร้าง trace มีประโยชน์แต่เกินความจำเป็นสำหรับทีมส่วนใหญ่ และเราจะกลับมาพูดถึงเฉพาะใน FAQ
ทำไมเรื่องนี้ถึงสำคัญเฉพาะกับพร็อกซี? เพราะความเสื่อมประสิทธิภาพของพร็อกซีแทบไม่เคยปรากฏเป็นการล่ม เซิร์ฟเวอร์ไม่ตอบสนอง — นั่นง่าย แต่พร็อกซี "ช้าลงเล็กน้อย" "ให้ connection error บ่อยขึ้นนิดหน่อย" "บางครั้งตัดการดาวน์โหลดกลางทาง" อาการแต่ละอย่างแยกกันต่ำกว่าเกณฑ์ที่คนจะสนใจ แต่รวมกันแล้วทำให้ต้นทุนการเก็บข้อมูลเพิ่มเป็นสองเท่าและทำลายกำหนดการ การสังเกตความเสื่อมประสิทธิภาพแบบช้าๆ ทำได้ด้วยการวัดเท่านั้น
สี่สัญญาณสำหรับพร็อกซี: ความสำเร็จ ความหน่วง การ retry ทราฟฟิก
ในทางปฏิบัติด้าน reliability engineering มีแนวคิด "สัญญาณทอง" (golden signals): ความหน่วง ทราฟฟิก ข้อผิดพลาด และความอิ่มตัว สำหรับพูลพร็อกซี เราปรับแนวคิดนี้และได้สี่สัญญาณที่เพียงพอสำหรับ 90% ของสถานการณ์ ทำไมต้องสี่ไม่ใช่ยี่สิบ? เพราะทุกกราฟที่เพิ่มบนแดชบอร์ดจะลดโอกาสที่เจ้าหน้าที่เวรจะดูกราฟที่ถูกต้อง สี่สัญญาณคือสิ่งที่พอดีกับหนึ่งบรรทัดบนหน้าจอและอ่านได้ในสิบวินาที
สัญญาณที่ 1: อัตราความสำเร็จของคำขอ
อัตราความสำเร็จ (success rate) คืออัตราส่วนของคำขอที่จบด้วยผลลัพธ์ที่มีประโยชน์ต่อจำนวนคำขอทั้งหมดในช่วงเวลาหนึ่ง คำสำคัญคือ "ที่มีประโยชน์" ความสำเร็จสำหรับพร็อกซีไม่ใช่แค่ HTTP 200 ความสำเร็จคือเมื่อปลายทางตอบสิ่งที่คุณคาดหวังและคำตอบมาครบ การตัด connection กลางเนื้อหาไม่ใช่ความสำเร็จ แม้ว่าเฮดเดอร์จะมีโค้ด 200 โค้ด 403 จากเว็บปลายทางก็ไม่ใช่ความสำเร็จ แต่นั่นอาจไม่ใช่ความผิดของพร็อกซี และเราแยกแยะด้วยการจำแนกผลลัพธ์
กฎปฏิบัติ: จำแนกผลลัพธ์ของทุกคำขอเป็นหมวดหมู่จำนวนไม่มาก ตัวอย่าง: ok (ได้คำตอบที่คาดหวัง) proxy_connect_error (เชื่อมต่อพร็อกซีไม่ได้) proxy_auth_error (พร็อกซีปฏิเสธการยืนยันตัวตน) upstream_timeout (พร็อกซีตอบ แต่ปลายทางไม่ตอบทันเวลา) upstream_4xx upstream_5xx truncated (connection ถูกตัดก่อนจบเนื้อหา) การจำแนกนี้เป็นฐานสำหรับการวิเคราะห์ทั้งหมดต่อไป อัตราความสำเร็จคำนวณเป็น ok ต่อทั้งหมด แต่แยกดูอัตราของแต่ละหมวดข้อผิดพลาด เพราะมันชี้ไปที่ผู้รับผิดชอบต่างกัน
สัญญาณที่ 2: ความหน่วงตามเปอร์เซ็นไทล์
ความหน่วงสำหรับพร็อกซีควรวัดสามจุด: เวลาสร้าง connection กับพร็อกซี เวลาถึงไบต์แรกของคำตอบจากปลายทาง (TTFB, time to first byte) และเวลารวมของคำขอ อย่างแรกบอกถึงความพร้อมใช้งานของโหนดพร็อกซี อย่างที่สองบอกถึงคุณภาพเส้นทางจากพร็อกซีไปยังปลายทางและความเร็วของปลายทาง อย่างที่สามบอกถึงแบนด์วิดท์ ถ้าแค่ตัวที่สามเพิ่มขึ้นแต่สองตัวแรกคงที่ ปัญหาอยู่ที่แบนด์วิดท์ ไม่ใช่ความพร้อมใช้งาน ถ้าตัวแรกเพิ่ม ปัญหาอยู่ระหว่างคุณกับพร็อกซี ความหน่วงต้องคำนวณเป็นเปอร์เซ็นไทล์ ไม่ใช่ค่าเฉลี่ย และมีหัวข้อแยกด้านล่าง
สัญญาณที่ 3: อัตราการ retry
อัตราการ retry (retry rate) เป็นสัญญาณที่ถูกมองข้ามมากที่สุด ไคลเอนต์ส่วนใหญ่เขียนไว้ให้ retry คำขอโดยอัตโนมัติผ่านที่อยู่อื่นเมื่อเกิดข้อผิดพลาด นั่นถูกต้องในแง่ reliability และแย่มากในแง่ observability: อัตราความสำเร็จคงอยู่ที่ 99% แต่ภายใต้ hood ทุกคำขอทำงานสามครั้ง คุณจ่ายค่าทราฟฟิกสามเท่า ปลายทางได้รับโหลดสามเท่า และบนแดชบอร์ดทุกอย่างเขียว อัตราการ retry เปิดเผยความเสื่อมประสิทธิภาพที่ซ่อนอยู่ก่อนจะกลายเป็นความเสื่อมที่มองเห็นได้ คำนวณเป็นอัตราส่วนจำนวน attempt ต่อจำนวนคำขอเชิงตรรกะ: ค่า 1.0 หมายถึงไม่มีการ retry, 1.5 หมายถึงทุกคำขอที่สองถูก retry
สัญญาณที่ 4: ปริมาณการใช้ทราฟฟิก
ปริมาณการใช้ทราฟฟิกเป็นสัญญาณที่เชื่อมโยงกับเงินโดยตรง เพราะหลายแพ็กเกจรวมถึงแพ็กเกจของ Proxeon คิดตามปริมาณข้อมูลที่ส่ง แต่มันยังมีบทบาทในการวินิจฉัย การเพิ่มขึ้นอย่างรวดเร็วของไบต์ต่อคำขอที่สำเร็จหนึ่งคำขอหมายความว่าปลายทางเริ่มส่งหน้าที่ต่างออกไป: หน้า placeholder หนักๆ การ redirect ไปส่วนอื่น โครงสร้างที่เปลี่ยนไป การลดลงอย่างรวดเร็วหมายความว่าแทนที่จะได้หน้าเต็ม คุณได้คำตอบข้อผิดพลาดสั้นๆ นับทราฟฟิกทั้งสองทิศทางและปรับด้วยจำนวนคำขอที่สำเร็จเสมอ: "กิโลไบต์ต่อคำขอที่สำเร็จ" เป็นเมตริกที่มีข้อมูลมากกว่า "กิกะไบต์ต่อชั่วโมง" มาก
หน้าตาในโค้ดเป็นอย่างไร
ด้านล่างเป็นชุดเมตริกขั้นต่ำสำหรับ Prometheus ใน Python สังเกตชุด label: pool, country, operator, proxy_type ที่อยู่ของพร็อกซีเฉพาะเจาะจงไม่อยู่ใน label โดยเจตนา และเราจะอธิบายในหัวข้อว่าด้วยมิติ
from prometheus_client import Counter, Histogram
LABELS = ["pool", "country", "operator", "proxy_type"]
REQUESTS = Counter("proxy_requests_total", "Логические запросы через пул", LABELS + ["outcome"])
ATTEMPTS = Counter("proxy_attempts_total", "Попытки, включая ретраи", LABELS + ["attempt_kind"])
TTFB = Histogram("proxy_ttfb_seconds", "Время до первого байта", LABELS, buckets=(0.1, 0.25, 0.5, 1, 2, 4, 8, 16, 30))
CONNECT = Histogram("proxy_connect_seconds", "Установление соединения с прокси", LABELS, buckets=(0.05, 0.1, 0.25, 0.5, 1, 2, 5))
BYTES = Counter("proxy_bytes_total", "Байты через пул", LABELS + ["direction"])
def observe(ctx, outcome, ttfb, connect, bytes_in, bytes_out, attempt):
base = dict(pool=ctx.pool, country=ctx.country, operator=ctx.operator, proxy_type=ctx.proxy_type)
kind = "first" if attempt == 1 else "retry"
ATTEMPTS.labels(**base, attempt_kind=kind).inc()
if outcome in ("ok", "upstream_4xx", "upstream_5xx", "truncated", "upstream_timeout"):
TTFB.labels(**base).observe(ttfb)
CONNECT.labels(**base).observe(connect)
BYTES.labels(**base, direction="in").inc(bytes_in)
BYTES.labels(**base, direction="out").inc(bytes_out)
if outcome == "ok" or attempt == ctx.max_attempts:
REQUESTS.labels(**base, outcome=outcome).inc()สังเกตรายละเอียด: ตัวนับคำขอเชิงตรรกะเพิ่มขึ้นเฉพาะเมื่อสำเร็จหรือ嘗試ครบแล้วเท่านั้น วิธีนี้อัตราความสำเร็จสะท้อนผลลัพธ์สำหรับผู้ใช้โค้ดของคุณ ส่วนตัวนับ attempt สะท้อนงานจริงของพูล ความต่างระหว่างทั้งสองคืออัตราการ retry
ตาราง: เมตริก ความหมายเมื่อเพิ่มขึ้น การดำเนินการแรก
| เมตริก | ความหมายเมื่อเพิ่มขึ้น | การดำเนินการแรก |
|---|---|---|
| อัตรา proxy_connect_error | โหนดพร็อกซีไม่พร้อมใช้หรือโอเวอร์โหลด ปัญหาระหว่างคุณกับผู้ให้บริการ | ตรวจสอบช่องทางขาออกและ DNS ของตัวเอง แล้วเทียบตามสไลซ์: ถ้าทั้งพูล ให้หาสาเหตุที่ตัวเอง ถ้าแค่สไลซ์เดียว ให้แจ้งผู้ให้บริการพร้อมข้อมูล |
| อัตรา proxy_auth_error | สิทธิ์การเข้าถึงหมดอายุ ข้อมูลรับรองเปลี่ยน เกินขีดจำกัดการเชื่อมต่อ | ตรวจสอบความถูกต้องของข้อมูลรับรองและขีดจำกัดในแผงผู้ให้บริการ อย่า retry คำขอแบบนี้ |
| อัตรา upstream_timeout | ปลายทางตอบช้าผ่านเส้นทางนี้ หรือพร็อกซีตามไม่ทัน | เทียบ TTFB ตามโฮสต์ปลายทาง: ถ้าโฮสต์เดียว ปัญหาปลายทาง ถ้าทั้งหมด ปัญหาเส้นทางพร็อกซี |
| อัตรา upstream_4xx (ยกเว้น 404) | ปลายทางจำกัดความเข้มข้นหรือเปลี่ยนกฎการเข้าถึง | ลดความถี่คำขอไปยังแหล่งข้อมูล ตรวจสอบว่าสอดคล้องกับกฎการใช้งาน เพิ่ม retry ไม่ช่วย |
| อัตรา upstream_5xx | ปัญหาฝั่งปลายทาง | รอ ลดโหลด พร็อกซีไม่น่าเกี่ยว |
| อัตรา truncated | connection ถูกตัดกลางทาง: ช่องทางไม่เสถียร read timeout สั้นเกินไป | ตรวจสอบ read timeout และขนาดคำตอบ เทียบตามโอเปอเรเตอร์ |
| p95 เวลาสร้าง connection กับพร็อกซี | ความพร้อมใช้งานของโหนดพร็อกซีเสื่อม หรือ network stack ของคุณมีปัญหา | ตรวจสอบจำนวน connection พร้อมกันและ port exhaustion ฝั่งตัวเอง |
| p95 TTFB ขณะเวลาสร้าง connection คงที่ | เส้นทางจากพร็อกซีไปปลายทางช้าลง หรือปลายทางช้าเอง | แยกตามโฮสต์ปลายทางและตามสไลซ์พูล |
| p99 เวลารวม ขณะ TTFB คงที่ | แบนด์วิดท์ช่องทางพร็อกซีลดลง หรือคำตอบใหญ่ขึ้น | ดูไบต์ต่อคำขอ ถ้าขนาดไม่เปลี่ยน ปัญหาแบนด์วิดท์ |
| อัตราการ retry | ความเสื่อมที่ซ่อนอยู่: ความสำเร็จคงไว้ด้วยการ retry ต้นทุนเพิ่ม | หาว่าหมวดข้อผิดพลาดใดทำให้เกิดการ retry แล้วจัดการ |
| ไบต์ต่อคำขอที่สำเร็จ | ปลายทางส่งเนื้อหาอื่น: placeholder, redirect, โครงสร้างเปลี่ยน | ตรวจสอบเนื้อหาคำตอบในตัวอย่าง อัปเดต parser และ validation |
| ทราฟฟิกรวมขณะจำนวนคำขอคงที่ | การ retry หรือขนาดคำตอบเพิ่ม กระแทกงบประมาณโดยตรง | แยกปัจจัยสองอย่าง: จำนวน attempt และไบต์ต่อ attempt |
เขียนอะไรลง log ต่อคำขอ และเขียนอะไรไม่ได้
เมตริกตอบคำถาม "เกิดอะไรขึ้น" log ตอบคำถาม "กับอะไรกันแน่" เมื่อแดชบอร์ดแสดงข้อผิดพลาดเพิ่มในสไลซ์ "ประเทศ X โอเปอเรเตอร์ Y" คุณต้องเข้าใจว่าที่อยู่ทั้งหมดในสไลซ์นั้นผิด หรือแค่สองจากสามสิบ เมตริกที่มี label ตามที่อยู่จะระเบิดที่เก็บข้อมูล แต่ log ไม่ เพราะ log ไม่ได้ถูกรวมตลอดเวลาแต่ว่าง่ายต่อการกรองตามคำขอ
ฟิลด์บังคับของบรรทัด log
ทุก attempt ของคำขอผ่านพูลควรสร้างหนึ่ง record แบบมีโครงสร้าง ในรูปแบบ JSON จะดี นี่คือชุดฟิลด์ที่เราเห็นว่าเพียงพอขั้นต่ำ:
- ts — timestamp ใน UTC พร้อมมิลลิวินาที ไม่ใช่เวลาท้องถิ่น ไม่มีข้อยกเว้น
- request_id — ตัวระบุคำขอเชิงตรรกะ ใช้ร่วมกันทุก attempt ช่วยรวบรวมประวัติทั้งหมดของคำขอหนึ่งด้วยคำสั่งเดียว
- attempt และ max_attempts — หมายเลข attempt และขีดจำกัด ถ้าไม่มีหมายเลข attempt คุณจะแยกความผิดพลาดครั้งแรกจากครั้งที่สามไม่ได้
- proxy_id — ตัวระบุภายในของพร็อกซี ไม่ใช่ที่อยู่พร้อมพอร์ตและไม่ใช่ connection string ตัวระบุภายในที่คุณจับคู่กับที่อยู่จริงผ่านตารางที่ป้องกันแยกต่างหาก
- proxy_type country operator pool — มิติเดียวกับในเมตริก ช่วยให้กระโดดระหว่างแดชบอร์ดและ log ได้โดยไม่ต้องคำนวณใหม่
- target_host — ชื่อโดเมนของปลายทาง ไม่ใช่ URL เต็ม เพราะใน query string อาจมี token และข้อมูลส่วนบุคคล
- method — HTTP method
- status — HTTP code ของคำตอบ ถ้ามี ไม่งั้น null
- outcome — การจำแนกผลลัพธ์ที่พูดถึงข้างบน
- error_class — ชั้นของ exception หรือรหัสข้อผิดพลาดของ transport แบบสั้น ถ้าผลลัพธ์ไม่ใช่ ok
- connect_ms ttfb_ms total_ms — ความหน่วงสามค่าในมิลลิวินาที
- bytes_in bytes_out — ขนาดเนื้อหาคำตอบและขนาดคำขอ
- session_id — ถ้าใช้ sticky session ตัวระบุ session จะเห็นว่าข้อผิดพลาดกระจุกตัวใน session ยาว
- retry_reason — สำหรับ attempt ตั้งแต่ครั้งที่สอง: ผลลัพธ์ของ attempt ก่อน
ตัวอย่างบรรทัด:
{"ts":"2026-03-14T11:32:07.412Z","request_id":"7f3a9c1e","attempt":2,"max_attempts":3,"retry_reason":"upstream_timeout","proxy_id":"px-2841","proxy_type":"mobile","country":"RU","operator":"op_a","pool":"pricing","target_host":"shop.example","method":"GET","status":200,"outcome":"ok","error_class":null,"connect_ms":184,"ttfb_ms":1420,"total_ms":2310,"bytes_in":48211,"bytes_out":612,"session_id":"s-19b"}หนึ่งบรรทัดแบบนี้หนักประมาณ 400 ไบต์ ที่ 50 คำขอต่อวินาที คิดเป็นประมาณ 1.7 กิกะไบต์ต่อวันโดยไม่บีบอัด และ 200–300 เมกะไบต์เมื่อบีบอัด จัดการได้สบายแม้กับงบประมาณจำกัด
สิ่งที่ต้องไม่มีใน log
ตรงนี้เราย้ายจากวิศวกรรมไปสู่ความรับผิดชอบ รายการสั้นแต่ทุกข้อสำคัญ:
- ข้อมูลรับรองการเข้าถึงพร็อกซี การรั่วไหลที่พบบ่อยที่สุดคือ connection string รูปแบบ http://login:password@host:port ที่ попадаетใน log ทั้งบรรทัด เพราะนักพัฒนาติ๊ก log config "เพื่อ debug" log ถูกคัดลอกไปใน ticket ส่งต่อให้เพื่อนร่วมงาน เก็บไว้นานหลายปี ข้อมูลรับรองในนั้นคือข้อมูลรับรองที่ทุกคนที่เคยเห็น log เข้าถึงได้
- เฮดเดอร์ Proxy-Authorization และเฮดเดอร์ยืนยันตัวตนอื่นๆ รวมถึงเฮดเดอร์ไปยังปลายทาง
- เนื้อหาคำตอบ มันใหญ่มาก อาจมีข้อมูลส่วนบุคคลของบุคคลที่สาม และแทบไม่เคยจำเป็นสำหรับการวินิจฉัยพร็อกซี ถ้าจำเป็นต้องรู้ว่าปลายทางส่งอะไร ให้เก็บ hash ของเนื้อหาและความยาว ส่วนคำตอบเองแยกไปที่เก็บต่างหากที่มีอายุสั้นและเก็บเฉพาะตัวอย่าง
- Cookies ทั้งสองทิศทาง
- URL เต็มพร้อม query parameters ชื่อโฮสต์ก็พอ และถ้าจำเป็น path ที่ไม่มี parameter ก็ได้
- ที่อยู่จริงของพร็อกซีในรูปแบบเปิดเผย ถ้า log ไปยังที่เก็บส่วนกลาง ตัวระบุภายในก็เพียงพอ
แนวปฏิบัติที่เราแนะนำ: implement การแก้ไขที่ระดับฟังก์ชันเดียวที่ทุก record ที่เกี่ยวกับพร็อกซีต้องผ่าน อย่าพึ่งวินัยของนักพัฒนา
import re
CRED_IN_URL = re.compile(r"(https?|socks5h?)://[^/@:]+:[^/@]+@")
SENSITIVE_HEADERS = {"proxy-authorization", "authorization", "cookie", "set-cookie"}
def redact(record: dict) -> dict:
for key, value in list(record.items()):
if key.lower() in SENSITIVE_HEADERS:
record[key] = "[redacted]"
elif isinstance(value, str):
record[key] = CRED_IN_URL.sub(lambda m: m.group(1) + "://[redacted]@", value)
record.pop("body", None)
return recordการสุ่มตัวอย่างและการเก็บรักษา
ต้องเขียนทุก attempt ไหม? ข้อผิดพลาด — ใช่ ทั้งหมด attempt ที่สำเร็จ — สุ่มได้: ที่ความเข้มข้นเกินร้อยคำขอต่อวินาที การเก็บ 5–10% ของบรรทัดที่สำเร็จก็เพียงพอสำหรับประเมินความหน่วง เพราะเมตริกมีภาพครบอยู่แล้ว สำคัญที่การตัดสินใจสุ่มต้องอิงตาม request_id ไม่ใช่สุ่มแยกแต่ละ attempt ไม่งั้นคุณจะได้ attempt ที่สองโดยไม่มีที่หนึ่ง อายุการเก็บ log รายละเอียดมัก 7–14 วัน ส่วน aggregate และเมตริกอยู่นานกว่า
เปอร์เซ็นไทล์แทนค่าเฉลี่ย: ทำไมค่าเฉลี่ยซ่อนปัญหา
ลองนึกภาพพูล 100 ที่อยู่ 90 แห่งตอบใน 0.8 วินาที 10 แห่งตอบใน 9 วินาที เพราะไปติดสถานีฐานที่โอเวอร์โหลด เวลาตอบเฉลี่ยคือ 1.62 วินาที เมื่อสัปดาห์ก่อนตอนทุกอย่างดี ค่าเฉลี่ยคือ 1.4 เพิ่มขึ้น 15% ใครจะตอบสนองต่อสิ่งนี้? ไม่มีใคร แต่ในขณะเดียวกัน ทุกคำขอที่สิบของคุณรอเก้าวินาที และถ้าคุณมี timeout ที่แปด ทุกคำขอที่สิบจะล้มเหลวด้วยข้อผิดพลาด
ค่าเฉลี่ยเลขคณิตอธิบายการกระจายที่คล้ายระฆังได้ดี ความหน่วงผ่านพร็อกซีไม่เคยคล้ายระฆัง มันคือการกระจายที่มีหางขวายาว: คำขอส่วนใหญ่เร็ว แต่บางส่วนช้ามาก และนั่นแหละที่กำหนดประสบการณ์ผู้ใช้และต้นทุน ยิ่งไปกว่านั้น ความหน่วงผ่านพูลมักเป็นแบบ bimodal: มี hump "ปกติ" และ hump "ที่อยู่มีปัญหา" ค่าเฉลี่ยตกลงระหว่างทั้งสอง ตรงจุดที่แทบไม่มีคำขอจริง
p50, p95, p99 คืออะไร
เปอร์เซ็นไทล์ คือค่าที่ต่ำกว่าซึ่งมีสัดส่วนที่กำหนดของ observations p50 (median) — ครึ่งหนึ่งของคำขอเร็วกว่าค่านี้ p95 — 95% เร็วกว่า 5% ช้ากว่า p99 — มีเพียงหนึ่งเปอร์เซ็นต์ที่ช้ากว่า สำหรับพูลพร็อกซีเราแนะนำดูสามเปอร์เซ็นไทล์ด้วยจุดประสงค์ต่างกัน:
- p50 แสดงคำขอ "ทั่วไป" ถ้า p50 เพิ่ม แปลว่าส่วนใหญ่เสื่อมแล้ว นี่เป็นสัญญาณที่พบยากและรุนแรง
- p95 — เมตริกทำงานสำหรับการแจ้งเตือน ไวต่อเซกเมนต์ที่มีปัญหา (ในตัวอย่างข้างบน p95 จะแสดง 9 วินาทีทันที) แต่ทนต่อ outlier เดี่ยวๆ
- p99 — เมตริกสำหรับ timeout และการวางแผน timeout ของคุณควรสูงกว่า p99 ที่ "สุขภาพดี" เล็กน้อย ไม่งั้นคุณจะตัดคำขอปกติและเปลี่ยนมันเป็นการ retry
วิธีคำนวณแบบแม่นยำ
ถ้าข้อมูลน้อย (พันค่าในช่วง) คำนวณแม่นยำได้: เรียงลำดับแล้วเอาองค์ประกอบตามดัชนี
def percentile(values, p):
if not values:
return None
ordered = sorted(values)
k = (len(ordered) - 1) * p / 100.0
lo = int(k)
hi = min(lo + 1, len(ordered) - 1)
return ordered[lo] + (ordered[hi] - ordered[lo]) * (k - lo)
ttfb_window = [0.8, 0.9, 0.7, 9.1, 0.85, 8.7, 0.78, 0.92, 0.81, 0.79]
print(percentile(ttfb_window, 50), percentile(ttfb_window, 95))การคำนวณแม่นยำไม่สเกล: การคำนวณ p95 ต่อสัปดาห์จากหลายล้านคำขอต้องเก็บค่าทั้งหมด ดังนั้นระบบอุตสาหกรรมใช้โครงสร้างแบบประมาณ
วิธีคำนวณในระดับสเกล: ฮิสโตแกรม
Prometheus และระบบที่เข้ากันได้เก็บฮิสโตแกรมเป็นชุดตัวนับต่อ bucket: มี observations กี่ตัวที่น้อยกว่า 0.1 วินาที น้อยกว่า 0.25 วินาที น้อยกว่า 0.5 วินาที เป็นต้น เปอร์เซ็นไทล์ถูกสร้างคืนด้วยการ interpolate เชิงเส้นภายใน bucket ความแม่นยำขึ้นกับการเลือกขอบเขต bucket สำหรับความหน่วงผ่านพร็อกซี สเกลลอการิทึมจาก 100 มิลลิวินาทีถึง 30 วินาทีใช้ได้ดี: 0.1; 0.25; 0.5; 1; 2; 4; 8; 16; 30 ถ้า p95 ที่สุขภาพดีของคุณประมาณ 2 วินาที และ timeout 15 ตารางนี้ให้ความคลาดเคลื่อนในระดับ 20–30% บนหาง ซึ่งเพียงพอสำหรับการแจ้งเตือนและการวินิจฉัย
histogram_quantile(0.95,
sum by (le, country, operator) (
rate(proxy_ttfb_seconds_bucket{pool="pricing"}[5m])
)
)สังเกต sum by (le, ...) ข้างใน นี่เป็นรายละเอียดสำคัญยิ่ง: ฮิสโตแกรมของ instance ต่างๆ ของไคลเอนต์ต้องรวมกันก่อนแล้วจึงคำนวณ quantile การดำเนินการย้อนกลับ — คำนวณ p95 ที่แต่ละ instance แล้วเฉลี่ย — ไม่มีความหมายทางคณิตศาสตร์ ค่าเฉลี่ยของเปอร์เซ็นไทล์ไม่ใช่เปอร์เซ็นไทล์ นี่เป็นข้อผิดพลาดที่พบบ่อยที่สุดอย่างหนึ่ง และสามารถซ่อนปัญหาได้แนบเนียนพอๆ กับค่าเฉลี่ยธรรมดา
ถ้าต้องการความแม่นยำสูงบนหาง (เช่น ขาย SLA ความหน่วงให้ลูกค้าภายใน) ใช้โครงสร้างแบบ t-digest หรือ HDR histogram ที่ให้ความแม่นยำระดับเศษเสี้ยวเปอร์เซ็นต์ด้วยหน่วยความจำคงที่ สำหรับทีมส่วนใหญ่ ฮิสโตแกรมมาตรฐานก็เพียงพอ
การติดป้ายตามมิติ: ประเทศ โอเปอเรเตอร์ ประเภทพร็อกซี
นี่คือสถานการณ์ที่แสดงให้เห็นว่าทำไมต้องมีมิติ คุณมีพูล 400 ที่อยู่ 5% เป็นที่อยู่มือถือของโอเปอเรเตอร์เดียวในภูมิภาคเดียว เซกเมนต์นี้เสื่อมอย่างสมบูรณ์: อัตราความสำเร็จตกเหลือ 10% กราฟอัตราความสำเร็จรวมของพูลจะแสดงอะไร? ตกจาก 99% เป็น 94.5% สังเกตได้แต่ไม่วิกฤต เจ้าหน้าที่เวรจะคิดว่า "เน็ตมีปัญหา" แล้วไปต่อ แต่ลูกค้าข้อมูลจากภูมิภาคนี้จะได้ความว่างเปล่า
การติดป้ายตามมิติ (ในศัพท์ Prometheus เรียกว่า label) แก้ปัญหานี้ตรงๆ: ช่วยให้แยก aggregate เป็นสไลซ์และเห็นว่าปัญหาจำกัดอยู่ที่ไหน แต่ label มีราคา และต้องเข้าใจ
เลือกมิติใด
มิติที่ดีมีคุณสมบัติสองอย่าง: มีจำนวนค่าจำกัดไม่มาก และการแบ่งตามมิตินั้นตอบคำถามการวินิจฉัยจริง สำหรับพูลพร็อกซีเราแนะนำสี่มิติในเมตริก:
- proxy_type — มือถือ, residential, data center ประเภทต่างๆ เสื่อมไม่เหมือนกันและมีต้นทุนต่างกัน
- country — ประเทศของที่อยู่ขาออก โดยทั่วไป 1 ถึง 30 ค่า
- operator — ผู้ให้บริการเครือข่ายหรือ autonomous system ที่ที่อยู่ผ่านออก ปัญหามักจำกัดอยู่ตรงนี้: สถานีฐานที่โอเวอร์โหลดหนึ่งแห่ง หรือ peering ที่แย่ของโอเปอเรเตอร์หนึ่ง
- pool — วัตถุประสงค์เชิงตรรกะ: ผลิตภัณฑ์หรือ task ใดใช้เซกเมนต์นี้ ช่วยแยกเหตุการณ์ตามผลกระทบทางธุรกิจ
มิติที่ห้าที่ควรคิดถึงคือ target_host โฮสต์ปลายทาง จำเป็นเพื่อแยกปัญหาพร็อกซีจากปัญหาปลายทาง แต่ถ้าคุณไปยังหลายพันโดเมน cardinality จะระเบิด ทางประนีประนอม: เก็บใน label เฉพาะ 20 โฮสต์อันดับต้นตามปริมาณ ส่วนที่เหลือรวมเป็นค่า other
เลขคณิตของ cardinality
ทุกชุดค่าผสมที่ไม่ซ้ำกันของค่า label คืออนุกรมเวลาแยก ลองคำนวณ: 3 ประเภท × 10 ประเทศ × 15 โอเปอเรเตอร์ × 4 พูล × 7 outcome = 12,600 อนุกรมแค่สำหรับตัวนับคำขอ ฮิสโตแกรมที่มี 9 bucket จะคูณจำนวนอนุกรมของมันด้วย 10 รวมประมาณ 50–60 พันอนุกรม สำหรับ Prometheus สบายมาก ทีนี้ลองนึกว่าคุณเพิ่ม label ที่อยู่พร็อกซี (400 ค่า) จำนวนอนุกรมโตขึ้น 400 เท่า — เป็น 20 ล้าน นั่นไม่ใช่ "สบาย" นั่นคือความตายของที่เก็บข้อมูล
จากตรงนี้กฎคือ: ที่อยู่ของพร็อกซีเฉพาะอยู่ใน log ไม่ใช่ในเมตริก เมตริกตอบคำถาม "สไลซ์ไหนเสื่อม" log ตอบ "ที่อยู่ไหนกันแน่ในสไลซ์" การแบ่งงานแบบนี้ให้ทั้งความเร็วและรายละเอียดโดยไม่ทำให้ต้นทุนพุ่ง
Query ที่หาสไลซ์
คำถามหลักของการวินิจฉัย: "ที่ไหนแย่ที่สุด?" ตัวที่ตอบคือการจัดเรียงสไลซ์ตามอัตราความสำเร็จ:
bottomk(5,
sum by (country, operator, proxy_type) (rate(proxy_requests_total{outcome="ok"}[10m]))
/
sum by (country, operator, proxy_type) (rate(proxy_requests_total[10m]))
)คำถามที่สอง: "นี่เป็นปัญหาใหม่หรือเป็นมาตลอด?" ตัวที่ตอบคือการเทียบกับอดีต:
(
sum by (operator) (rate(proxy_requests_total{outcome="ok"}[10m])) / sum by (operator) (rate(proxy_requests_total[10m]))
)
-
(
sum by (operator) (rate(proxy_requests_total{outcome="ok"}[10m] offset 1d)) / sum by (operator) (rate(proxy_requests_total[10m] offset 1d))
)ค่าลบแสดงว่าสไลซ์แย่ลงกว่าหนึ่งวันก่อนแค่ไหน กราฟแบบนี้บนแดชบอร์ดเปลี่ยน "มีอะไรไม่ถูกที่ไหนสักแห่ง" เป็น "โอเปอเรเตอร์ B ในประเทศ C ลบ 12 จุดเทียบกับเมื่อวาน"
ค่าของมิติมาจากไหน
ประเทศและประเภทพร็อกซีมักรู้จาก config ของพูล โอเปอเรเตอร์ได้จาก metadata ของผู้ให้บริการ: เช่น ในแผงและ API ของ Proxeon ที่อยู่มีข้อมูลภูมิศาสตร์และโอเปอเรเตอร์ ซึ่งคุณดึงตอน initialize พูลและ cache ไว้ข้างตัวระบุภายในของพร็อกซี การระบุโอเปอเรเตอร์ ณ เวลาคำขอผ่านฐานข้อมูลภายนอกไม่ควรทำ: จะเพิ่มความหน่วงและสร้าง dependency เกินจำเป็น
การแจ้งเตือนที่ไม่รบกวน: เกณฑ์ หน้าต่าง และ hysteresis
สิ่งที่แย่ที่สุดที่อาจเกิดกับระบบแจ้งเตือนไม่ใช่เหตุการณ์ที่พลาดไป สิ่งที่แย่ที่สุดคือเมื่อเจ้าหน้าที่เวรได้รับ 40 การแจ้งเตือนต่อวัน ซึ่ง 39 ไม่มีอะไร และเลิกอ่านอันที่สี่สิบ การแจ้งเตือนที่ถูกเพิกเฉยแย่กว่าการไม่มีการแจ้งเตือน เพราะสร้างภาพลวงของการควบคุม
หลักการที่ 1: แจ้งเตือนที่อาการ ไม่ใช่ที่สาเหตุ
อย่าตั้งการแจ้งเตือนที่ "พร็อกซี px-2841 ตอบข้อผิดพลาด" ที่อยู่เดียวมีสิทธิ์ไม่สบาย และกลไกกักกันภายในพูลรับผิดชอบเรื่องนั้น ซึ่งเราไม่พูดถึงที่นี่ ตั้งการแจ้งเตือนที่สิ่งที่ผู้บริโภคข้อมูลของคุณรู้สึก: อัตราความสำเร็จของพูลต่ำกว่าเป้า p95 สูงกว่าเป้า retry สูงกว่าเป้า สาเหตุจะหาได้จากแดชบอร์ดในหนึ่งนาที ถ้าทำมิติถูก หน้าที่ของการแจ้งเตือนคือปลุก ไม่ใช่อธิบาย
หลักการที่ 2: ปริมาณทราฟฟิกขั้นต่ำ
อัตราความสำเร็จ 50% กับสองคำขอในห้านาที คือคำขอที่ล้มเหลวหนึ่งครั้ง ไม่ใช่เหตุการณ์ กฎใดที่ว่าด้วยอัตราต้องมีเงื่อนไขปริมาณคำขอขั้นต่ำในช่วงเวลา ไม่งั้นกลางคืนตอนทราฟฟิกลดลง คุณจะจมอยู่กับ false positive
หลักการที่ 3: หน้าต่างและอัตราการเผางบประมาณ
ถ้าคุณกำหนดระดับ reliability เป้าหมาย (เช่น 98% คำขอสำเร็จต่อเดือน) คุณมี error budget: 2% ของคำขอ การแจ้งเตือนที่ดีตอบสนองต่อ อัตราการใช้งบประมาณ ไม่ใช่ค่าทันที แนวปฏิบัติที่เป็นมาตรฐานในวงการ reliability คือการแจ้งเตือนแบบหลายหน้าต่าง: หน้าต่างสั้น (5 นาที) จับการล่มรุนแรง หน้าต่างยาว (1 ชั่วโมง) จับการเสื่อมช้าๆ และทั้งสองต้องเกินพร้อมกันเพื่อตัด spike สั้นๆ ออก
หลักการที่ 4: hysteresis
Hysteresis คือเกณฑ์ที่ต่างกันสำหรับการ trigger และการ clear ถ้าการแจ้งเตือน trigger ที่อัตราต่ำกว่า 95% และ clear เมื่อกลับขึ้นเหนือ 95% เมตริกที่แกว่งรอบ 95% จะสร้างชุดการแจ้งเตือน "trigger–clear–trigger" ทุกสองสามนาที ทางออก: trigger ที่ 95% clear ที่ 97% หรือพูดง่ายกว่า ต้องให้เงื่อนไขคงอยู่ต่อเนื่องตามเวลาที่กำหนด (ใน Prometheus คือ keyword for) และกำหนดดีเลย์การ clear
หลักการที่ 5: ระดับความรุนแรงและการ routing
ไม่ทุกอย่างต้องส่งเป็นสายด่วน แบ่งการแจ้งเตือนเป็นสองสามระดับ: วิกฤต (กระทบผู้บริโภคข้อมูลตอนนี้ — โทรหรือ push ถึงเจ้าหน้าที่เวร) เตือน (สไลซ์เสื่อม retry เพิ่ม ความผิดปกติทราฟฟิก — ข้อความไปช่อง รอจัดการในเวลาทำงาน) ข้อมูล (instance ไม่ส่งข้อมูล ใกล้ถึงขีดจำกัดทราฟฟิก — สรุปรายวัน)
ตัวอย่างชุดกฎ
groups:
- name: proxy-pool
rules:
- record: pool:success_ratio:5m
expr: sum by (pool) (rate(proxy_requests_total{outcome="ok"}[5m])) / sum by (pool) (rate(proxy_requests_total[5m]))
- record: pool:success_ratio:1h
expr: sum by (pool) (rate(proxy_requests_total{outcome="ok"}[1h])) / sum by (pool) (rate(proxy_requests_total[1h]))
- record: pool:request_rate:5m
expr: sum by (pool) (rate(proxy_requests_total[5m]))
- alert: ProxyPoolSuccessLow
expr: pool:success_ratio:5m < 0.95 and pool:success_ratio:1h < 0.97 and pool:request_rate:5m > 0.5
for: 5m
labels: { severity: critical }
annotations:
summary: "Доля успеха по пулу {{ $labels.pool }} ниже цели"
- alert: ProxyPoolLatencyHigh
expr: histogram_quantile(0.95, sum by (le, pool) (rate(proxy_ttfb_seconds_bucket[10m]))) > 4 and pool:request_rate:5m > 0.5
for: 10m
labels: { severity: warning }
- alert: ProxyPoolRetryRateHigh
expr: sum by (pool) (rate(proxy_attempts_total[15m])) / sum by (pool) (rate(proxy_requests_total[15m])) > 1.4
for: 15m
labels: { severity: warning }
- alert: ProxyPoolSliceDegraded
expr: (sum by (pool, country, operator) (rate(proxy_requests_total{outcome="ok"}[15m])) / sum by (pool, country, operator) (rate(proxy_requests_total[15m]))) < 0.85 and sum by (pool, country, operator) (rate(proxy_requests_total[15m])) > 0.2
for: 15m
labels: { severity: warning }
- alert: ProxyPoolNoData
expr: absent(proxy_requests_total) or pool:request_rate:5m == 0
for: 10m
labels: { severity: warning }สังเกตกฎสุดท้าย การไม่มีข้อมูลคือความล้มเหลวที่ร้ายกาจที่สุด: ไคลเอนต์ล่ม ไม่มีเมตริก กราฟแบนและเขียว ทุกคนสบายใจ การแจ้งเตือนความเงียบเป็นสิ่งจำเป็น
Checklist สำหรับการแจ้งเตือนใหม่ทุกครั้ง
- เขียนใน annotation หรือยังว่าผู้รับควรทำอะไร? ถ้าไม่ กฎยังไม่พร้อม
- มีเงื่อนไขปริมาณทราฟฟิกขั้นต่ำไหม?
- มี for หรือ hysteresis ไหม?
- ทดสอบกฎกับประวัติเดือนที่แล้วหรือยัง: จะ trigger กี่ครั้ง และมีกี่ครั้งที่เป็นเหตุการณ์จริง?
- ชัดเจนไหมว่าอยู่ระดับความรุนแรงใดและ route ไปไหน?
- มีกระบวนการย้อนกลับไหม: ถ้าแจ้งเตือนแล้วเป็น false positive ใครจะทบทวนเมื่อไร?
วินิจฉัยเร็วบนแดชบอร์ด: สามรูปแบบที่พบบ่อย
แดชบอร์ดไม่ใช่หอศิลป์ของกราฟ แต่เป็นเครื่องมือตัดสินใจ แดชบอร์ดที่ดีสำหรับพูลพร็อกซีถูกจัดเป็นกรวย แถวบน: สัญญาณสี่อย่างของทั้งพูลใน 6 ชั่วโมงล่าสุด ซ้อนทับกับเมื่อวานด้วยเส้นบาง แถวที่สอง: สัญญาณเดิม แยกตามประเทศ โอเปอเรเตอร์ และประเภท — ในรูปตารางหรือ heatmap เรียงตามค่าที่แย่ที่สุด แถวที่สาม: อัตราความสำเร็จและ TTFB ของ 10 โฮสต์ปลายทางอันดับต้น แถวที่สี่: อัตราส่วนเวลาสร้าง connection, TTFB และเวลารวม บวก marker แนวตั้งของการ deploy และการเปลี่ยน config ของพูล
ด้วยแดชบอร์ดแบบนี้ การวินิจฉัยคือการตอบสามคำถาม: "ทั้งพูลหรือสไลซ์เสื่อม?" "ทุกปลายทางหรือหนึ่งปลายทาง?" "ความหน่วงในสามค่าตัวไหนเพิ่ม?" คำตอบรวมกันได้สามรูปแบบ
รูปแบบที่ 1: ทุกสไลซ์เสื่อมพร้อมกัน
ทุกประเทศ ทุกโอเปอเรเตอร์ ทุกประเภทพร็อกซีแสดงข้อผิดพลาดหรือความหน่วงเพิ่มพร้อมกัน ระดับนาที ผู้ให้บริการภายนอกและโอเปอเรเตอร์ไม่พังแบบนี้: พวกเขาไม่มีจุดล้มเหลวร่วมกับฝั่งคุณ แสดงว่าปัญหาอยู่ที่คุณ: ช่องทางขาออก DNS การหมด file descriptor หรือ local port การ deploy ล่าสุดที่เปลี่ยน timeout หรือขนาดพูล connection การแย่ง CPU กับ process อื่นบนเครื่องเดียว การดำเนินการแรก: ดู marker ของ deploy และเวลาสร้าง connection กับพร็อกซี ถ้าเวลาสร้าง connection เพิ่มแต่ TTFB (หัก connection แล้ว) คงที่ ปัญหาอยู่ระหว่างคุณกับโหนดพร็อกซี และเกือบแน่นอนว่าอยู่ฝั่งคุณหรือ ISP ของคุณ
รูปแบบที่ 2: สไลซ์เดียวเสื่อม
โอเปอเรเตอร์เดียวในประเทศเดียว หรือประเภทพร็อกซีเดียว แสดงการตก ในขณะที่สไลซ์อื่นคงที่ นี่คือรูปแบบที่พบบ่อยที่สุด สาเหตุอยู่ที่โอเปอเรเตอร์ (ความแออัดของเซกเมนต์เครือข่าย งานบำรุงรักษาตามกำหนด) หรือที่เซกเมนต์โครงสร้างพื้นฐานของผู้ให้บริการพร็อกซี การดำเนินการแรก: ลดน้ำหนักสไลซ์นี้ในการกระจายโหลดชั่วคราว (กลไกอยู่นอกบทความนี้) และเขียนถึงฝ่ายสนับสนุนของผู้ให้บริการด้วยข้อมูลเฉพาะ: สไลซ์ เวลาเริ่ม เมตริกก่อนและหลัง ตัวอย่าง request_id ฝ่ายสนับสนุนของ Proxeon ตอบสนองต่อคำขอแบบนี้เร็วกว่า "ของคุณแย่หมด" เพราะได้รับการระบุตำแหน่งที่พร้อมใช้ การดำเนินการที่สอง: ดู log ของสไลซ์ เพื่อเข้าใจว่าที่อยู่ทั้งหมดในสไลซ์เสื่อม หรือแค่บางส่วน
รูปแบบที่ 3: อัตราความสำเร็จตกสำหรับโฮสต์ปลายทางเดียว ความหน่วงปกติ
ทุกสไลซ์ของพูลยังไปยังทุกโฮสต์ได้ดี ยกเว้นโฮสต์เดียว สำหรับโฮสต์นี้อัตรา upstream_4xx เพิ่มขึ้น หรือปริมาณไบต์ต่อคำขอเปลี่ยน พร็อกซีไม่ผิดเลย ปลายทางเปลี่ยนพฤติกรรม: กำหนดข้อจำกัดความถี่ เปลี่ยนโครงสร้างหน้า เริ่มส่งเนื้อหาต่างสำหรับบางคำขอ การดำเนินการแรก: ลดความเข้มข้นคำขอไปยังแหล่งนี้และตรวจสอบว่าการเก็บข้อมูลของคุณสอดคล้องกับกฎการใช้งานหรือไม่ การดำเนินการที่สอง: ตรวจสอบเนื้อหาในตัวอย่างและอัปเดต validation คำตอบ ในสถานการณ์นี้ไม่ควรเพิ่มจำนวน retry หรือเปลี่ยนที่อยู่บ่อยขึ้นเด็ดขาด — นั่นจะเพิ่มโหลดให้แหล่งข้อมูลและต้นทุนทราฟฟิกของคุณ
รูปแบบที่ 4 (โบนัส): retry เพิ่ม ความสำเร็จคงที่
อัตราความสำเร็จ 99% ความหน่วงพอรับได้ แต่อัตราการ retry เพิ่มจาก 1.1 เป็น 1.8 นี่คือความเสื่อมที่ซ่อนอยู่ มันกำลังกินเงินคุณอยู่ตอนนี้ และจะกลายเป็นความเสื่อมที่มองเห็นได้ทันทีที่จำนวน attempt หมด การดำเนินการแรก: ดูว่า outcome ใดเป็นสาเหตุของการ retry (ฟิลด์ retry_reason ใน log) แล้วเดินตามหนึ่งในสามรูปแบบแรก
Checklist วินิจฉัยใน 60 วินาที
- วินาทีที่ 0–10: แถวบนของแดชบอร์ด สัญญาณใดในสี่ออกจากเส้นเมื่อวาน? เมื่อไรกันแน่?
- วินาทีที่ 10–25: ตารางตามสไลซ์ สไลซ์เดียวแดงหรือทั้งหมด? ถ้าทั้งหมด — รูปแบบที่ 1 ไปที่ marker ของ deploy และโครงสร้างพื้นฐานของตัวเอง
- วินาทีที่ 25–40: ตารางตามโฮสต์ปลายทาง หนึ่งหรือทั้งหมด? ถ้าหนึ่ง — รูปแบบที่ 3 พร็อกซีไม่เกี่ยว
- วินาทีที่ 40–50: อัตราส่วนความหน่วง เพิ่มที่ connection, TTFB หรือเวลารวม?
- วินาทีที่ 50–60: ตั้งประโยคเดียว: "ตั้งแต่ [เวลา] ในสไลซ์ [สไลซ์] สำหรับ [ปลายทาง] [เมตริก] เพิ่มจาก [ก่อน] เป็น [หลัง] สันนิษฐานว่าเกิดจาก [สมมติฐาน]" ประโยคนี้ส่งไปช่องเหตุการณ์และ ticket ถึงผู้ให้บริการ
ข้อผิดพลาดที่พบบ่อยซึ่งลบล้างประโยชน์ของการสังเกตการณ์
เราเห็นการนำการมอนิเตอร์รอบพร็อกซีมาหลายสิบครั้ง ข้อผิดพลาดซ้ำด้วยความคงเส้นคงวาน่าทึ่ง
- เวลาเฉลี่ยเป็นเมตริกหลัก เราพูดแล้ว: ค่าเฉลี่ยซ่อนหาง และหางคือปัญหาจริง
- เฉลี่ยเปอร์เซ็นไทล์ระหว่าง instance รวมฮิสโตแกรม ไม่ใช่ p95 ที่คำนวณแล้ว
- ที่อยู่พร็อกซีเป็น label ของเมตริก cardinality ระเบิดและที่เก็บข้อมูลล่มในเดือนหน้า
- ข้อมูลรับรองการเข้าถึงใน log บรรทัดเดียว "for debug" — และรหัสผ่านอยู่ในที่เก็บ log ส่วนกลางเป็นปี
- การ retry ถูกนับเป็นความสำเร็จ ถ้านับเฉพาะผลลัพธ์สุดท้าย คุณจะไม่เห็นว่าพูลทำงานหนักเป็นสามเท่าของที่ควร
- โค้ด 429 และ 403 จากปลายทางถูกบันทึกเป็นข้อผิดพลาดพร็อกซี นี่คือข้อผิดพลาดในการจำแนกที่พาการสืบสวนไปผิดทางและทำลายความสัมพันธ์กับผู้ให้บริการ
- ความหน่วงเดียวแทนที่จะเป็นสาม โดยไม่แยกวัด connection, TTFB และเวลารวม คุณจะแยกปัญหาความพร้อมใช้งานจากแบนด์วิดท์ไม่ได้
- การแจ้งเตือนที่ไม่มีปริมาณทราฟฟิกขั้นต่ำ เสียงรบกวนกลางคืนและสูญเสียความเชื่อถือต่อการแจ้งเตือน
- การแจ้งเตือนทุกที่อยู่เดียว นั่นเป็นงานของการกักกัน ไม่ใช่การปลุกคน
- ไม่มีการแจ้งเตือนเมื่อไม่มีข้อมูล ไคลเอนต์ล่ม — กราฟเขียว
- เวลาท้องถิ่นใน log การเทียบกับข้อมูลผู้ให้บริการและเพื่อนร่วมทีมใน timezone อื่นกลายเป็นบทลงทัณฑ์
- แดชบอร์ดที่ไม่มี marker ของ deploy ครึ่งหนึ่งของเหตุการณ์เกิดขึ้นพร้อมกับการเปลี่ยนโค้ดหรือ config ถ้าไม่มี marker คุณจะหาสาเหตุจากภายนอกทั้งที่อยู่ภายใน
- เก็บ log รายละเอียดตลอดไป แพงและไร้ความหมาย: ผ่านไปสองสัปดาห์รายละเอียดไม่จำเป็น ส่วน aggregate มีในเมตริกแล้ว
สแตกขั้นต่ำโดยไม่ซับซ้อนเกินไป
การสังเกตการณ์กลายเป็นโปรเจกต์เพื่อโปรเจกต์ได้ง่ายๆ ทีมสามคน deploy message queue แบบกระจาย คลัสเตอร์ฐานข้อมูลคอลัมน์ และระบบ trace — สำหรับไคลเอนต์ที่ทำ 20 คำขอต่อวินาที ไม่ต้องทำอย่างนั้น สแตกต้องเหมาะกับขนาด
ระดับ 1: บริการเดียว ไม่เกิน 50 คำขอต่อวินาที
เพียงพอด้วย JSON log แบบมีโครงสร้างไปยังไฟล์หรือ standard output และสคริปต์หนึ่งที่รวมทุกนาทีและเขียนสี่สัญญาณลงฐานข้อมูลง่ายๆ หรือแม้แต่ log เดียวกัน แดชบอร์ด — เครื่องมือใดก็ได้ที่วาดกราฟจากตารางได้ การแจ้งเตือน — สคริปต์ cron ที่เทียบ aggregate กับเกณฑ์และส่งข้อความไปยัง messenger สแตกแบบนี้ตั้งได้ในหนึ่งวันและครอบคลุม 80% ของความต้องการ
ระดับ 2: หลายบริการ 50–1000 คำขอต่อวินาที
ชุดมาตรฐาน: Prometheus สำหรับเมตริก Grafana สำหรับแดชบอร์ด Alertmanager สำหรับ routing การแจ้งเตือน Loki หรือที่เก็บ log ที่เทียบเท่าพร้อม index ตาม label ไคลเอนต์ส่งออกเมตริกผ่าน HTTP endpoint ในตัวและเขียน JSON log ไปยัง standard output; agent เก็บรวบรวม recording rules คำนวณอัตราความสำเร็จล่วงหน้าเพื่อให้แดชบอร์ดเปิดทันที นี่คือ config ที่อธิบายในตัวอย่างข้างบน และครอบคลุมทีมส่วนใหญ่
ระดับ 3: หลายสิบบริการ หลายพันคำขอต่อวินาที
ตรงนี้จึงมีเหตุผลที่จะใช้ OpenTelemetry เป็นชั้นเก็บรวบรวมเดียว ที่เก็บ log แบบคอลัมน์ที่รองรับ query เชิงวิเคราะห์ตามอำเภอใจบนหลายพันล้านแถว และอาจ trace สำหรับ chain ที่ซับซ้อน แต่แม้ในระดับนี้ สี่สัญญาณและโครงสร้าง log ยังไม่เปลี่ยน เปลี่ยนแค่ transport และการเก็บ
ข้อมูลจากผู้ให้บริการเป็นส่วนเสริม
ผู้ให้บริการพร็อกซีเห็นฝั่งของเขา: ปริมาณการใช้ทราฟฟิกต่อพอร์ต จำนวน connection ที่ใช้งาน เวลาหมุนเวียนล่าสุด แผงและ API ของ Proxeon ให้สถิติเหล่านี้ และมีประโยชน์ที่จะดึงมาไว้ข้างเมตริกของตัวเอง — อย่างน้อยเพื่อเทียบปริมาณทราฟฟิก: ถ้าตัวนับไบต์ของคุณกับของผู้ให้บริการต่างกันเกิน 10–15% คุณมีปัญหาการวัด (บ่อยที่สุดคือไม่ได้นับ header, TLS handshake หรือคำตอบที่ถูกตัด) แต่ข้อมูลผู้ให้บริการไม่แทนที่ของคุณ: มีเพียงคุณที่รู้ว่าคำขอใดสำเร็จในแง่ของ task
Wrapper ของไคลเอนต์เป็นจุดวัดเดียว
ไม่ว่าจะระดับไหนของสแตก การวัดทั้งหมดต้องผ่าน wrapper เดียวเหนือ HTTP client ไม่มี "ตรงนี้วัด ตรงนั้นลืม" แบบแผน:
import time, json, sys, requests
def fetch_via_proxy(ctx, url, timeout=(5, 20)):
t0 = time.perf_counter()
outcome, status, err = "ok", None, None
ttfb = connect = total = 0.0
bytes_in = 0
try:
session = ctx.session # уже настроена на прокси по внутреннему proxy_id
resp = session.get(url, timeout=timeout, stream=True)
ttfb = time.perf_counter() - t0
status = resp.status_code
body = resp.content
bytes_in = len(body)
total = time.perf_counter() - t0
if status >= 500: outcome = "upstream_5xx"
elif status >= 400: outcome = "upstream_4xx"
elif not ctx.validate(body): outcome = "invalid_content"
except requests.exceptions.ProxyError as e:
outcome, err = "proxy_connect_error", type(e).__name__
except requests.exceptions.ReadTimeout as e:
outcome, err = "upstream_timeout", type(e).__name__
except requests.exceptions.ChunkedEncodingError as e:
outcome, err = "truncated", type(e).__name__
finally:
total = total or (time.perf_counter() - t0)
observe(ctx, outcome, ttfb, connect, bytes_in, len(url), ctx.attempt)
record = redact({"ts": ctx.now_utc(), "request_id": ctx.request_id, "attempt": ctx.attempt,
"proxy_id": ctx.proxy_id, "proxy_type": ctx.proxy_type, "country": ctx.country,
"operator": ctx.operator, "pool": ctx.pool, "target_host": ctx.host(url),
"status": status, "outcome": outcome, "error_class": err,
"ttfb_ms": int(ttfb * 1000), "total_ms": int(total * 1000), "bytes_in": bytes_in})
sys.stdout.write(json.dumps(record, ensure_ascii=False) + chr(10))
return outcome, statusสังเกต outcome invalid_content: คำตอบที่ code 200 แต่ไม่ผ่าน validation ของคุณ (เช่น หน้าที่ไม่มีบล็อกข้อมูลที่คาดหวัง) ไม่ใช่ความสำเร็จ คำตอบแบบนี้มักปกปิดการเปลี่ยนพฤติกรรมของปลายทางบ่อยที่สุด
เคส: การสังเกตการณ์ในทางปฏิบัติเป็นอย่างไร
เคส 1: โอเปอเรเตอร์เดียว ประเทศเดียว ลบสองชั่วโมง downtime ต่อสัปดาห์
ทีมมอนิเตอร์ราคาบนแพลตฟอร์มการค้าสาธารณะทำงานผ่านพูลประมาณ 300 ที่อยู่มือถือและ residential ที่ความเข้มข้นประมาณ 40 คำขอต่อวินาที ก่อนใช้การติดป้ายตามโอเปอเรเตอร์ พวกเขาเห็นอย่างเดียว: p95 TTFB รวมกระโดดจาก 1.6 เป็น 3–4 วินาทีเป็นครั้งคราวหลายชั่วโมง อัตราความสำเร็จลดลง 3–5 จุด การสืบสวนแต่ละครั้งใช้เวลาหนึ่งถึงสองชั่วโมง และมักจบด้วยคำว่า "หายเอง" หลังเพิ่ม label country และ operator เหตุการณ์แรกถูกระบุตำแหน่งในสองนาที: ที่อยู่มือถือของโอเปอเรเตอร์เดียวในภูมิภาคเดียว p95 ตรงนั้น 11 วินาที เทียบกับปกติ 2 ที่เหลือ 85% ของพูลปกติทั้งหมด ทีมลดน้ำหนักสไลซ์ในการกระจาย ส่งรายงานให้ผู้ให้บริการพร้อมกรอบเวลาและเมตริก และอีกไม่กี่ชั่วโมงต่อมาได้รับการยืนยันว่ามีงานบำรุงรักษาตามกำหนดของโอเปอเรเตอร์ ผลลัพธ์ในไตรมาส: เวลาวินิจฉัยลดจาก 90 นาทีเหลือ 5 จำนวนเหตุการณ์ไม่เปลี่ยน แต่ผลกระทบต่อ p95 รวมกลายเป็นแยกไม่ออก
เคส 2: retry ที่ซ่อนอยู่และ +38% ในบิลค่าทราฟฟิก
อีกทีมพบปริมาณทราฟฟิกเพิ่มขึ้น 38% ในหนึ่งเดือนขณะจำนวน task ไม่เปลี่ยนและอัตราความสำเร็จคงที่ 98.5% สมมติฐานแรกว่า หน้าปลายทางหน้าใหม่หนักขึ้น เมตริกไบต์ต่อคำขอที่สำเร็จหักล้าง: 46 กิโลไบต์เหมือนเดิม แต่กลับพบว่าอัตราการ retry ซึ่งไม่มีใครดูมาก่อน เพิ่มจาก 1.08 เป็น 1.52 การวิเคราะห์ฟิลด์ retry_reason ใน log แสดงว่าเกือบทุกการ retry เกิดจาก outcome upstream_timeout สำหรับโฮสต์ปลายทางเดียวที่ตอบช้าลงแต่ทัน timeout ในครั้งที่สอง พร็อกซีไม่ใช่สาเหตุเลย ทีมเพิ่ม read timeout สำหรับโฮสต์นี้จาก 8 เป็น 15 วินาที (p99 ที่สุขภาพดีคือ 11) ลดความเข้มข้นคำขอไปยังโฮสต์ และคืนปริมาณทราฟฟิกสู่ระดับเดิมภายในสามวัน บทเรียน: อัตราการ retry คือเมตริกว่าด้วยเงิน
เคส 3: จาก 200 การแจ้งเตือนต่อสัปดาห์เหลือหก
ทีมที่สามรับช่วงระบบแจ้งเตือนที่มีกฎ "อัตราความสำเร็จของสไลซ์ใดก็ตามใน 1 นาทีต่ำกว่า 90%" โดยไม่มีข้อจำกัดทราฟฟิก กลางคืน เมื่อบางสไลซ์มีสองสามคำขอต่อนาที กฎ trigger หลายสิบครั้ง เจ้าหน้าที่เวรปิดเสียง มีเหตุการณ์จริงหนึ่งครั้ง — ความพร้อมใช้งานของเซกเมนต์ทั้งหมดล่มสี่ชั่วโมง — ถูกพบเช้าวันถัดไปจากคำร้องเรียน การปรับกฎใช้เวลาหนึ่งวัน: เพิ่มเงื่อนไขทราฟฟิกขั้นต่ำ ใช้สองหน้าต่าง (5 นาทีและ 1 ชั่วโมง) เพิ่ม for 10 นาที ย้ายสไลซ์ไประดับ warning และเหลือ critical เดียวที่ระดับ pool ผลลัพธ์: ประมาณหกการแจ้งเตือนต่อสัปดาห์ สี่ถึงห้าต้องลงมือจริง เจ้าหน้าที่เวรเปิดเสียงอีกครั้ง
FAQ: คำถามที่พบบ่อยเกี่ยวกับการสังเกตการณ์พูลพร็อกซี
ต้องใช้ trace ไหมถ้ามีเมตริกและ log แล้ว?
สำหรับทีมส่วนใหญ่ — ไม่ คำขอผ่านพร็อกซีไม่ได้ผ่านสิบ microservice มันมี transition เดียว บทบาทของ trace ที่นี่ทำโดย request_id ใน log: ใช้รวบรวมทุก attempt ของคำขอเดียว trace มีประโยชน์เมื่อพร็อกซีไคลเอนต์เป็นเพียงส่วนหนึ่งของ chain ยาว (task queue, parser, validator, loader เข้าฐานข้อมูล) และคุณต้องเข้าใจว่าเวลาหายไปที่ไหนกันแน่
จะวัด TTFB อย่างไรถ้า HTTP client ไม่ให้มาโดยตรง?
ไคลเอนต์ส่วนใหญ่คืนการควบคุมหลังได้ header ถ้าขอ stream ช่วยอ่าน (stream=True หรือเทียบเท่า) ขณะที่คืนคือการประมาณ TTFB เวลาสร้าง connection วัดได้แม่นกว่าที่ระดับ transport: หลายไลบรารีมี hook ต่อ event ของ connection บางตัวมีข้อมูล phase ของคำขอพร้อม ถ้าไม่มี hook สามารถวัดเวลาถึงการส่งไบต์แรกของคำขอแยกด้วยการวัดระดับต่ำ แต่ในทางปฏิบัติการประมาณผ่าน stream เพียงพอ
จะแยกข้อผิดพลาดพร็อกซีจากข้อผิดพลาดปลายทางอย่างไร?
ตามตำแหน่งที่เกิด ถ้า connection กับพร็อกซีสร้างไม่ได้ หรือพร็อกซีส่ง code ของตัวเอง (เช่น 407 เมื่อมีปัญหาการยืนยันตัวตน หรือคำตอบ gateway เฉพาะ) นี่คือข้อผิดพลาดพร็อกซี ถ้าพร็อกซีรับคำขอแล้วได้ code จากปลายทางหรือ timeout รอคำตอบ นี่คือข้อผิดพลาดระหว่างพร็อกซีกับปลายทางหรือของปลายทางเอง จำแนกที่ขอบเขตนี้ แล้วเทียบตามสไลซ์: ข้อผิดพลาดปลายทางปรากฏพร้อมกันทุกสไลซ์ ข้อผิดพลาดพร็อกซีปรากฏเฉพาะบางสไลซ์
อัตราการ retry ที่สุขภาพดีควรเป็นเท่าไร?
ขึ้นกับเป้าหมายและประเภทพร็อกซี สำหรับที่อยู่มือถือ 1.05–1.15 ถือว่าปกติ: เครือข่ายไร้สายโดยธรรมชาติคาดเดาได้น้อยกว่า สำหรับ data center — 1.01–1.05 สิ่งสำคัญกว่าไม่ใช่ค่าสัมบูรณ์ แต่ความ穩定: การเพิ่ม 30% เทียบกับ baseline รายสัปดาห์เป็นเหตุให้ตรวจสอบ แม้ค่าสัมบูรณ์ยังดูเล็กน้อย
จะเลือกเกณฑ์การแจ้งเตือนอย่างไรถ้าไม่มีประวัติ?
เริ่มด้วยการสังเกตโดยไม่แจ้งเตือน: เก็บข้อมูลสองสัปดาห์ ดูการกระจายอัตราความสำเร็จและ p95 ตามวันและชั่วโมง ตั้งเกณฑ์ critical ให้ในช่วงสองสัปดาห์นี้ trigger ไม่เกินหนึ่งถึงสองครั้ง และทั้งสองครั้งคุณเห็นด้วยว่าเป็นเหตุการณ์ เกณฑ์ warning — ให้ trigger สองสามครั้งต่อสัปดาห์ แล้วทบทวนทุกเดือน: การแจ้งเตือนที่ไม่เคยนำไปสู่การลงมือ ต้องเข้มขึ้นหรือลบทิ้ง
ควรเก็บเนื้อหาคำตอบอย่างน้อยสำหรับข้อผิดพลาดไหม?
ข้อผิดพลาด transport ไม่มีเนื้อหา สำหรับคำตอบที่มีเนื้อหาไม่คาดคิด (invalid_content) มีประโยชน์ที่จะเก็บสองสามร้อยไบต์แรก หรือ hash และความยาว ไปยังที่เก็บแยกที่มีอายุหนึ่งถึงสองวัน พร้อมลบข้อมูลส่วนบุคคลเสมอ ไม่จำเป็นต้องเก็บเนื้อหาเต็ม: หนัก อาจมีข้อมูลบุคคลที่สาม และสำหรับการวินิจฉัยพร็อกซีมักพอรู้ว่าหน้าประเภทไหน
จะเทียบบัญชีทราฟฟิกของตัวเองกับข้อมูลผู้ให้บริการอย่างไร?
นับไบต์ทั้งสองทิศทาง รวม header และเทียบยอดรายวันต่อพอร์ตหรือ session กับที่แผงผู้ให้บริการแสดง ความต่าง 5–10% ปกติ: ฝั่งคุณไม่นับค่าใช้จ่าย TLS และ connection ที่ถูกตัด ความต่างเกิน 20% เป็นเหตุให้ตรวจว่าคุณนับเฉพาะเนื้อหาคำตอบและเสีย attempt ที่ล้มเหลวก่อนอ่านเนื้อหาหรือไม่
ใช้เวลานานเท่าไรในการนำวิธีนี้มาใช้จากศูนย์?
สำหรับไคลเอนต์เดียวที่มี wrapper เหนือ HTTP: หนึ่งวันสำหรับเมตริกและโครงสร้าง log หนึ่งวันสำหรับแดชบอร์ด หนึ่งวันสำหรับกฎแจ้งเตือน และอีกสองสัปดาห์สำหรับสังเกตก่อนเปิด critical ถ้ามีหลายไคลเอนต์ในภาษาต่างกัน เวลาส่วนใหญ่อยู่ที่การตกลงชุด outcome และ label ร่วม — ตรงนี้อย่ารีบ เพราะการเปลี่ยนชื่อ label หลังเก็บประวัติมาปีเป็นเรื่องเจ็บปวดมาก
ทำอย่างไรถ้าข้อมูลโอเปอเรเตอร์จากผู้ให้บริการไม่มีสำหรับบางที่อยู่?
ใช้ค่า unknown และติดตามสัดส่วน ถ้า unknown เกิน 10–15% ของพูล การแบ่งสไลซ์ตามโอเปอเรเตอร์จะหมดความหมาย — ขอ metadata ครบจากผู้ให้บริการ หรือติดป้ายที่อยู่ด้วยมือครั้งเดียวตอนเพิ่มเข้าพูล
บทสรุป: หนึ่งนาทีแทนหนึ่งชั่วโมง
กลับไปที่ฉากตอนต้น "พร็อกซีช้า" เป็นประโยคที่ไม่มีข้อมูล ทุกสิ่งที่อธิบายในคู่มือนี้รับใช้เป้าหมายเดียว: เปลี่ยนมันให้เป็นประโยคที่มีตัวเลข สไลซ์ เวลา และสมมติฐาน สี่สัญญาณ — อัตราความสำเร็จ ความหน่วงตามเปอร์เซ็นไทล์ อัตราการ retry ปริมาณทราฟฟิก — ให้ภาพสถานะของพูล log แบบมีโครงสร้างพร้อม request_id, proxy_id, หมายเลข attempt, code, ความหน่วงสามค่า และขนาด — ให้รายละเอียดสำหรับการสืบสวน เปอร์เซ็นไทล์แทนค่าเฉลี่ยแสดงหางที่มีปัญหาจริง การติดป้ายตามประเทศ โอเปอเรเตอร์ และประเภท ระบุตำแหน่งความเสื่อมถึงระดับสไลซ์ การแจ้งเตือนที่มีทราฟฟิกขั้นต่ำ สองหน้าต่าง และ hysteresis trigger น้อยและตรงประเด็น แดชบอร์ดแบบกรวยตอบสามคำถามในหนึ่งนาที
เริ่มจากอะไรพรุ่งนี้? ขั้นแรก — wrapper เหนือไคลเอนต์ที่มีการจำแนก outcome ร่วมและฟังก์ชันบันทึกเดียว ขั้นที่สอง — ฮิสโตแกรม TTFB และตัวนับ attempt พร้อม label ตามประเทศ โอเปอเรเตอร์ และประเภท ขั้นที่สาม — ฟังก์ชันแก้ไข log ที่ทุกอย่างต้องผ่านโดยไม่มีข้อยกเว้น ขั้นที่สี่ — แดชบอร์ดสี่กราฟและตารางตามสไลซ์หนึ่งตาราง ขั้นที่ห้า — สังเกตสองสัปดาห์แล้วจึงค่อยแจ้งเตือน อีกหนึ่งเดือนคุณจะดูกราฟอัตราการ retry และอาจประหลาดใจ: พูลทำงานหนักกว่าที่คิดมาก และในหนึ่งไตรมาส ประโยค "ของเราทุกอย่างช้าไปหมด" จะหายไปจากแชทของคุณ — ไม่ใช่เพราะปัญหาไม่มี แต่เพราะคุณจะเรียกแต่ละปัญหาด้วยชื่อของมัน