【AI落地工程技术】-SSE协议
目录
SSE协议内容
SSE事件格式
使用示例
示例一
客户端(JavaScript)
服务器端(Node.js 示例)
示例二
前端(JavaScript)
后端(Java,使用Spring Boot和Servlet API)
SSE(Server-Sent Events)协议是一种用于服务器主动向客户端推送数据的技术,也称为“事件流”(Event Stream)。它基于HTTP协议,利用长连接特性,在客户端与服务器之间建立一条持久化连接,并通过这条连接实现服务器向客户端的实时数据推送。ChatGPT即采用的是SSE协议。
SSE协议内容
- 协议头:
- SSE协议返回的是事件流,需要指定内容类型。因此,在HTTP header里需要加上以下信息:
Content-Type: text/event-stream
Connection: keep-alive
Cache-Control: no-cache
- SSE协议返回的是事件流,需要指定内容类型。因此,在HTTP header里需要加上以下信息:
- 协议内容:
- 协议内容放在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事件。在实际应用中,你可能会使用一个更复杂的逻辑来控制何时发送消息以及如何优雅