Tóm tắt
Sử dụng Server-Sent Events (SSE) để truyền luồng phản hồi API qua HTTP. Gửi Content-Type: text/event-stream và ghi các sự kiện dưới dạng data: {json}\n\n. SSE hoạt động tốt cho việc truyền luồng phản hồi AI, cập nhật tiến độ và các luồng dữ liệu trực tiếp. Modern PetstoreAPI sử dụng SSE cho các đề xuất thú cưng AI và cập nhật trạng thái đơn hàng.
Giới thiệu
API của bạn tạo ra các đề xuất thú cưng bằng AI. Phản hồi mất 10 giây. Bạn có bắt người dùng chờ đợi, hay truyền trực tiếp kết quả khi chúng được tạo ra?
Với Server-Sent Events (SSE), bạn truyền trực tiếp phản hồi theo thời gian thực. Người dùng sẽ thấy kết quả ngay lập tức khi AI tạo ra chúng, mang lại trải nghiệm tốt hơn.
Modern PetstoreAPI sử dụng SSE cho các đề xuất thú cưng bằng AI, cập nhật trạng thái đơn hàng và thay đổi kho hàng.
Nếu bạn đang kiểm thử các API truyền dữ liệu, Apidog hỗ trợ kiểm thử và xác thực SSE.
Các khái niệm cơ bản về SSE
SSE là một phương pháp truyền dữ liệu một chiều dựa trên HTTP từ máy chủ đến máy khách.
Định dạng SSE
Content-Type: text/event-stream
Cache-Control: no-cache
data: {"message":"First chunk"}\n\n
data: {"message":"Second chunk"}\n\n
data: {"message":"Third chunk"}\n\n
Mỗi sự kiện:
- Bắt đầu bằng
data: - Chứa tải trọng (payload)
- Kết thúc bằng
\n\n(hai ký tự xuống dòng)
Các sự kiện được đặt tên
event: recommendation
data: {"petId":"019b4132","score":0.95}
event: recommendation
data: {"petId":"019b4127","score":0.89}
event: complete
data: {"total":2}
ID sự kiện
id: 1
data: {"message":"First"}
id: 2
data: {"message":"Second"}
Máy khách có thể tiếp tục từ ID cuối cùng nếu bị ngắt kết nối.
Triển khai máy chủ SSE
Ví dụ Node.js/Express
app.get('/v1/pets/recommendations/stream', async (req, res) => {
// Set SSE headers
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// Send recommendations as they're generated
const recommendations = await generateRecommendations(req.query.userId);
for (const rec of recommendations) {
res.write(`data: ${JSON.stringify(rec)}\n\n`);
await sleep(100); // Simulate streaming delay
}
// Send completion event
res.write(`event: complete\ndata: {"total":${recommendations.length}}\n\n`);
res.end();
});
Ví dụ Python/FastAPI
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import json
import asyncio
app = FastAPI()
@app.get("/v1/pets/recommendations/stream")
async def stream_recommendations(user_id: str):
async def generate():
recommendations = await get_recommendations(user_id)
for rec in recommendations:
yield f"data: {json.dumps(rec)}\n\n"
await asyncio.sleep(0.1)
yield f"event: complete\ndata: {json.dumps({'total': len(recommendations)})}\n\n"
return StreamingResponse(
generate(),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive"
}
)
Triển khai máy khách SSE
JavaScript/Trình duyệt
const eventSource = new EventSource(
'https://petstoreapi.com/v1/pets/recommendations/stream?userId=user-456'
);
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
displayRecommendation(data);
};
eventSource.addEventListener('complete', (event) => {
const data = JSON.parse(event.data);
console.log(`Received ${data.total} recommendations`);
eventSource.close();
});
eventSource.onerror = (error) => {
console.error('SSE error:', error);
eventSource.close();
};
Hook React
import { useEffect, useState } from 'react';
function useSSE(url) {
const [data, setData] = useState([]);
const [complete, setComplete] = useState(false);
useEffect(() => {
const eventSource = new EventSource(url);
eventSource.onmessage = (event) => {
const item = JSON.parse(event.data);
setData(prev => [...prev, item]);
};
eventSource.addEventListener('complete', () => {
setComplete(true);
eventSource.close();
});
return () => eventSource.close();
}, [url]);
return { data, complete };
}
// Cách sử dụng
function Recommendations({ userId }) {
const { data, complete } = useSSE(
`https://petstoreapi.com/v1/pets/recommendations/stream?userId=${userId}`
);
return (
<div>
{data.map(rec => (
<PetCard key={rec.petId} pet={rec} />
))}
{!complete && <Spinner />}
</div>
);
}
Cách Modern PetstoreAPI sử dụng SSE
Đề xuất thú cưng bằng AI
Truyền luồng các đề xuất do AI tạo ra:
GET /v1/pets/recommendations/stream?userId=user-456
Accept: text/event-stream
event: recommendation
data: {"petId":"019b4132","name":"Fluffy","score":0.95,"reason":"Matches your preference for cats"}
event: recommendation
data: {"petId":"019b4127","name":"Buddy","score":0.89,"reason":"Similar to pets you liked"}
event: complete
data: {"total":2,"processingTime":850}
Cập nhật trạng thái đơn hàng
Truyền luồng các bước xử lý đơn hàng:
GET /v1/orders/019b4132/status/stream
Accept: text/event-stream
data: {"status":"payment_processing","timestamp":"2026-03-13T10:30:00Z"}
data: {"status":"payment_confirmed","timestamp":"2026-03-13T10:30:02Z"}
data: {"status":"preparing_shipment","timestamp":"2026-03-13T10:30:05Z"}
event: complete
data: {"status":"shipped","trackingNumber":"1Z999AA10123456784"}
Thay đổi kho hàng
Truyền luồng cập nhật kho hàng theo thời gian thực:
GET /v1/inventory/stream
Accept: text/event-stream
event: stock-change
data: {"petId":"019b4132","oldStock":5,"newStock":4}
event: price-change
data: {"petId":"019b4127","oldPrice":299.99,"newPrice":279.99}
Xem tài liệu SSE của Modern PetstoreAPI.
Kiểm thử SSE với Apidog
Apidog hỗ trợ kiểm thử SSE:
- Tạo yêu cầu SSE
- Đặt
Accept: text/event-stream - Kết nối và xem các sự kiện theo thời gian thực
- Xác thực định dạng sự kiện
- Kiểm thử kết nối lại
Các phương pháp hay nhất
1. Đặt tiêu đề (headers) phù hợp
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
res.setHeader('X-Accel-Buffering', 'no'); // Tắt bộ đệm của nginx
2. Gửi tín hiệu giữ kết nối (Heartbeats)
Giữ kết nối hoạt động:
const heartbeat = setInterval(() => {
res.write(': heartbeat\n\n');
}, 15000);
res.on('close', () => clearInterval(heartbeat));
3. Xử lý lỗi một cách khéo léo
eventSource.onerror = (error) => {
if (eventSource.readyState === EventSource.CLOSED) {
// Kết nối đã đóng, sẽ tự động kết nối lại
} else {
// Lỗi khác
console.error('SSE error:', error);
}
};
4. Sử dụng ID sự kiện để tiếp tục
let lastEventId = 0;
app.get('/stream', (req, res) => {
const startId = parseInt(req.headers['last-event-id'] || '0');
for (let i = startId + 1; i <= 100; i++) {
res.write(`id: ${i}\ndata: {"message":"Event ${i}"}\n\n`);
}
});
5. Đóng kết nối
// Máy khách
eventSource.addEventListener('complete', () => {
eventSource.close();
});
// Máy chủ
res.on('close', () => {
// Dọn dẹp tài nguyên
});
Kết luận
SSE hoàn hảo để truyền luồng phản hồi API. Nó đơn giản hơn WebSocket cho giao tiếp một chiều, hoạt động qua HTTP và tự động xử lý việc kết nối lại.
Modern PetstoreAPI sử dụng SSE để truyền dữ liệu AI, cập nhật đơn hàng và các luồng trực tiếp. Kiểm thử các điểm cuối SSE bằng Apidog.
Câu hỏi thường gặp
SSE có thể hoạt động qua tường lửa của công ty không?
Có, SSE sử dụng HTTP/HTTPS tiêu chuẩn, vì vậy nó hoạt động qua hầu hết các tường lửa và máy chủ proxy.
Kết nối SSE có thể duy trì mở trong bao lâu?
Vô thời hạn, nhưng hãy sử dụng tín hiệu giữ kết nối (heartbeats) cứ sau 15-30 giây để duy trì kết nối thông qua các máy chủ proxy.
Tôi có thể gửi dữ liệu nhị phân qua SSE không?
Không, SSE chỉ xử lý văn bản. Hãy mã hóa Base64 dữ liệu nhị phân hoặc sử dụng WebSocket thay thế.
SSE có hỗ trợ giao tiếp hai chiều không?
Không, SSE chỉ truyền từ máy chủ đến máy khách. Máy khách sử dụng các yêu cầu HTTP thông thường để giao tiếp từ máy khách đến máy chủ.
Một trình duyệt có thể có bao nhiêu kết nối SSE?
Các trình duyệt giới hạn số lượng kết nối SSE trên mỗi miền (thường là 6). Sử dụng multiplexing hoặc WebSocket cho nhiều kết nối.
