พร็อกซีและเว็บฮุค: ทำไมพร็อกซีขาออกไม่ทำให้บริการเข้าถึงได้จากภายนอก
บทความ
- พื้นฐาน: พร็อกซีคืออะไร และเว็บฮุคคืออะไรจริงๆ
- ทิศทางของ connection: ขาออก vs ขาเข้า
- ทำไมพร็อกซีไคลเอนต์ถึงไม่เปิดพอร์ตฟัง และไม่ให้ที่อยู่สาธารณะ
- เครือข่ายมือถือและที่อยู่ร่วม: ทำไมที่อยู่ผู้ใช้ถึงกำหนดเส้นทางจากภายนอกไม่ได้ตั้งแต่แรก
- อะไรที่แก้ปัญหาการรับเว็บฮุคได้จริง
- Polling แทนเว็บฮุค: ออกแบบอย่างไรไม่ให้ชน rate limit
- ตรงไหนที่พร็อกซียังจำเป็นข้างๆ เว็บฮุค
- ข้อผิดพลาดทั่วไปและวิธีหลีกเลี่ยง
- เครื่องมือและแหล่งข้อมูล
- เคสและผลลัพธ์
- ตาราง: งานและทางแก้ที่เหมาะสม
- Faq: คำถามที่พบบ่อย
- บทสรุป: รวบรวมทุกอย่างเข้าด้วยกัน
มีความเข้าใจผิดฝังรากลึกที่โผล่มาให้เห็นครั้งแล้วครั้งเล่าในแชทนักพัฒนา ในกลุ่มผู้รวมระบบชำระเงิน และในหมู่คนที่เพิ่งเริ่มแตะ API ภายนอก มันฟังดูประมาณนี้: "ซื้อพร็อกซีมา แล้วเว็บฮุคจะวิ่งมาที่มัน" ความคาดหวังนี้เข้าใจได้ เกือบจะสัญชาตญาณด้วยซ้ำ ในเมื่อพร็อกซีให้ที่อยู่กับฉัน มันก็ต้องมีคนเข้าถึงที่อยู่นั้นได้ใช่ไหม น่าเสียดายที่คำตอบคือไม่ และความผิดพลาดนี้ทำให้ผู้คนเสียเวลาปลุกปล้ำหลายชั่วโมง รายงานบั๊กให้ผู้ให้บริการอย่างผิดๆ และพลาดเดดไลน์ไปเปล่าๆ
คู่มือนี้เราจะแกะเรื่องนี้ลงไปถึงรากฐานเลย ทำไมพร็อกซีถึงทำงานได้อย่างยอดเยี่ยมกับงาน คำขอขาออก แต่ fundamentally ทำไม่ได้เลยที่จะทำให้บริการของคุณ เข้าถึงได้จากภายนอก เกิดอะไรขึ้นในระดับทิศทางของ connection ทำไมที่อยู่ของผู้ใช้ในเครือข่ายมือถือถึงกำหนดเส้นทางจากภายนอกไม่ได้ และที่สำคัญที่สุด อะไรกันแน่ที่คุณใช้ได้จริงถ้าต้องรับเว็บฮุค: เซิร์ฟเวอร์ที่มีที่อยู่สาธารณะ อุโมงค์ย้อนกลับ คิวฝั่งผู้ให้บริการ หรือ polling แทนการสมัครสมาชิก
บทความนี้เขียนด้วยภาษาแบบวิศวกร พร้อมตัวอย่างโค้ดและกรอบการตัดสินใจที่พร้อมใช้ เราตั้งใจไม่ลงรายละเอียดเรื่องการตั้งค่าเราเตอร์และการ forward port เพราะนั่นเป็นอีกหัวข้อหนึ่งที่อยู่ข้างๆ กัน เราจะแค่บอกขอบเขตไว้เท่านั้น เป้าหมายของเราต่างออกไป นั่นคือเข้าใจโมเดลและเลือกเครื่องมือให้ถูก ไปกันเลย
พื้นฐาน: พร็อกซีคืออะไร และเว็บฮุคคืออะไรจริงๆ
ก่อนที่เราจะเถียงกันว่าพร็อกซีทำอะไรได้หรือไม่ได้ เรามาตกลงเรื่องคำศัพท์กันก่อน ถ้าไม่ทำแบบนี้ บทสนทนาก็จะกลายเป็นข้าวต้มที่คลุกเคล้าไปด้วยความคาดหวังและตำนาน
พร็อกซีแบบพูดง่ายๆ
พร็อกซีเซิร์ฟเวอร์ คือตัวกลางสำหรับคำขอขาออกของคุณ โปรแกรมของคุณอยากติดต่อเว็บไซต์หรือ API สักแห่ง แทนที่จะเชื่อมต่อตรงๆ มันก็เชื่อมต่อไปยังพร็อกซี แล้วพร็อกซีก็จะวิ่งไปหาปลายทางในนามของตัวเองและส่งคำตอบกลับมาให้คุณ คำสำคัญตรงนี้คือ ขาออก คนที่ริเริ่มคือคุณเสมอ
สิ่งที่คุณได้จากการใช้พร็อกซี:
- เซิร์ฟเวอร์ปลายทางเห็น IP ของพร็อกซี ไม่ใช่ IP ของคุณเอง
- คุณควบคุมภูมิภาคและประเภทเครือข่ายของทางออกได้ (เช่น เครือข่ายมือถือ)
- คุณกระจายโหลดและหมุนที่อยู่ได้สำหรับงานที่ถูกกฎหมายอย่างการเก็บข้อมูลสาธารณะหรือการทดสอบเนื้อหาที่ขึ้นกับพื้นที่
สิ่งที่พร็อกซี ไม่ ให้คุณ: มันไม่เปิดพอร์ตที่คอยรับ connection ขาเข้าจากบุคคลที่สาม และไม่เปลี่ยนเครื่องของคุณให้กลายเป็นเซิร์ฟเวอร์ที่กำหนดที่อยู่จากภายนอกได้ นี่คือหลักการพื้นฐาน จำไว้แล้วเราจะกลับมาที่จุดนี้
เว็บฮุคแบบพูดง่ายๆ
เว็บฮุค (webhook) คือกลไก callback คุณลงทะเบียน URL ไว้กับบริการของบุคคลที่สาม เมื่อเกิดเหตุการณ์ (มีเงินเข้ามา สถานะคำสั่งซื้อเปลี่ยน เอกสารอัปเดต) บริการนั้นจะส่ง HTTP request มาที่ URL นั้นด้วยตัวเอง นั่นหมายความว่าบริการภายนอกกลายเป็นไคลเอนต์ และคุณต้องเป็นเซิร์ฟเวอร์ที่คอยฟังและตอบกลับ
สังเกตการสลับบทบาทนะ ในกรณีพร็อกซี คุณคือไคลเอนต์ที่เคาะออกไปข้างนอก ในกรณีเว็บฮุค โลกภายนอกคือไคลเอนต์ที่เคาะเข้ามาหาคุณ มันเป็นสองทิศทางที่ตรงข้ามกันเลย และนี่แหละคือจุดที่ความสับสนถือกำเนิด
ทำไมถึงสับสนกัน
ทั้งสองแนวคิดเกี่ยวพันกับคำว่า "HTTP" "ที่อยู่" "คำขอ" คนได้ยินว่า "พร็อกซีให้ IP ฉัน" แล้วก็สรุปอย่างมีเหตุผลแต่ผิดว่า: ในเมื่อมี IP ก็ส่งเว็บฮุคไปที่นั้นได้ ปัญหาคือการมี IP ขาออกกับการมีพอร์ตที่ฟังจากภายนอกได้เป็นคนละเรื่องกัน อุปมาเปรียบเทียบ: คุณมีเบอร์โทรของศูนย์เรียกรถแท็กซี่ ที่คุณโทรออกไปเพื่อเรียกรถ แต่นั่นไม่ได้หมายความว่าใครก็ได้จะโทรเข้ามาหาคุณที่เบอร์นั้นแล้วได้ตัวคุณ เบอร์นั้นเป็นของศูนย์รับสาย ไม่ใช่ของคุณ
ทิศทางของ connection: ขาออก vs ขาเข้า
นี่คือแนวคิดหลักของบทความทั้งหมด ถ้าคุณจะจำได้แค่หัวข้อเดียว ให้เป็นหัวข้อนี้
ใครเคาะหาใคร
TCP connection ทุกอันมีคนริเริ่มและฝ่ายที่รับ คนริเริ่มเปิด connection (ทำ connect) ฝ่ายที่รับก็คอยฟัง (ทำ listen และ accept) มาดูสองแบบ
แบบคำขอขาออกผ่านพร็อกซี
ลองนึกภาพเป็นลูกศรแบบนี้:
- แอปของคุณ (คนริเริ่ม) → เปิด connection ไปยัง → พร็อกซี Proxeon
- พร็อกซี (ตอนนี้มันกลายเป็นคนริเริ่ม) → เปิด connection ไปยัง → API ปลายทาง
- คำตอบวิ่งกลับมาเส้นทางเดิมผ่าน connection ที่เปิดไว้แล้ว
สังเกตว่า ทั้งสอง connection ถูกเริ่มจากข้างในออกไปข้างนอก ไม่มีใครจากภายนอกเริ่มเชื่อมต่อกับคุณเลย คุณเป็นคนแรกที่เคาะเสมอ พร็อกซีเข้ากับโมเดลนี้อย่างสมบูรณ์แบบ เพราะสำหรับคำขอขาออก แค่เปิด connection ได้ก็พอ ไม่ต้องรับ
แบบเว็บฮุคขาเข้า
ทีนี้เรื่องเปลี่ยน:
- บริการภายนอก (คนริเริ่ม) → อยากเปิด connection ไปยัง → บริการของคุณ
- มันต้องมี ที่อยู่และพอร์ตที่เข้าถึงได้จากสาธารณะ ที่มีใครสักคนทำ listen และ accept
- บริการของคุณรับ connection อ่าน body ของ request แล้วตอบกลับด้วยสถานะ 200
ตรงนี้คนริเริ่มมาจากภายนอก แปลว่าคุณต้องมีจุดที่เข้าถึงได้จากอินเทอร์เน็ต พร็อกซีที่ทำงานในโหมดไคลเอนต์สำหรับคำขอขาออกของคุณไม่ใช่จุดแบบนั้น มันไม่ได้คอยฟัง connection ขาเข้าจากบริการแปลกหน้าที่ส่งมาหาคุณ
ทำไมทิศทางถึง "พลิกกลับเอง" ไม่ได้
บางคนถามว่า แค่ "หมุน" พร็อกซีให้มันรับได้เลยไม่ได้หรือ ในทางเทคนิคแล้วการพลิกทิศทางทำได้ แต่มันจะเป็นผลิตภัณฑ์คนละอย่างและสถาปัตยกรรมคนละแบบไปเลย: reverse proxy, อุโมงค์ หรือเซิร์ฟเวอร์ พร็อกซีไคลเอนต์ธรรมดาสำหรับทราฟฟิกขาออกไม่กลายเป็นตัวรับ connection ขาเข้าได้เพียงคลิกเดียว มันเหมือนขอให้บันไดเลื่อนทำงานเป็นบันไดเลื่อนที่วิ่งสวนทาง ทั้งสองอย่างเกี่ยวข้องกับขั้นบันไดเหมือนกัน แต่กลไกคนละเรื่อง
ทำไมพร็อกซีไคลเอนต์ถึงไม่เปิดพอร์ตฟัง และไม่ให้ที่อยู่สาธารณะ
มาเจาะลึกกลไกทางเทคนิคกัน ทำไมพร็อกซีไคลเอนต์ถึงเป็นจุดรับไม่ได้
พร็อกซีไคลเอนต์กับพร็อกซีเซิร์ฟเวอร์: อย่าสับสนบทบาท
เวลา "ซื้อพร็อกซี" คุณจะได้เข้าถึง พร็อกซีเซิร์ฟเวอร์: ที่อยู่ พอร์ต ล็อกอิน และรหัสผ่าน แอปของคุณทำหน้าที่เป็น พร็อกซีไคลเอนต์: มันเชื่อมต่อไปยังเซิร์ฟเวอร์และขอให้พร็อกซีคำขอขาออก พอร์ตที่คุณใส่ในตั้งค่า คือพอร์ตของพร็อกซีเซิร์ฟเวอร์ที่คุณเชื่อมต่อไปหา ไม่ใช่พอร์ตที่ใครจะมาเปิดฟังเว็บฮุคที่ส่งมาถึงคุณ
มาดูตัวอย่างง่ายๆ ของการยิง request ผ่านพร็อกซีใน Python:
import requests
proxies = {
"http": "http://user:pass@gateway.proxeon.net:8080",
"https": "http://user:pass@gateway.proxeon.net:8080",
}
resp = requests.get("https://api.example.com/v1/orders", proxies=proxies, timeout=15)
print(resp.status_code, resp.json())ทุกอย่างตรงนี้เป็นขาออก โค้ดของคุณริเริ่ม connection ไปยังพร็อกซี พร็อกซีวิ่งไปที่ api.example.com ไม่มี socket ที่คอยฟังสำหรับรับเว็บฮุคโผล่มา และไม่มีทางโผล่ขึ้นมาได้ พอร์ต 8080 เป็นของโครงสร้างพื้นฐานของ Proxeon และมีไว้รับคำขอขาออกของคุณ ไม่ใช่รับเว็บฮุคจากคนอื่นที่ส่งมาหาคุณ
การ "ฟังพอร์ต" หมายถึงอะไร และทำไมมันเป็นฟังก์ชันแยกต่างหาก
การจะรับ connection ขาเข้า ต้องมีโปรเซสที่เรียก system call bind (ผูกกับที่อยู่และพอร์ต), listen (พร้อมรับ), และ accept (รับ connection นั้นจริงๆ) นี่คือตัวอย่างเซิร์ฟเวอร์ขั้นต่ำที่รับเว็บฮุคได้:
from flask import Flask, request
app = Flask(__name__)
@app.post("/webhook")
def webhook():
event = request.get_json(silent=True) or {}
# ยืนยันการรับอย่างรวดเร็ว
return "", 200
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)โปรเซสนี้คอยฟังพอร์ต 8000 แต่แค่ฟังอย่างเดียวไม่พอ ต้องให้พอร์ตนี้ เข้าถึงได้จากอินเทอร์เน็ต ด้วย และนั่นคือเรื่องของที่อยู่สาธารณะและการเข้าถึงจากเครือข่าย ซึ่งพร็อกซีไคลเอนต์ไม่ได้แก้ให้คุณ
ความแตกต่างในประโยคเดียว
พร็อกซีให้ ทางออก สู่อินเทอร์เน็ตภายใต้ที่อยู่ที่ต้องการ เว็บฮุคต้องได้ ทางเข้า จากอินเทอร์เน็ตมาที่ที่อยู่ของคุณ ทางออกกับทางเข้าไม่ใช่คำพ้อง แต่เป็นการดำเนินการที่สะท้อนกัน พร็อกซีไคลเอนต์รับผิดชอบทางออก
เครือข่ายมือถือและที่อยู่ร่วม: ทำไมที่อยู่ผู้ใช้ถึงกำหนดเส้นทางจากภายนอกไม่ได้ตั้งแต่แรก
หัวข้อนี้แยกออกมาและสำคัญมาก โดยเฉพาะถ้าคุณทำงานกับพร็อกซีมือถือ ตรงนี้ความสับสนทวีคูณขึ้นจากธรรมชาติของเครือข่ายเซลลูลาร์
ที่อยู่เดียวใช้หลายคน: ทางออกร่วมทำงานอย่างไร
ในเครือข่ายมือถือ ผู้ใช้มักไม่ได้รับ IP สาธารณะเป็นของตัวเอง ผู้ให้บริการใช้เทคโนโลยีแปลงที่อยู่ ซึ่งผู้ใช้จำนวนมากแชร์ IP สาธารณะจำนวนเล็กน้อยร่วมกัน สมาร์ทโฟนหรือโมเด็มของคุณได้ที่อยู่ภายในจากช่วงส่วนตัว ส่วนขาออกทุกคนออกผ่านเกตเวย์ร่วมของผู้ให้บริการ ภายนอกอินเทอร์เน็ตจึงเห็นที่อยู่ของผู้ให้บริการ ไม่ใช่ของคุณส่วนตัว
นี่หมายความว่าอะไรในทางปฏิบัติ:
- ที่อยู่ผู้ใช้ของคุณคือที่อยู่ภายในในเครือข่ายผู้ให้บริการ มันไม่ถูกกำหนดเส้นทางจากอินเทอร์เน็ตสาธารณะ
- ต่อให้อยากทำ คุณก็แค่ "เปิดพอร์ต" บนการเชื่อมต่อมือถือเพื่อให้บริการภายนอกเข้าถึงอุปกรณ์คุณเฉพาะเจาะจงไม่ได้
- ที่อยู่สาธารณะที่เว็บไซต์เห็นเป็นของโครงสร้างพื้นฐานผู้ให้บริการและถูกแชร์ระหว่างผู้ใช้จำนวนมากในเวลาเดียวกัน
ทำไมมันถึงเข้ากับการรับเว็บฮุคไม่ได้เลยตั้งแต่ราก
นึกภาพออฟฟิศขนาดใหญ่ที่มีเคาน์เตอร์ต้อนรับกลางตัวเดียว ทุกสายที่โทรออกผ่านหมายเลขกลางหมายเลขเดียว คุณโทรหาใครก็ได้ (การโทรออกทำงาน) แต่ถ้าใครจากภายนอกกดหมายเลขกลางนี้ เขาจะได้เคาน์เตอร์ต้อนรับ ไม่ใช่คุณที่โต๊ะชั้นเจ็ด เคาน์เตอร์ไม่รู้ว่าสายนั้นตั้งใจจะให้ใคร เพราะคนโทรมาไม่ได้ระบุหมายเลขต่อภายใน ซึ่งในระบบนี้คุณก็ไม่มีอยู่แล้ว
ทางออกมือถือก็ทำงานแบบนี้เป๊ะๆ connection ขาออกจำได้ว่าใครเริ่มมัน คำตอบเลยกลับมาหาคุณ แต่ connection ขาเข้าใหม่จากภายนอกไม่มีความข้อมูลว่าเกี่ยวกับผู้ใช้คนไหนในหลายพันคน เพราะงั้นเว็บฮุคที่ส่งไปที่ที่อยู่ร่วมของผู้ให้บริการจึงส่งมาถึงอุปกรณ์คุณโดยตรงไม่ได้จริงๆ นี่ไม่ใช่ข้อจำกัดของแพ็กเกจใดๆ แต่มันเป็นธรรมชาติของสถาปัตยกรรม
พร็อกซีมือถือกับเว็บฮุค: จุดที่ได้ประโยชน์จริง
พร็อกซีมือถือ Proxeon แก้ปัญหาคำขอขาออกภายใต้ที่อยู่มือถือได้อย่างยอดเยี่ยม นี่เป็นที่ต้องการในสถานการณ์ที่ถูกกฎหมาย: ตรวจสอบว่าบริการหน้าตาเป็นอย่างไรสำหรับผู้ใช้มือถือในแต่ละภูมิภาค เก็บข้อมูลสาธารณะ ทดสอบตรรกะตามพื้นที่ ทำงานกับ API ที่แยกแยะประเภทเครือข่าย แต่การรับเว็บฮุคเป็นเรื่องทางเข้า และทางเข้าผ่านที่อยู่มือถือร่วมทำไม่ได้ แปลว่าสำหรับการรับต้องมีองค์ประกอบแยกต่างหาก เดี๋ยวเราจะพูดถึงเรื่องนั้นต่อไป
อะไรที่แก้ปัญหาการรับเว็บฮุคได้จริง
เราเข้าใจแล้วว่าทำไมพร็อกซีไม่ใช่ตัวรับ ต่อจากนี้คือทางออกที่ใช้ได้จริง มีสี่แนวทางที่ทำงานได้ และเกือบทุกครั้งคุณเลือกอันใดอันหนึ่งหรือผสมกัน
แนวทางที่ 1: เซิร์ฟเวอร์ที่มีที่อยู่สาธารณะ
วิธีที่ตรงไปตรงมาและคาดเดาได้ที่สุด คุณตั้งบริการบนเครื่องที่มีที่อยู่สาธารณะถาวรและชื่อโดเมน ตั้งค่า TLS และคอยฟัง HTTPS request ขาเข้า บริการภายนอกส่งเว็บฮุคมาที่โดเมนของคุณ คุณรับและตอบกลับ
เมื่อไหร่ควรเลือก:
- คุณมีหรือสามารถมีเซิร์ฟเวอร์เฉพาะหรือเครื่องเสมือนบนคลาวด์
- คุณต้องการตัวกลางน้อยที่สุดและควบคุมได้มากที่สุด
- ต้องการความเสถียร ความหน่วงที่คาดเดาได้ และกฎความปลอดภัยของตัวเอง
ตัวจัดการเว็บฮุคที่เรียบง่ายแต่ก็รอบคอบ พร้อมการตรวจสอบลายเซ็น หน้าตาแบบนี้:
import hmac, hashlib
from flask import Flask, request, abort
app = Flask(__name__)
SECRET = b"your_shared_secret"
def valid_signature(body, signature):
digest = hmac.new(SECRET, body, hashlib.sha256).hexdigest()
return hmac.compare_digest(digest, signature or "")
@app.post("/webhook")
def webhook():
body = request.get_data()
sig = request.headers.get("X-Signature")
if not valid_signature(body, sig):
abort(401)
# รับอย่างรวดเร็วและโยนเข้าคิวเพื่อประมวลผล
enqueue(body)
return "", 200
def enqueue(body):
# ใส่ลง broker หรือ DB ตระกูลหนักๆ ทำแบบ asynchronous
pass
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)สังเกตหลักการสองข้อของการประมวลผลที่โตเต็มวัย ข้อแรก: ตรวจสอบลายเซ็นเพื่อรับเฉพาะเว็บฮุคที่แท้จริง ข้อสอง: ตอบด้วย 200 อย่างรวดเร็วและย้ายงานหนักไปที่คิว asynchronous ไม่งั้นผู้ส่งจะมองว่าคุณเข้าถึงไม่ได้เพราะ timeout แล้วเริ่มส่งซ้ำ
แนวทางที่ 2: อุโมงค์ย้อนกลับ
ถ้าไม่มีเซิร์ฟเวอร์ที่มีที่อยู่สาธารณะ แต่บริการรันบนเครื่องท้องถิ่นหรือหลังที่อยู่ร่วม อุโมงค์ย้อนกลับช่วยได้ แนวคิดสวยงามและเข้ากับโมเดลทิศทางที่เราคุยกันไปแล้วอย่างสมบูรณ์
เครื่องของคุณเริ่ม connection ขาออกไปยังจุดอุโมงค์สาธารณะด้วยตัวเอง connection นี้เปิดค้างไว้ เมื่อเว็บฮุคจากภายนอกมาที่ที่อยู่สาธารณะของอุโมงค์ มันจะถูกดันผ่านช่องทางที่สร้างไว้แล้วมาถึงคุณ ดูความสวยงามสิ: ภายนอกก็ยังไม่มีใครเริ่มเชื่อมต่อกับเครื่องส่วนตัวของคุณ คนที่ริเริ่มคือคุณตอนที่ตั้งอุโมงค์ ส่วนเว็บฮุคเดินทางกลับตามเส้นทางเดิมภายในช่องทางที่เปิดไว้แล้ว
เมื่อไหร่ควรเลือก:
- พัฒนาและดีบักการเชื่อมต่อในเครื่องตัวเอง
- ไม่มีความสามารถหรือไม่อยากดูแลเซิร์ฟเวอร์สาธารณะ
- ต้องการจุดรับชั่วคราวหรือยืดหยุ่น
ตรงนี้สำคัญที่ต้องเข้าใจขอบเขตการใช้: เราตั้งใจไม่ลงรายละเอียดการตั้งค่าซอฟต์แวร์อุโมงค์ การกำหนดเส้นทาง และการ forward port บนเราเตอร์ เพราะนั่นเป็นหัวข้อวิศวกรรมแยกต่างหาก ความคิดหลักคืออุโมงค์แก้ปัญหาทางเข้าได้ด้วย connection ขาออกที่เปิดไว้ล่วงหน้า
แนวทางที่ 3: คิวฝั่งผู้ให้บริการ
แพลตฟอร์มใหญ่ๆ หลายแห่งเสนอไม่แค่เว็บฮุค แต่ยังมี คิวข้อความ หรือ event bus ฝั่งของเขา แทนที่เขาจะเคาะมาหาคุณ คุณก็ไปดึงเหตุการณ์ออกมาจากคิวของเขาด้วย connection ขาออกของคุณเอง นี่เข้ากับโมเดลพร็อกซีเป๊ะ เพราะทุกอย่างเป็นขาออกอีกแล้ว
แนวคิดการทำงาน:
- ผู้ให้บริการใส่เหตุการณ์ลงในคิวหรือหัวข้อของเขา
- consumer ของคุณเชื่อมต่อกับคิวและดึงข้อความออกมา
- หลังประมวลผลเสร็จ คุณยืนยันการรับ และข้อความจะถูกลบออกจากคิว
ข้อได้เปรียบมหาศาล: ถ้า consumer ของคุณล่มชั่วคราว เหตุการณ์จะไม่หาย มันรออยู่ในคิว นี่กำจัดปัญหาปวดหัวที่สุดของเว็บฮุค นั่นคือการหายไปเมื่อตัวรับไม่พร้อม และที่สำคัญสำหรับหัวข้อของเรา ทุกการเข้าถึงคิวเป็นขาออก แปลว่าผ่านพร็อกซี Proxeon ได้โดยไม่มีความยุ่งยากเรื่องที่อยู่สาธารณะเลย
แนวทางที่ 4: polling แทนการสมัครสมาชิก
ถ้าบริการของบุคคลที่สามไม่มีทั้งคิวและอุโมงค์ที่สะดวก และคุณรับเว็บฮุคไม่ได้ ที่เหลือก็คือของคลาสสิก: polling คุณถาม API เป็นระยะด้วยตัวเองว่ามีเหตุการณ์ใหม่ไหม นี่ก็เป็นคำขอขาออก และทำงานผ่านพร็อกซีได้อย่างยอดเยี่ยม
polling มักถูกมองข้ามว่าเป็นวิธีดั้งเดิม ที่จริงแล้ว polling ที่ออกแบบมาอย่างดีนั้นเชื่อถือได้ ดูแลง่าย และไม่ต้องมีโครงสร้างพื้นฐานสาธารณะอะไรเลย ศัตรูตัวฉกาจเพียงตัวเดียวของมันคือ rate limit เรื่องวิธีไม่ให้ละเมิด เราจะพูดในหัวข้อใหญ่ต่อไปนี้
วิธีเลือกแนวทาง: กรอบสั้นๆ
- มีเซิร์ฟเวอร์สาธารณะและต้องการความหน่วงต่ำสุด? เลือกเซิร์ฟเวอร์ตรงๆ ที่มีที่อยู่สาธารณะ
- ไม่มีเซิร์ฟเวอร์ แต่ต้องรับเดี๋ยวนี้ โดยเฉพาะเพื่อการพัฒนา? อุโมงค์ย้อนกลับ
- ผู้ให้บริการมีคิวหรือ event bus? เลือกมันเสมอ นี่คือตัวเลือกที่มั่นคงที่สุด
- ไม่มีอะไรข้างบนเลย แต่มี API ให้อ่าน? polling ผ่านพร็อกซี
polling แทนเว็บฮุค: ออกแบบอย่างไรไม่ให้ชน rate limit
polling คือสนามบินสำรองที่เชื่อถือได้ของคุณเมื่อการรับขาเข้าเป็นไปไม่ได้ แต่การทำแบบ naive จะชนข้อจำกัดจำนวน request อย่างรวดเร็วและเริ่มโดนปฏิเสธ มาออกแบบให้ถูกต้องกัน
เวอร์ชัน naive พื้นฐานและทำไมมันแย่
มือใหม่มักเขียนอะไรแบบนี้:
import time, requests
proxies = {"https": "http://user:pass@gateway.proxeon.net:8080"}
while True:
r = requests.get("https://api.example.com/v1/events", proxies=proxies, timeout=15)
handle(r.json())
time.sleep(1)
def handle(data):
passอะไรผิดตรงนี้? การหยุดคงที่หนึ่งวินาทีหมายถึง 86400 request ต่อวัน ไม่ว่ามีเหตุการณ์หรือไม่ก็ตาม คุณเผา limit ทิ้งเปล่าๆ เมื่อเกิด error ลูปก็ยังคงยิงเซิร์ฟเวอร์ด้วยความถี่เดิม ไม่มีการอ่าน header ของ limit เลย นี่คือทางตรงสู่การถูกบล็อกเพราะความถี่
หลักการที่ 1: polling แบบ incremental ด้วย cursor
อย่าดึงทุกอย่างรวดเดียว ขอเฉพาะสิ่งที่เกิดหลังเหตุการณ์ล่าสุดที่คุณรู้จัก API ส่วนใหญ่ส่ง cursor หรือ timestamp ของเหตุการณ์ล่าสุดมาให้ เก็บมันไว้และส่งไปในคำขอถัดไป
state = load_cursor() # เช่น id ของเหตุการณ์ล่าสุด
params = {"since": state} if state else {}
r = requests.get("https://api.example.com/v1/events", params=params,
proxies=proxies, timeout=15)
events = r.json().get("items", [])
for e in events:
process(e)
state = e["id"]
save_cursor(state)แบบนี้คุณได้แค่ของใหม่ ปริมาณทราฟฟิกน้อยที่สุด และแทบไม่มีข้อมูลซ้ำ
หลักการที่ 2: ช่วงเวลาแบบปรับตัวได้
ถามถี่เมื่อเหตุการณ์ไหลมาเป็นสาย และถามห่างเมื่อเงียบสงบ ฮิวริสติกง่ายๆ: ถ้าคำตอบมีเหตุการณ์ ให้ลดช่วงห่าง ถ้าว่างเปล่า ให้เพิ่มขึ้นจนถึงเพดานที่สมเหตุสมผล
min_delay, max_delay = 2, 60
delay = min_delay
while True:
events = poll_once()
if events:
delay = min_delay
else:
delay = min(delay * 2, max_delay)
time.sleep(delay)การผ่อนแบบ exponential นี้ลดจำนวน request ที่ยิงเปล่าในช่วงสงบได้อย่างรวดเร็ว คุณจะประหลาดใจว่าโหลดลดลงมากแค่ไหนในขณะที่ยังคงตอบสนองได้
หลักการที่ 3: เคารพ header ของ limit
API ที่ดีจะส่ง header เกี่ยวกับสถานะ limit: เหลือ request อีกกี่อันและตัวนับจะรีเซ็ตเมื่อไหร่ อ่านมันและชะลอไว้ก่อน ไม่ใช่รอจนโดนปฏิเสธแล้วค่อยชะลอ
r = requests.get(url, proxies=proxies, timeout=15)
remaining = int(r.headers.get("X-RateLimit-Remaining", "1"))
reset = int(r.headers.get("X-RateLimit-Reset", "0"))
if remaining <= 1 and reset:
wait = max(reset - int(time.time()), 1)
time.sleep(wait)หลักการที่ 4: จัดการรหัส 429 และการ retry อย่างถูกต้อง
ถ้าเซิร์ฟเวอร์ตอบรหัส 429 (คำขอมากเกินไป) อย่ามองข้าม ดู header Retry-After และรอตามเวลาที่ระบุ สำหรับ network error ให้ retry แบบ exponential backoff พร้อม jitter เพื่อไม่ให้เกิดคลื่นซิงโครไนซ์
import random
def get_with_backoff(url, attempts=5):
delay = 1
for i in range(attempts):
r = requests.get(url, proxies=proxies, timeout=15)
if r.status_code == 429:
retry_after = int(r.headers.get("Retry-After", delay))
time.sleep(retry_after)
continue
if r.status_code >= 500:
time.sleep(delay + random.uniform(0, delay))
delay = min(delay * 2, 30)
continue
return r
raise RuntimeError("too many failures")หลักการที่ 5: การประมวลผลแบบ idempotent
ในการ polling อาจมีเหตุการณ์ซ้ำได้ โดยเฉพาะตรงขอบของ cursor ให้การประมวลผลแบบ idempotent: ก่อนลงมือตรวจสอบก่อนว่าเหตุการณ์นี้เคยประมวลผลไปแล้วหรือยังโดยดูจาก id นี่ช่วยป้องกันการหักเงินซ้ำ การแจ้งเตือนซ้ำ และเรื่องไม่พึงประสงค์อื่นๆ
def process(event):
if already_seen(event["id"]):
return
do_business_logic(event)
mark_seen(event["id"])เช็กลิสต์ polling ที่เชื่อถือได้
- ใช้ cursor หรือ timestamp ดึงเฉพาะของใหม่
- ใช้ช่วงเวลาแบบปรับตัวได้พร้อมการผ่อน exponential
- อ่านและเคารพ header ของ limit
- จัดการ 429 และ Retry-After อย่างถูกต้อง
- ทำ retry พร้อม jitter เมื่อเกิด network error
- ทำให้การประมวลผลเหตุการณ์เป็น idempotent
- ล็อก cursor และ metric เพื่อเห็นความล่าช้า
- นำคำขอขาออกผ่านพร็อกซี Proxeon เพื่อภูมิภาคและประเภทเครือข่ายที่ต้องการ ถ้าการเชื่อมต่อกำหนดไว้แบบนั้น
ตรงไหนที่พร็อกซียังจำเป็นข้างๆ เว็บฮุค
อาจดูเหมือนว่าถ้าพร็อกซีไม่ใช่ตัวรับเว็บฮุค มันก็ไม่เกี่ยวอะไรกับงานนี้เลย ซึ่งไม่จริง พร็อกซีมีบทบาทชัดเจน แค่เป็นอีกฝั่งของกระบวนการ
คำตอบขาออกและการเรียกกลับ
การประมวลผลเว็บฮุคไม่ค่อยจบแค่ 200 ง่ายๆ บ่อยครั้งคุณต้องยิงไปที่ API ของบุคคลที่สามเพื่อตอบสนองต่อเหตุการณ์: ยืนยันการรับ ขอรายละเอียดของออบเจกต์ อัปเดตสถานะบนแพลตฟอร์มอื่น คำขอเหล่านี้ทั้งหมดเป็นขาออก และนี่แหละที่พร็อกซี Proxeon เหมาะสมและมีประโยชน์
def on_payment_event(event):
order_id = event["order_id"]
# คำขอขาออกเพื่อดูรายละเอียดผ่านพร็อกซี
r = requests.get(f"https://api.partner.com/orders/{order_id}",
proxies=proxies, timeout=15)
details = r.json()
update_internal_state(order_id, details)ทำไมต้องเป็นพร็อกซีในการเรียกเหล่านี้
- ภูมิภาคขาออกที่มั่นคง API บางตัวส่งคอนเทนต์หรือราคาตามภูมิภาคของคำขอ พร็อกซีช่วยให้ติดต่อจากตำแหน่งที่ต้องการได้อย่างถูกกฎหมายและคาดเดาได้
- ประเภทเครือข่ายที่ต้องการ บริการบางอย่างตอบต่างกันระหว่างคำขอจากที่อยู่มือถือและที่อยู่นิ่ง พร็อกซีมือถือให้โปรไฟล์เครือข่ายที่ถูกต้องสำหรับงานของคุณ
- แยกกระแส การย้ายคำขอขาออกไปที่เกตเวย์ที่ควบคุมได้ ช่วยให้การมอนิเตอร์ การวินิจฉัย และการควบคุมโหลดง่ายขึ้น
การอ่านคิวและ API ผ่านพร็อกซี
อย่างที่บอกไปแล้ว วิธีคิวและ polling สร้างขึ้นบน connection ขาออกทั้งหมด แปลว่าทราฟฟิก所有这些ก็ผ่านพร็อกซีตามธรรมชาติ กลายเป็นสถาปัตยกรรมที่ลงตัว: การรับเหตุการณ์แก้ด้วยเซิร์ฟเวอร์ อุโมงค์ คิว หรือ polling ส่วนการสื่อสารขาออกทั้งหมดกับระบบภายนอกผ่านเกตเวย์พร็อกซีที่ควบคุมได้ แต่ละเครื่องมือทำในสิ่งที่มันถูกสร้างมาเพื่อ
สถาปัตยกรรมขนาดเล็กของการเชื่อมต่อที่โตเต็มวัย
- จุดรับเหตุการณ์: เซิร์ฟเวอร์สาธารณะ อุโมงค์ คิว หรือ polling
- รับอย่างรวดเร็วและโยนเข้าคิวภายใน ตอบ 200 โดยไม่หน่วง
- เวิร์กเกอร์ async ดึงจากคิวและทำ business logic
- คำขอขาออกทั้งหมดไปที่ API ของบุคคลที่สามผ่านพร็อกซี Proxeon ด้วยภูมิภาคและประเภทเครือข่ายที่ต้องการ
- idempotency, retry พร้อม jitter, metric และ alert เมื่อเกิดความล่าช้า
ข้อผิดพลาดทั่วไปและวิธีหลีกเลี่ยง
มารวบรวมกับดักที่คนมักเหยียบมาให้ครบ ตรวจสอบตัวเองกับรายการนี้
ข้อผิดพลาดที่ 1: รอเว็บฮุคที่ที่อยู่พร็อกซี
ที่พบบ่อยที่สุด คนใส่ที่อยู่พร็อกซีในตั้งค่าเว็บฮุคของบริการบุคคลที่สามแล้วรอการส่ง มันไม่มาเลย เพราะนั่นคือที่อยู่ขาออก ไม่ใช่จุดรับ ทางแก้: ใช้หนึ่งในสี่แนวทางรับที่ใช้ได้ และอย่าสับสนทางออกกับทางเข้า
ข้อผิดพลาดที่ 2: พยายามทำให้ที่อยู่มือถือเข้าถึงได้จากสาธารณะ
ความพยายามเข้าถึงผู้ใช้มือถือเฉพาะเจาะจงจากภายนอกเป็นเรื่องที่สิ้นหวังเพราะทางออกร่วมของผู้ให้บริการ อย่าเสียเวลากับมัน สำหรับการรับให้ใช้เซิร์ฟเวอร์สาธารณะ อุโมงค์ หรือเลิกใช้เว็บฮุคแล้วหันไปใช้ polling และคิว
ข้อผิดพลาดที่ 3: งานหนักในตัวจัดการเว็บฮุค
ถ้าคุณทำ logic ยาวแบบซิงโครนัสก่อนตอบ 200 ผู้ส่งจะมองว่าคุณเข้าถึงไม่ได้เพราะ timeout และเริ่มส่งซ้ำ คุณจะได้พายุข้อมูลซ้ำ ทางแก้: รับทันทีและโยนเข้าคิว งานหนักทำแบบ async
ข้อผิดพลาดที่ 4: ไม่ตรวจสอบลายเซ็น
ปลายทางเปิดที่ไม่มีตรวจสอบความถูกต้องจะรับอะไรก็ได้จากใครก็ได้ นี่คือความเสี่ยง ตรวจสอบลายเซ็นของเว็บฮุคขาเข้าด้วย secret ที่แชร์กันเสมอ ปฏิเสธคำขอที่ไม่มีลายเซ็น
ข้อผิดพลาดที่ 5: polling แบบ naive ที่ไม่คำนึงถึง limit
การหยุดคงที่หนึ่งวินาทีและการมองข้าม header ของ limit นำไปสู่การถูกปฏิเสธเพราะความถี่ ใช้ช่วงเวลาแบบปรับตัวได้ cursor และเคารพ Retry-After
ข้อผิดพลาดที่ 6: ไม่มี idempotency
ทั้งเว็บฮุคและ polling ส่งเหตุการณ์เดียวซ้ำได้ ถ้าไม่มีป้องกันด้วย id คุณเสี่ยงต่อการกระทำซ้ำ ตรวจสอบเสมอว่าเคยประมวลผลเหตุการณ์นี้มาก่อนหรือไม่
ข้อผิดพลาดที่ 7: ผสมขาเข้าและขาออกในโหนดเดียวโดยไม่แยก
เมื่อการรับเหตุการณ์และคำขอขาออกกองรวมกัน การวินิจฉัยกลายเป็นฝันร้าย แยกบทบาท: จุดรับแยกต่างหาก เกตเวย์ขาออกผ่านพร็อกซีแยกต่างหาก
ข้อผิดพลาดที่ 8: การรับที่ล้มเหลวอย่างเงียบๆ
ถ้าจุดรับล่มและคุณไม่รู้ตัว เหตุการณ์จะหายไปเงียบๆ ตั้งการมอนิเตอร์ความพร้อมของปลายทางและความล่าช้าของคิว เพื่อให้รู้ปัญหาก่อนใคร
เครื่องมือและแหล่งข้อมูล
อะไรที่ใช้ได้จริงในทางปฏิบัติ เรามาแยกตามชั้น
สำหรับการรับเหตุการณ์
- เว็บเฟรมเวิร์ก โครงเบาๆ สำหรับปลายทางรับที่รวดเร็ว: ใช้โซลูชันยอดนิยมใดก็ได้ในภาษาของคุณ สิ่งสำคัญคือตัวจัดการตอบเร็วและโยนงานเข้าคิวได้
- อุโมงค์ย้อนกลับ เครื่องมือที่เปิด connection ขาออกไปยังจุดสาธารณะและดันคำขอขาเข้ามาหาคุณ มีประโยชน์สำหรับการพัฒนาและสถานการณ์ชั่วคราว
- คิวและ event bus ของผู้ให้บริการ ถ้าแพลตฟอร์มเสนอการอ่านเหตุการณ์จากคิว นี่มักเป็นตัวเลือกที่ดีที่สุดในเรื่องความเชื่อถือได้
สำหรับการประมวลผลแบบ async
- broker ข้อความ คิวภายในระหว่างการรับและการประมวลผล ช่วยแยกการรับที่รวดเร็วออกจาก logic ที่ช้า
- เวิร์กเกอร์และตัวจัดตารางเวลา ตัวทำงานเบื้องหลังที่ดึงจากคิว ทำ retry และรักษา idempotency
สำหรับคำขอขาออก
- HTTP client ที่รองรับพร็อกซี ไลบรารีที่โตเต็มวัยเกือบทุกตัวทำงานผ่านพร็อกซีได้ ตั้งค่าที่อยู่เกตเวย์ timeout และ retry
- พร็อกซีเกตเวย์ Proxeon จุดขาออกที่ควบคุมได้สำหรับคำขอขาออกด้วยภูมิภาคและประเภทเครือข่ายที่ต้องการ รวมทั้งที่อยู่มือถือ สำหรับงานวิศวกรรมที่ถูกกฎหมาย
สำหรับการสังเกตการณ์
- ล็อกที่มีบริบท บันทึก id ของเหตุการณ์ cursor ของ polling โค้ดตอบกลับ และเวลาประมวลผล
- metric ความล่าช้าของคิว สัดส่วน 429 จำนวน retry ความหน่วงในการรับ เหล่านี้คือสัญญาณเตือนปัญหาล่วงหน้าของคุณ
- alert แจ้งเตือนเมื่อปลายทางเข้าถึงไม่ได้และเมื่อความล่าช้าเพิ่มขึ้น
เคสและผลลัพธ์
มาดูว่าหลักการทำงานได้จริงอย่างไร ตัวอย่างเป็นแบบรวบรวม แต่สะท้อนสถานการณ์ทั่วไปและลำดับขนาด
เคสที่ 1: เชื่อมต่อการแจ้งเตือนสถานะโดยไม่มีเซิร์ฟเวอร์ของตัวเอง
ทีมเล็กทีมหนึ่งเชื่อมต่อกับแพลตฟอร์มที่ส่งเว็บฮุคเมื่อสถานะเปลี่ยน ไม่มีเซิร์ฟเวอร์สาธารณะ และความพยายามแรกที่จะใส่ที่อยู่พร็อกซีในตั้งค่าเว็บฮุคก็ไม่เกิดอะไรขึ้นตามคาด ไม่มีการส่งมาเลย หลังแกะโมเดลทิศทาง ทีมเปลี่ยนไปใช้สองวิธีพร้อมกัน
สำหรับขั้นพัฒนาใช้อุโมงค์ย้อนกลับเพื่อดีบักตัวจัดการในเครื่อง สำหรับโปรดักชัน แพลตฟอร์มเสนอการอ่านจากคิว ทีมจึงย้ายการรับไปที่นั่น ผลลัพธ์: การหายของเหตุการณ์เมื่อบริการรีสตาร์ทสั้นๆ ลดลงเหลือศูนย์ เพราะคิวกักข้อความไว้ คำขอขาออกเพื่อดูรายละเอียดเพิ่มเติมทั้งหมดไปที่ API ของแพลตฟอร์มผ่านพร็อกซี Proxeon ด้วยภูมิภาคที่ต้องการ การวินิจฉัยง่ายขึ้นเพราะทางเข้าและทางออกถูกแยก
เคสที่ 2: เปลี่ยนจากเว็บฮุคมาเป็น polling เมื่อรับไม่ได้
บริการทำงานในสภาพแวดล้อมที่การรับขาเข้าเป็นไปไม่ได้ด้วยเหตุผลทางสถาปัตยกรรม ตอนแรกพยายามรับเว็บฮุค แต่ไม่มีการส่งมา ตัดสินใจเลิกสมัครสมาชิกและสร้าง polling เวอร์ชันแรกที่หยุดคงที่หนึ่งวินาทีชน limit เกือบทันทีและเริ่มได้การปฏิเสธเพราะความถี่
หลังปรับปรุงใหม่ก็ใส่ polling แบบ incremental ด้วย cursor ช่วงเวลาแบบปรับตัวได้ตั้งแต่ 2 ถึง 60 วินาที เคารพ header ของ limit และจัดการ 429 อย่างถูกต้อง จำนวน request ในช่วงเวลาสงบลดลงหลายเท่าจากการผ่อนแบบ exponential การปฏิเสธเพราะความถี่หายไป ความหน่วงในการรับเหตุการณ์ใหม่ในช่วงแอคทีฟอยู่ในหลักวินาที ซึ่งธุรกิจพอใจเต็มที่ คำขอทั้งหมดผ่านพร็อกซี ให้โปรไฟล์เครือข่ายที่ต้องการ
เคสที่ 3: พายุข้อมูลซ้ำเพราะตัวจัดการที่ช้า
การเชื่อมต่อกับเซิร์ฟเวอร์สาธารณะทำงานได้ แต่บางครั้งตัวจัดการทำ logic ซิงโครนัสที่หนักและตอบ 200 ไม่ทันในเวลาที่กำหนด ผู้ส่งมองว่าการส่งล้มเหลวและส่งซ้ำ เกิดข้อมูลซ้ำและการกระทำซ้ำ กับดักคลาสสิก
ทางแก้ตรงไปตรงมา ตัวจัดการเปลี่ยนมารับเหตุการณ์ทันที ตรวจสอบลายเซ็น โยนงานเข้าคิวภายใน และตอบ 200 ทันที logic หนักย้ายไปเวิร์กเกอร์ async เพิ่ม idempotency ด้วย id ของเหตุการณ์ ข้อมูลซ้ำไม่ทำให้เกิดการกระทำซ้ำอีก และเวลาตอบของปลายทางก็ต่ำอย่างสม่ำเสมอ พายุการส่งซ้ำก็หยุดลง
ข้อสรุปรวมจากเคสทั้งหมด
ในทุกเรื่อง รากของปัญหามีอันเดียว: ความสับสนระหว่างทิศทางขาออกและขาเข้า และความพยายามมอบบทบาทตัวรับให้พร็อกซีซึ่งไม่ใช่หน้าที่ของมัน พอทีมแยกบทบาทและเลือกเครื่องมือให้ตรงกับทิศทาง ทุกอย่างก็เข้าที่ พร็อกซีดูแลทางออก ส่วนทางเข้าแก้ด้วยเซิร์ฟเวอร์ อุโมงค์ คิว หรือ polling
ตาราง: งานและทางแก้ที่เหมาะสม
เก็บบันทึกสั้นๆ นี้ไว้ใช้ นี่ช่วยประหยัดเวลาถกเถียงหลายชั่วโมง
การจับคู่งานกับเครื่องมือ
- คำขอขาออกไป API บุคคลที่สามด้วยภูมิภาคที่ต้องการ ทางแก้: พร็อกซี Proxeon ทิศทาง: ขาออก ไม่ต้องใช้ที่อยู่สาธารณะ
- คำขอขาออกภายใต้ประเภทเครือข่ายมือถือ ทางแก้: พร็อกซีมือถือ Proxeon ทิศทาง: ขาออก ไม่ต้องใช้ที่อยู่สาธารณะ
- รับเว็บฮุคเมื่อมีเซิร์ฟเวอร์สาธารณะ ทางแก้: เซิร์ฟเวอร์ที่มีที่อยู่สาธารณะและ TLS ทิศทาง: ขาเข้า ต้องมีที่อยู่สาธารณะ
- รับเว็บฮุคโดยไม่มีเซิร์ฟเวอร์ของตัวเอง สำหรับการพัฒนา ทางแก้: อุโมงค์ย้อนกลับ ทิศทาง: ขาเข้าภายในช่องทางขาออกที่เปิดไว้ล่วงหน้า
- รับเหตุการณ์พร้อมการรับประกันความอยู่รอดเมื่อ idle ทางแก้: คิวหรือ event bus ของผู้ให้บริการ อ่านด้วย consumer ของตัวเอง ทิศทาง: อ่านขาออก
- รับเหตุการณ์เมื่อขาเข้าเป็นไปไม่ได้เลย ทางแก้: polling ผ่านพร็อกซีด้วย cursor และช่วงเวลาแบบปรับตัวได้ ทิศทาง: ขาออก
- การเรียกเพิ่มเติมเพื่อตอบสนองต่อเหตุการณ์ ทางแก้: คำขอขาออกผ่านพร็อกซี Proxeon ทิศทาง: ขาออก
- เข้าถึงผู้ใช้มือถือเฉพาะเจาะจงจากภายนอก ทางแก้: เป็นไปไม่ได้เพราะทางออกร่วมของผู้ให้บริการ ใช้แนวทางรับอื่น
กฎการเลือกในหนึ่งบรรทัด
ถ้าคุณเป็นคนริเริ่ม connection เครื่องมือของคุณคือพร็อกซี ถ้าคนริเริ่มคือโลกภายนอก คุณต้องมีเซิร์ฟเวอร์ อุโมงค์ คิว หรือแทนการสมัครสมาชิกด้วย polling
FAQ: คำถามที่พบบ่อย
ตั้งค่าพร็อกซีให้เว็บฮุคส่งมาที่มันได้ไหม
ไม่ได้ พร็อกซีไคลเอนต์ให้บริการคำขอขาออกของคุณ และไม่ใช่จุดรับที่ฟังจากสาธารณะสำหรับ connection ของคนอื่นที่ส่งมาหาคุณ ที่อยู่พร็อกซีคือที่อยู่ขาออก ไม่ใช่ที่อยู่ขาเข้า สำหรับการรับเว็บฮุคให้ใช้เซิร์ฟเวอร์สาธารณะ อุโมงค์ย้อนกลับ คิวของผู้ให้บริการ หรือ polling
ทำไมเว็บฮุคไปไม่ถึงที่อยู่มือถือ
เพราะในเครือข่ายมือถือ ผู้ใช้ออกอินเทอร์เน็ตผ่านที่อยู่ร่วมของผู้ให้บริการ ส่วนที่อยู่เฉพาะของอุปกรณ์เป็นที่อยู่ภายในและไม่ถูกกำหนดเส้นทางจากภายนอก connection ขาเข้าใหม่จากภายนอกไม่รู้ว่าตั้งใจให้ผู้ใช้คนไหน เพราะงั้นการส่งถึงอุปกรณ์เฉพาะจึงเป็นไปไม่ได้ นี่เป็นธรรมชาติของสถาปัตยกรรมเครือข่าย ไม่ใช่ข้อจำกัดของแพ็กเกจ
ถ้าไม่มีเซิร์ฟเวอร์สาธารณะ จะรับเหตุการณ์ได้อย่างไร
มีสามทาง ทางแรก อุโมงค์ย้อนกลับที่ดันขาเข้าผ่านช่องทางขาออกที่คุณเปิดไว้ล่วงหน้า เหมาะสำหรับการพัฒนา ทางที่สอง การอ่านจากคิวหรือ event bus ถ้าผู้ให้บริการเสนอ เป็นตัวเลือกที่เชื่อถือได้ที่สุด ทางที่สาม polling API ด้วยคำขอขาออกของตัวเอง สองทางหลังทำงานผ่านพร็อกซีได้อย่างยอดเยี่ยม
polling มันไม่มีประสิทธิภาพไม่ใช่หรือ
polling แบบ naive เปลืองจริง แต่ polling ที่ดีด้วย incremental cursor ช่วงเวลาแบบปรับตัวได้ เคารพ limit และ idempotency นั้นประหยัดและเชื่อถือได้ ในช่วงสงบจำนวน request ลดลงอย่างรวดเร็วจากการผ่อน exponential ส่วนช่วงแอคทีฟคุณได้เหตุการณ์ภายในไม่กี่วินาที สำหรับงานหลายอย่างนี่เกินพอแล้ว
ต้องใช้พร็อกซีไหมถ้ารับเว็บฮุค
สำหรับการรับเองไม่ต้อง เพราะการรับเป็นทิศทางขาเข้า แต่พร็อกซีมีประโยชน์มากสำหรับคำขอขาออกที่คุณยิงตอบสนองต่อเหตุการณ์: ขอดูรายละเอียดของออบเจกต์ เพิ่อยืนยัน เพื่ออัปเดตสถานะบนแพลตฟอร์มอื่น และสำหรับ polling และการอ่านคิว เพราะนั่นคือทราฟฟิกขาออก
จะปกป้องปลายทางรับเว็บฮุคได้อย่างไร
ตรวจสอบลายเซ็นของคำขอขาเข้าด้วย secret ที่แชร์กัน และปฏิเสธคำขอที่ไม่มีลายเซ็น ใช้ TLS ตอบอย่างรวดเร็วและย้ายการประมวลผลไปที่คิว async ทำการประมวลผลแบบ idempotent เพื่อไม่ให้การส่งซ้ำนำไปสู่การกระทำซ้ำ ล็อกและมอนิเตอร์ความพร้อม
ทำอย่างไรถ้าเหตุการณ์บางครั้งมาสองครั้ง
นี่เป็นสถานการณ์ปกติทั้งสำหรับเว็บฮุคและ polling ให้ทำให้ idempotent: ก่อนทำ logic ตรวจสอบด้วย id ของเหตุการณ์ว่าเคยประมวลผลมาก่อนหรือไม่ และทำเครื่องหมายอันที่ประมวลผลแล้ว ข้อมูลซ้ำก็จะปลอดภัย
ใช้พร็อกซีมือถือกับคำขอขาออกในการเชื่อมต่อกับเว็บฮุคได้ไหม
ได้ และนี่เป็นสถานการณ์ที่ถูกกฎหมายพบบ่อย พร็อกซีมือถือ Proxeon ให้ประเภทเครือข่ายและภูมิภาคที่ต้องการสำหรับคำขอขาออกไปยัง API บุคคลที่สามและสำหรับ polling ส่วนการรับเว็บฮุคเองแก้ด้วยองค์ประกอบแยก เพราะการรับเป็นทางเข้า และทางออกมือถือเป็นเรื่องร่วมที่กำหนดที่อยู่จากภายนอกไม่ได้
คิวของผู้ให้บริการกับเว็บฮุคต่างกันเรื่องความน่าเชื่อถืออย่างไร
เว็บฮุคส่งในจังหวะที่เกิดเหตุการณ์ และถ้าตัวรับของคุณไม่พร้อม เหตุการณ์อาจหายไปหรือต้องให้ผู้ส่งทำ retry คิวกักเหตุการณ์ไว้จนกว่าคุณจะอ่านและยืนยัน เพราะงั้นช่วง idle สั้นๆ ของ consumer ข้อมูลจะไม่หาย ถ้าผู้ให้บริการเสนอคิว มันมักจะน่าเชื่อถือกว่า
บทความอธิบายการตั้งค่าเราเตอร์และ forward port ไหม
ไม่ นั่นเป็นหัวข้อข้างเคียงที่มีความเฉพาะเจาะจงของมันเอง และเราตั้งใจเว้นไว้ ตรงนี้สำคัญคือเข้าใจโมเดลทิศทางและเลือกเครื่องมือ ถ้าคุณตั้งจุดรับของตัวเองหลังอุปกรณ์ที่บ้าน เรื่องการกำหนดเส้นทางต้องแก้แยกและต้องวิเคราะห์ของมันเอง
บทสรุป: รวบรวมทุกอย่างเข้าด้วยกัน
เราเดินทางจากความเข้าใจผิดที่พบบ่อยไปจนถึงโมเดลวิศวกรรมที่ลงตัว ข้อสรุปหลักนั้นเรียบง่ายและทรงพลัง: พร็อกซีแก้ปัญหาคำขอขาออก แต่ไม่ทำให้บริการของคุณเข้าถึงได้จากภายนอก ทุกอย่างขึ้นอยู่กับทิศทางของ connection เมื่อคนริเริ่มคือคุณ พร็อกซีอยู่ถูกที่ เมื่อคนริเริ่มคือโลกภายนอก ต้องใช้เครื่องมืออื่น
เราแกะแล้วว่าทำไมพร็อกซีไคลเอนต์ถึงไม่ฟังพอร์ตและไม่ให้ที่อยู่สาธารณะ และทำไมที่อยู่ผู้ใช้มือถือถึงกำหนดเส้นทางจากภายนอกไม่ได้ตั้งแต่รากเพราะทางออกร่วมของผู้ให้บริการ เราได้ดูสี่แนวทางที่ใช้ได้ในการรับเว็บฮุค: เซิร์ฟเวอร์ที่มีที่อยู่สาธารณะ อุโมงค์ย้อนกลับ คิวฝั่งผู้ให้บริการ และ polling แทนการสมัครสมาชิก เราได้ออกแบบ polling ที่เชื่อถือได้ซึ่งไม่ชน limit ด้วย cursor ช่วงเวลาแบบปรับตัวได้ การเคารพ header และ idempotency และเราเห็นว่าพร็อกซี Proxeon มีประโยชน์จริงตรงไหนข้างๆ เว็บฮุค: ในการตอบกลับขาออก การเรียก API บุคคลที่สาม การอ่านคิว และการ polling
ทำอะไรต่อไปดี? กำหนดทิศทางของงานคุณ ถ้านี่คือขาเข้า ให้มองหาส่วนที่เหลือของบทความ