สรุป
ใช้ Server-Sent Events (SSE) สำหรับการอัปเดตแบบทางเดียวจากเซิร์ฟเวอร์ไปยังไคลเอนต์ เช่น การแจ้งเตือนและฟีดข้อมูลสด ใช้ WebSocket สำหรับการสื่อสารแบบสองทิศทาง เช่น แชทและเกม SSE นั้นง่ายกว่าและทำงานผ่าน HTTP ส่วน WebSocket ซับซ้อนกว่า แต่รองรับการส่งข้อความแบบสองทาง Modern PetstoreAPI ใช้งานทั้งสองแบบสำหรับกรณีการใช้งานแบบเรียลไทม์ที่แตกต่างกัน
บทนำ
คุณต้องการการอัปเดตแบบเรียลไทม์ใน API ของคุณ สถานะของสัตว์เลี้ยงเปลี่ยนจาก “พร้อมใช้งาน” เป็น “รับไปแล้ว”—ไคลเอนต์จำเป็นต้องทราบทันที คุณจะใช้ WebSocket หรือ Server-Sent Events (SSE) ดี?
นักพัฒนาส่วนใหญ่เลือกใช้ WebSocket เพราะคิดว่า “มีประสิทธิภาพมากกว่า” แต่ SSE มักเป็นทางเลือกที่ดีกว่า มันง่ายกว่า ทำงานผ่าน HTTP มาตรฐาน และจัดการการเชื่อมต่อใหม่โดยอัตโนมัติ WebSocket เพิ่มความซับซ้อนที่คุณอาจไม่จำเป็นต้องใช้
Modern PetstoreAPI ใช้ทั้งสองโปรโตคอล SSE สำหรับการอัปเดตสถานะสัตว์เลี้ยงและการแจ้งเตือนคำสั่งซื้อ WebSocket สำหรับการประมูลสดและแชทแบบเรียลไทม์ แต่ละโปรโตคอลมีกรณีการใช้งานที่แตกต่างกัน
ในคู่มือนี้ คุณจะได้เรียนรู้ความแตกต่างระหว่าง SSE และ WebSocket ดูตัวอย่างจริงจาก Modern PetstoreAPI และค้นพบว่าควรใช้โปรโตคอลใดในสถานการณ์ใด
Server-Sent Events (SSE) คืออะไร?
SSE เป็นโปรโตคอลที่ใช้ HTTP สำหรับการสตรีมเหตุการณ์จากเซิร์ฟเวอร์ไปยังไคลเอนต์
SSE ทำงานอย่างไร
ไคลเอนต์เปิดการเชื่อมต่อและรับเหตุการณ์ที่เกิดขึ้น:
const eventSource = new EventSource('https://petstoreapi.com/v1/pets/notifications');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Pet update:', data);
};
eventSource.addEventListener('adoption', (event) => {
const data = JSON.parse(event.data);
console.log('Pet adopted:', data.petId);
});
เซิร์ฟเวอร์ส่งเหตุการณ์:
GET /v1/pets/notifications
Accept: text/event-stream
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
event: adoption
data: {"petId":"019b4132","userId":"user-456"}
event: status-change
data: {"petId":"019b4127","status":"AVAILABLE"}
คุณสมบัติของ SSE
1. การสื่อสารแบบทางเดียว
เซิร์ฟเวอร์ผลักข้อมูลไปยังไคลเอนต์ ไคลเอนต์ไม่สามารถส่งข้อความกลับผ่านการเชื่อมต่อ SSE ได้ (แต่สามารถใช้คำขอ HTTP ปกติได้)
2. สร้างบน HTTP
ใช้ HTTP มาตรฐาน ทำงานผ่านพร็อกซี ไฟร์วอลล์ และ CDN
3. การเชื่อมต่อใหม่โดยอัตโนมัติ
หากการเชื่อมต่อหลุด เบราว์เซอร์จะเชื่อมต่อใหม่โดยอัตโนมัติ
4. รหัสเหตุการณ์สำหรับการดำเนินการต่อ
เซิร์ฟเวอร์สามารถส่งรหัสเหตุการณ์ได้ ไคลเอนต์จะดำเนินการต่อจากเหตุการณ์ที่ได้รับล่าสุด:
id: 123
event: adoption
data: {"petId":"019b4132"}
id: 124
event: status-change
data: {"petId":"019b4127"}
หากการเชื่อมต่อหลุด ไคลเอนต์จะส่งส่วนหัว Last-Event-ID: 124 เพื่อดำเนินการต่อ
5. โปรโตคอลที่เรียบง่าย
รูปแบบข้อความ แก้ไขข้อผิดพลาดได้ง่ายด้วย curl:
curl -N -H "Accept: text/event-stream" \
https://petstoreapi.com/v1/pets/notifications
ข้อจำกัดของ SSE
1. แบบทางเดียวเท่านั้น
ไคลเอนต์ไม่สามารถส่งข้อความผ่าน SSE ได้ จำเป็นต้องมีคำขอ HTTP แยกต่างหากสำหรับการสื่อสารจากไคลเอนต์ไปยังเซิร์ฟเวอร์
2. ข้อความเท่านั้น
SSE ส่งข้อความเป็นข้อความ ข้อมูลไบนารีต้องถูกเข้ารหัสแบบ base64
3. ข้อจำกัดการเชื่อมต่อของเบราว์เซอร์
เบราว์เซอร์จำกัดการเชื่อมต่อ SSE ต่อโดเมน (โดยทั่วไปคือ 6) ซึ่งไม่ใช่ปัญหาสำหรับแอปส่วนใหญ่
4. ไม่มีการบีบอัดในตัว
การบีบอัด HTTP ทำงานได้ แต่ไม่มีการบีบอัดระดับโปรโตคอลเหมือน WebSocket
WebSocket คืออะไร?
WebSocket เป็นโปรโตคอลแบบสองทิศทาง (full-duplex) ผ่านการเชื่อมต่อแบบถาวร
WebSocket ทำงานอย่างไร
ทั้งไคลเอนต์และเซิร์ฟเวอร์สามารถส่งข้อความได้ตลอดเวลา:
const ws = new WebSocket('wss://petstoreapi.com/auctions/019b4132');
// ส่งข้อความไปยังเซิร์ฟเวอร์
ws.send(JSON.stringify({
type: 'bid',
amount: 500
}));
// รับข้อความจากเซิร์ฟเวอร์
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
console.log('Auction update:', data);
};
ws.onclose = () => {
console.log('Connection closed');
// ต้องใช้ logic การเชื่อมต่อใหม่ด้วยตนเอง
};
เซิร์ฟเวอร์สามารถส่งได้ตลอดเวลา:
{"type":"bid","userId":"user-456","amount":550}
{"type":"outbid","newAmount":550}
ไคลเอนต์สามารถส่งได้ตลอดเวลา:
{"type":"bid","amount":600}
{"type":"watch","petId":"019b4132"}
คุณสมบัติของ WebSocket
1. แบบสองทิศทาง
ทั้งไคลเอนต์และเซิร์ฟเวอร์สามารถส่งข้อความได้ตลอดเวลา การสื่อสารแบบสองทางที่แท้จริง
2. ความหน่วงต่ำ
การเชื่อมต่อแบบถาวร ไม่มีภาระ HTTP เพิ่มเติมต่อข้อความ เหมาะสำหรับเกม แชท การทำงานร่วมกันแบบสด
3. รองรับไบนารี
สามารถส่งข้อมูลไบนารีได้โดยตรง ไม่จำเป็นต้องเข้ารหัส base64
4. โปรโตคอลที่กำหนดเอง
ใช้ ws:// หรือ wss:// (ปลอดภัย) ไม่ใช่ HTTP หลังจากการ Handshake เริ่มต้น
5. แบบ Frame
ข้อความเป็น Frame สามารถส่งข้อความบางส่วนและประกอบกลับเข้าด้วยกันได้
ข้อจำกัดของ WebSocket
1. การตั้งค่าที่ซับซ้อน
ต้องใช้ WebSocket เซิร์ฟเวอร์ ซับซ้อนกว่า HTTP endpoints
2. การเชื่อมต่อใหม่ด้วยตนเอง
ไม่มีการเชื่อมต่อใหม่โดยอัตโนมัติ คุณต้องใช้ retry logic เอง
3. ปัญหาพร็อกซี
พร็อกซีขององค์กรบางแห่งบล็อก WebSocket พร็อกซี HTTP ไม่เข้าใจ ws://
4. มีสถานะ (Stateful)
เซิร์ฟเวอร์ต้องติดตามการเชื่อมต่อ ปรับขนาดได้ยากกว่า HTTP ที่ไม่มีสถานะ (stateless)
5. ไม่มีคุณสมบัติ HTTP
ไม่สามารถใช้ HTTP caching, รหัสสถานะ หรือส่วนหัวมาตรฐานหลังจากการ Handshake ได้
การเปรียบเทียบ
| คุณสมบัติ | SSE | WebSocket |
|---|---|---|
| ทิศทาง | เซิร์ฟเวอร์ → ไคลเอนต์ | สองทิศทาง |
| โปรโตคอล | HTTP | กำหนดเอง (ws://) |
| การเชื่อมต่อใหม่ | อัตโนมัติ | ด้วยตนเอง |
| การรองรับเบราว์เซอร์ | เบราว์เซอร์สมัยใหม่ทั้งหมด | เบราว์เซอร์สมัยใหม่ทั้งหมด |
| ใช้งานกับพร็อกซีได้ดี | ใช่ | บางครั้ง |
| ความซับซ้อน | ง่าย | ซับซ้อน |
| ข้อมูลไบนารี | ไม่ (ข้อความเท่านั้น) | ใช่ |
| ความหน่วง | ต่ำ | ต่ำมาก |
| ความสามารถในการปรับขนาด | สูง (ไม่มีสถานะ) | ปานกลาง (มีสถานะ) |
| กรณีการใช้งาน | การแจ้งเตือน, ฟีด | แชท, เกม, การทำงานร่วมกัน |
Modern PetstoreAPI ใช้งานทั้งสองแบบอย่างไร
Modern PetstoreAPI ใช้ทั้ง SSE และ WebSocket สำหรับสถานการณ์ที่แตกต่างกัน
SSE สำหรับการอัปเดตสัตว์เลี้ยง
Endpoint: GET /v1/pets/notifications
const events = new EventSource(
'https://petstoreapi.com/v1/pets/notifications?userId=user-456'
);
events.addEventListener('adoption', (e) => {
const data = JSON.parse(e.data);
showNotification(`${data.petName} ถูกรับไปแล้ว!`);
});
events.addEventListener('status-change', (e) => {
const data = JSON.parse(e.data);
updatePetStatus(data.petId, data.status);
});
การใช้งานฝั่งเซิร์ฟเวอร์:
app.get('/v1/pets/notifications', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const userId = req.query.userId;
// สมัครรับการอัปเดตสัตว์เลี้ยง
const subscription = petUpdates.subscribe(userId, (event) => {
res.write(`event: ${event.type}\n`);
res.write(`data: ${JSON.stringify(event.data)}\n\n`);
});
req.on('close', () => {
subscription.unsubscribe();
});
});
กรณีการใช้งาน:
- การเปลี่ยนแปลงสถานะสัตว์เลี้ยง (พร้อมใช้งาน → รับไปแล้ว)
- การแจ้งเตือนคำสั่งซื้อ (สั่งแล้ว, จัดส่งแล้ว, ส่งถึงแล้ว)
- การอัปเดตสต็อกสินค้า
- การเปลี่ยนแปลงราคา
WebSocket สำหรับการประมูลสด
Endpoint: wss://petstoreapi.com/auctions/{auctionId}
const ws = new WebSocket('wss://petstoreapi.com/auctions/019b4132');
// เสนอราคา
function placeBid(amount) {
ws.send(JSON.stringify({
type: 'bid',
amount
}));
}
// รับการอัปเดต
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
switch (msg.type) {
case 'bid':
updateCurrentBid(msg.amount, msg.userId);
break;
case 'outbid':
showOutbidNotification(msg.newAmount);
break;
case 'auction-end':
showAuctionResult(msg.winner);
break;
}
};
การใช้งานฝั่งเซิร์ฟเวอร์:
wss.on('connection', (ws, req) => {
const auctionId = req.params.auctionId;
const auction = auctions.get(auctionId);
ws.on('message', (data) => {
const msg = JSON.parse(data);
if (msg.type === 'bid') {
const result = auction.placeBid(msg.userId, msg.amount);
// กระจายไปยังผู้เข้าร่วมทั้งหมด
auction.participants.forEach(participant => {
participant.send(JSON.stringify({
type: 'bid',
userId: msg.userId,
amount: msg.amount
}));
});
}
});
});
กรณีการใช้งาน:
- การประมูลสด
- แชทแบบเรียลไทม์พร้อมการสนับสนุน
- การวางแผนการดูแลสัตว์เลี้ยงแบบร่วมมือ
- การอัปเดตสต็อกสินค้าแบบเรียลไทม์ระหว่างการขาย
การทดสอบ API แบบเรียลไทม์ด้วย Apidog
Apidog รองรับการทดสอบทั้ง SSE และ WebSocket APIs
การทดสอบ SSE
1. สร้างคำขอ SSE:
GET https://petstoreapi.com/v1/pets/notifications
Accept: text/event-stream
2. ตรวจสอบเหตุการณ์:
- ตรวจสอบประเภทเหตุการณ์
- ตรวจสอบ JSON payloads
- ทดสอบพฤติกรรมการเชื่อมต่อใหม่
- ยืนยันรหัสเหตุการณ์
3. ทดสอบสถานการณ์:
- การเชื่อมต่อหลุด
- เซิร์ฟเวอร์รีสตาร์ท
- ลำดับเหตุการณ์
- ดำเนินการต่อจากเหตุการณ์ล่าสุด
การทดสอบ WebSocket
1. สร้างการเชื่อมต่อ WebSocket:
wss://petstoreapi.com/auctions/019b4132
2. ส่งข้อความทดสอบ:
{"type":"bid","amount":500}
{"type":"watch","petId":"019b4132"}
3. ตรวจสอบการตอบกลับ:
- ตรวจสอบรูปแบบข้อความ
- ทดสอบการไหลแบบสองทิศทาง
- ตรวจสอบการจัดการการเชื่อมต่อ
- ทดสอบสถานการณ์ข้อผิดพลาด
4. ทดสอบสถานการณ์:
- การเชื่อมต่อพร้อมกันหลายรายการ
- ลำดับข้อความ
- หมดเวลาการเชื่อมต่อ
- logic การเชื่อมต่อใหม่
เมื่อไหร่ควรใช้แต่ละแบบ
ควรใช้ SSE เมื่อ:
- การอัปเดตแบบทางเดียว - เซิร์ฟเวอร์ผลักข้อมูลไปยังไคลเอนต์, ไคลเอนต์ไม่จำเป็นต้องส่งกลับ
- การตั้งค่าที่ง่าย - ต้องการใช้โครงสร้างพื้นฐาน HTTP มาตรฐาน
- การเชื่อมต่อใหม่โดยอัตโนมัติ - ไม่ต้องการใช้ retry logic
- ใช้งานกับพร็อกซีได้ดี - จำเป็นต้องทำงานผ่านไฟร์วอลล์ขององค์กร
- การแจ้งเตือน - การอัปเดตสถานะ, การแจ้งเตือน, ฟีดข้อมูลสด
ตัวอย่าง:
- การแจ้งเตือนการรับเลี้ยงสัตว์เลี้ยง
- การอัปเดตสถานะคำสั่งซื้อ
- การเปลี่ยนแปลงสต็อกสินค้า
- การแจ้งเตือนราคา
- ฟีดข่าว
ควรใช้ WebSocket เมื่อ:
- แบบสองทิศทาง - ทั้งไคลเอนต์และเซิร์ฟเวอร์ส่งข้อความบ่อยครั้ง
- ความหน่วงต่ำเป็นสิ่งสำคัญ - เกม, การทำงานร่วมกันแบบเรียลไทม์
- ข้อมูลไบนารี - การส่งรูปภาพ, เสียง, วิดีโอ
- โปรโตคอลที่กำหนดเอง - ต้องการควบคุมรูปแบบข้อความอย่างสมบูรณ์
- ความถี่ข้อความสูง - ข้อความหลายร้อยข้อความต่อวินาที
ตัวอย่าง:
- การประมูลสด
- แชทแบบเรียลไทม์
- เกมผู้เล่นหลายคน
- การแก้ไขแบบร่วมมือ
- การสตรีมวิดีโอสด
อย่าใช้ WebSocket เพียงเพราะว่า:
❌ “มันล้ำหน้ากว่า” - ความซับซ้อนที่ไม่มีประโยชน์
❌ “ใครๆ ก็ใช้มัน” - SSE มักจะง่ายกว่า
❌ “มันเร็วกว่า” - SSE เร็วพอสำหรับการใช้งานส่วนใหญ่
❌ “มันเป็นแบบสองทิศทาง” - คุณจำเป็นต้องใช้แบบสองทิศทางจริงหรือ?
สรุป
ทั้ง SSE และ WebSocket ช่วยให้สามารถสื่อสารแบบเรียลไทม์ได้ แต่ถูกออกแบบมาสำหรับสถานการณ์ที่แตกต่างกัน SSE โดดเด่นในการอัปเดตแบบทางเดียวจากเซิร์ฟเวอร์ไปยังไคลเอนต์ พร้อมการเชื่อมต่อใหม่โดยอัตโนมัติและความเข้ากันได้กับ HTTP ส่วน WebSocket โดดเด่นสำหรับการสื่อสารแบบสองทิศทางที่มีความหน่วงต่ำ
Modern PetstoreAPI แสดงให้เห็นถึงวิธีการใช้ทั้งสองโปรโตคอลอย่างมีประสิทธิภาพ SSE สำหรับการแจ้งเตือนและการอัปเดตสถานะ WebSocket สำหรับการประมูลสดและแชท เลือกตามกรณีการใช้งานของคุณ ไม่ใช่เลือกตามโปรโตคอลที่ดูเหมือน “ดีกว่า”
ทดสอบ API แบบเรียลไทม์ของคุณด้วย Apidog เพื่อให้แน่ใจว่าการใช้งานทั้ง SSE และ WebSocket ทำงานได้อย่างถูกต้องในสถานการณ์ต่างๆ
คำถามที่พบบ่อย
SSE สามารถทำงานผ่านไฟร์วอลล์ขององค์กรได้หรือไม่?
ได้ SSE ใช้ HTTP มาตรฐาน ดังนั้นจึงทำงานผ่าน HTTP proxies และไฟร์วอลล์ได้ ส่วน WebSocket ใช้โปรโตคอลที่กำหนดเองซึ่งพร็อกซีบางตัวอาจบล็อก
WebSocket เร็วกว่า SSE หรือไม่?
WebSocket มีความหน่วงต่ำกว่าเล็กน้อย (ไม่มีภาระ HTTP เพิ่มเติมต่อข้อความ) แต่สำหรับแอปพลิเคชันส่วนใหญ่ ความแตกต่างนั้นน้อยมากจนไม่สำคัญ SSE เร็วพอสำหรับการแจ้งเตือน, ฟีดข้อมูล และการอัปเดตสถานะ
คุณจัดการการเชื่อมต่อใหม่ของ SSE อย่างไร?
เบราว์เซอร์จัดการการเชื่อมต่อใหม่โดยอัตโนมัติ ส่งรหัสเหตุการณ์จากเซิร์ฟเวอร์ และไคลเอนต์จะดำเนินการต่อจากเหตุการณ์ล่าสุดที่ได้รับโดยใช้ส่วนหัว Last-Event-ID
คุณสามารถใช้ SSE กับแอปพลิเคชันมือถือได้หรือไม่?
ได้ iOS และ Android รองรับ SSE ผ่าน HTTP clients หรือ libraries ที่เป็น native SSE ทำงานได้ทุกที่ที่ HTTP ทำงานได้
ระยะเวลาการเชื่อมต่อ SSE สูงสุดคือเท่าใด?
ไม่มีขีดจำกัดตายตัว การเชื่อมต่อ SSE สามารถเปิดอยู่ได้ไม่จำกัด บางพร็อกซีหรือโหลดบาลานเซอร์อาจมีค่าหมดเวลา (โดยทั่วไปคือ 30-60 วินาที) แต่เบราว์เซอร์จะเชื่อมต่อใหม่โดยอัตโนมัติ
WebSocket สามารถส่งข้อมูลไบนารีได้หรือไม่?
ได้ WebSocket รองรับทั้งข้อความและ binary frames คุณสามารถส่งรูปภาพ, เสียง หรือข้อมูลไบนารีใดๆ โดยไม่ต้องเข้ารหัส base64
เบราว์เซอร์สามารถมีการเชื่อมต่อ SSE ได้กี่รายการ?
เบราว์เซอร์จำกัดการเชื่อมต่อ SSE ต่อโดเมน (โดยทั่วไปคือ 6) ซึ่งไม่ค่อยเป็นปัญหาสำหรับแอปส่วนใหญ่ที่ต้องการการเชื่อมต่อ SSE เพียง 1-2 รายการ
คุณจำเป็นต้องมีเซิร์ฟเวอร์พิเศษสำหรับ SSE หรือไม่?
ไม่ เซิร์ฟเวอร์ HTTP ใดๆ ก็สามารถจัดการ SSE ได้ เพียงแค่ตั้งค่าส่วนหัวที่ถูกต้อง (`Content-Type: text/event-stream`) และเปิดการเชื่อมต่อไว้
