【AI落地工程技术】-SSE协议

发布于:2024-07-02 ⋅ 阅读:(15) ⋅ 点赞:(0)

目录

SSE协议内容

SSE事件格式

使用示例

示例一

客户端(JavaScript)

服务器端(Node.js 示例)

示例二

前端(JavaScript)

后端(Java,使用Spring Boot和Servlet API)


SSE(Server-Sent Events)协议是一种用于服务器主动向客户端推送数据的技术,也称为“事件流”(Event Stream)。它基于HTTP协议,利用长连接特性,在客户端与服务器之间建立一条持久化连接,并通过这条连接实现服务器向客户端的实时数据推送。ChatGPT即采用的是SSE协议。

SSE协议内容

  1. 协议头
    • SSE协议返回的是事件流,需要指定内容类型。因此,在HTTP header里需要加上以下信息:
      • Content-Type: text/event-stream
      • Connection: keep-alive
      • Cache-Control: no-cache
  2. 协议内容
    • 协议内容放在HTTP返回的body里,每次返回一个Event信息。
    • 每个Event里可以包含以下5个属性(但并非所有属性都是必需的):
      • id:用于表示Event的序号,客户端通过序号实现断线重连功能。
      • event:表示自定义事件类型,客户端通过该字段区分不同消息。
      • data:表示返回的业务数据,如果数据很长可以分成多行返回。
      • retry:(可选)注释消息,表示重新连接的时间间隔(单位:毫秒)。
    • 每个属性值占用一行,每行的内容都是由属性名称+属性值组成+换行符,之间用冒号隔开(:)。
    • 两个消息之间用额外的换行符(\n\n)区分。

SSE事件格式

每个SSE事件都以文本形式发送,遵循以下格式:

  • 单行注释:以: 开头(例如 : 这是一条注释\n
  • 字段:由字段名、冒号、字段值以及一个换行符组成(例如 data: This is a message\n
  • 事件:通过event字段指定(例如 event: myevent\n
  • ID:通过id字段指定,用于在重新连接时帮助客户端确定错过的消息(例如 id: 123\n
  • 重试:通过retry字段指定在重新连接之前的延迟时间(单位:毫秒)(例如 retry: 2000\n
  • 消息结束:两个换行符(\n\n)表示一个事件的结束和下一个事件的开始

使用示例

示例一

客户端(JavaScript)

// 创建EventSource对象  
const eventSource = new EventSource('http://example.com/sse');  
  
// 监听服务器发送的事件  
eventSource.onmessage = function(event) {  
    console.log('收到消息:', event.data);  
};  
  
// 监听服务器关闭连接的事件  
eventSource.onclose = function() {  
    console.log('连接已关闭');  
};  
  
// 监听服务器错误事件  
eventSource.onerror = function() {  
    console.log('连接出错');  
};


服务器端(Node.js 示例)

const http = require('http');  
  
const server = http.createServer((req, res) => {  
    if (req.url === '/sse' && req.method === 'GET') {  
        // 设置响应头  
        res.writeHead(200, {  
            'Content-Type': 'text/event-stream',  
            'Connection': 'keep-alive',  
            'Cache-Control': 'no-cache'  
        });  
  
        // 模拟发送数据  
        let count = 0;  
        const interval = setInterval(() => {  
            const eventData = `data: 这是第 ${count++} 条消息\n\n`;  
            res.write(eventData);  
  
            // 假设在发送10条消息后关闭连接  
            if (count > 10) {  
                clearInterval(interval);  
                res.end();  
            }  
        }, 1000); // 每秒发送一次  
    }  
});  
  
server.listen(8080, () => {  
    console.log('SSE 服务器已启动,监听 8080 端口');  
});
在上面的示例中,客户端使用EventSource对象创建一个与服务器端的SSE连接,并监听不同的事件。服务器端则创建一个HTTP服务器,并在接收到特定的GET请求时,通过长连接向客户端发送数据。这个示例模拟了一个简单的SSE服务器,每秒钟向客户端发送一条消息,并在发送了10条消息后关闭连接。

示例二

前端(JavaScript)

// 创建EventSource实例并连接到SSE服务  
var source = new EventSource('/server-sent-events-endpoint');  
  
// 监听message事件(默认事件类型)  
source.onmessage = function(event) {  
    console.log('Received data:', event.data);  
};  
  
// 监听自定义事件类型  
source.onmyevent = function(event) {  
    console.log('Received myevent:', event.data);  
};  
  
// 监听连接打开事件  
source.onopen = function(event) {  
    console.log('Connection to server opened.');  
};  
  
// 监听连接错误事件  
source.onerror = function(event) {  
    if (source.readyState === EventSource.CLOSED) {  
        // 连接已关闭,可以尝试重新连接  
        console.log('Connection to server was closed.');  
    }  
};

后端(Java,使用Spring Boot和Servlet API)

在Spring Boot应用中,你可以使用@RestController@GetMapping来创建一个SSE端点,但更常见的是使用ServletResponse直接发送SSE消息。

import javax.servlet.ServletException;  
import javax.servlet.annotation.WebServlet;  
import javax.servlet.http.HttpServlet;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
import java.io.IOException;  
import java.io.PrintWriter;  
  
@WebServlet("/server-sent-events-endpoint")  
public class SseServlet extends HttpServlet {  
  
    @Override  
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {  
        resp.setContentType("text/event-stream");  
        resp.setCharacterEncoding("UTF-8");  
  
        PrintWriter out = resp.getWriter();  
  
        int count = 0;  
        while (true) { // 模拟持续发送事件  
            out.println("data: Event " + (count++) + " at " + System.currentTimeMillis());  
            out.println("event: server-time"); // 可选的事件类型  
            out.println("retry: 1000\n"); // 推荐客户端在断开连接后等待1秒再重连  
  
            // 刷新输出流  
            out.flush();  
  
            // 暂停一段时间再发送下一条消息  
            try {  
                Thread.sleep(1000);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
                break;  
            }  
        }  
  
        // 注意:在实际应用中,你可能需要一种机制来优雅地关闭连接,而不是使用无限循环  
    }  
}

在这个示例中,我们创建了一个Servlet来模拟SSE服务。Servlet响应设置了正确的内容类型和字符编码,并使用PrintWriter发送SSE事件。在实际应用中,你可能会使用一个更复杂的逻辑来控制何时发送消息以及如何优雅