Cách phát trực tuyến API Response bằng Server-Sent Events (SSE)

Ashley Innocent

Ashley Innocent

13 tháng 3 2026

Cách phát trực tuyến API Response bằng Server-Sent Events (SSE)

Apidog cho doanh nghiệp

Triển khai tại chỗ

SSO & RBAC

Tuân thủ SOC 2

Khám phá Apidog Enterprise

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.

nút

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:

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:

  1. Tạo yêu cầu SSE
  2. Đặt Accept: text/event-stream
  3. Kết nối và xem các sự kiện theo thời gian thực
  4. Xác thực định dạng sự kiện
  5. 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.

Thực hành thiết kế API trong Apidog

Khám phá cách dễ dàng hơn để xây dựng và sử dụng API