在 Java 中,使用 Server-Sent Events (SSE) 来实现流式响应,允许服务器实时地将数据推送到客户端,适用于如实时通知、数据流、实时更新等场景。SSE 是基于 HTTP 协议的,只需要一个单向连接,服务器向客户端推送事件。
SSE (Server-Sent Events) 概述
- 单向通信:SSE 是客户端通过 HTTP 请求向服务器发起连接,而服务器通过该连接向客户端推送事件数据。
- 自动重连:如果连接断开,浏览器会自动尝试重新连接。
- 数据格式:SSE 使用基于文本的格式(例如 JSON 或普通文本)来发送事件数据。
在 Java 中实现 SSE 流式响应
使用 Servlet 或 Spring Boot 都可以实现 SSE 流式响应。下面分别介绍两种方法。
1. 使用 Servlet 实现 SSE 流式响应
1.1 创建一个 Servlet 来实现 SSE
以下是一个基于 Servlet 的示例,展示如何向客户端推送事件数据。
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.concurrent.*;
public class SSEServlet extends HttpServlet {
// 使用 ExecutorService 来模拟异步任务
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置响应类型为 SSE
response.setContentType("text/event-stream");
response.setCharacterEncoding("UTF-8");
// 设置流式响应
PrintWriter writer = response.getWriter();
// 模拟向客户端发送 SSE 数据
executorService.submit(() -> {
try {
int i = 0;
while (true) {
// 模拟事件数据
writer.write("data: Event number " + i + "\n\n");
writer.flush();
// 每隔 2 秒发送一次事件
Thread.sleep(2000);
i++;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
@Override
public void destroy() {
super.destroy();
executorService.shutdown();
}
}
1.2 配置 web.xml
确保在 web.xml
中正确配置该 Servlet。
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<servlet>
<servlet-name>SSEServlet</servlet-name>
<servlet-class>SSEServlet</servlet-class>
</servlet>
<servlet-mapping>
<url-pattern>/events</url-pattern>
</servlet-mapping>
</web-app>
1.3 客户端实现
客户端可以使用 JavaScript 接收 SSE 事件:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SSE Example</title>
</head>
<body>
<h1>Server-Sent Events Example</h1>
<div id="events"></div>
<script type="text/javascript">
// 建立与服务器的 SSE 连接
var eventSource = new EventSource("/events");
// 监听来自服务器的消息
eventSource.onmessage = function(event) {
var newElement = document.createElement("div");
newElement.textContent = "Received: " + event.data;
document.getElementById("events").appendChild(newElement);
};
// 监听连接打开事件
eventSource.onopen = function(event) {
console.log("Connection to server opened.");
};
// 监听错误事件
eventSource.onerror = function(event) {
console.error("Error occurred:", event);
};
</script>
</body>
</html>
在上述代码中,客户端通过 EventSource
来连接 /events
路径,服务器则会向客户端推送事件。
2. 使用 Spring Boot 实现 SSE 流式响应
在 Spring Boot 中,使用 @GetMapping
来实现流式响应也非常简单。Spring 提供了非常方便的支持来处理 SSE。
2.1 创建一个 Spring Boot Controller 来实现 SSE
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.http.ResponseEntity;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
@Controller
@RequestMapping("/events")
public class SSEController {
@GetMapping
public ResponseEntity<SseEmitter> stream() {
// 创建一个 SseEmitter 对象
SseEmitter emitter = new SseEmitter();
// 模拟向客户端发送 SSE 数据
new Thread(() -> {
try {
int i = 0;
while (true) {
emitter.send(SseEmitter.event().name("message").data("Event number " + i));
i++;
Thread.sleep(2000);
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}).start();
return ResponseEntity.ok()
.contentType(MediaType.TEXT_EVENT_STREAM)
.body(emitter);
}
}
2.2 Spring Boot 应用主类
确保你有一个 Spring Boot 应用的主类来启动应用。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SseApplication {
public static void main(String[] args) {
SpringApplication.run(SseApplication.class, args);
}
}
2.3 客户端实现
与 Servlet 示例中的客户端实现一样,Spring Boot 也可以通过 JavaScript 使用 EventSource
来接收事件。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SSE Example</title>
</head>
<body>
<h1>Server-Sent Events Example</h1>
<div id="events"></div>
<script type="text/javascript">
// 建立与服务器的 SSE 连接
var eventSource = new EventSource("/events");
// 监听来自服务器的消息
eventSource.onmessage = function(event) {
var newElement = document.createElement("div");
newElement.textContent = "Received: " + event.data;
document.getElementById("events").appendChild(newElement);
};
// 监听连接打开事件
eventSource.onopen = function(event) {
console.log("Connection to server opened.");
};
// 监听错误事件
eventSource.onerror = function(event) {
console.error("Error occurred:", event);
};
</script>
</body>
</html>
总结:
- Servlet 实现 SSE:通过设置
Content-Type
为text/event-stream
,并使用PrintWriter
流式发送数据。 - Spring Boot 实现 SSE:使用
SseEmitter
来异步地向客户端发送事件,并使用MediaType.TEXT_EVENT_STREAM
设置响应类型。 - 客户端接收 SSE:使用
EventSource
对象连接到 SSE 端点,并处理接收到的事件。
SSE 是一个非常适合实时数据推送的技术,广泛应用于实时通知、股票行情、游戏消息等场景。希望这个示例能帮助你快速上手 SSE,如果有任何问题,随时告诉我!
发表回复