Server Send Event(基于Http协议的单向消息通信)
2024-01-02 12:42:49
简介
Server-Sent Events(SSE)是一种简单的技术,允许服务器向客户端推送实时更新。在Spring Boot项目中,我们可以使用SseEmitter类来实现SSE功能。本文将详细介绍如何在Spring Boot项目中使用SSE,并给出一个使用示例。
核心原理
首先,客户端通过浏览器向服务器发送一个SSE请求,当服务器收到这个SSE请求后,会建立一个持久的HTTP连接,并将响应的Content-Type设置为text/event-stream。然后,服务器就可以通过这条已经建立的连接向客户端持续发送数据了。每个数据都由一个或多个字段组成,如event、data、id等。需要注意的是,SSE是单向通信协议,只能从服务器端向客户端发送数据。
引入项目依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
创建SSE的使用工具类
public class SseEmitterUtil {
private final static Map<String, SseEmitter> sseEmitterMap = new ConcurrentHashMap<>();
private final static AtomicInteger count = new AtomicInteger();
/**
* 创建SSE连接
* Connect sse emitter.
*
* @param clientId the client id
* @return the sse emitter
*/
public static SseEmitter connect(String clientId) {
try {
// 设置超时时间,0表示不过期。默认30秒
SseEmitter sseEmitter = new SseEmitter(0L);
// 注册回调
sseEmitter.onCompletion(() -> {
log.info("客户端正常关闭,clientId = {}", clientId);
removeClientId(clientId);
});
sseEmitter.onError(throwable -> {
log.error("连接出现异常,准备关闭,clientId = {}", clientId);
removeClientId(clientId);
});
sseEmitter.onTimeout(() -> {
log.info("连接已超时,准备关闭,clientId = {}", clientId);
removeClientId(clientId);
});
sseEmitterMap.put(clientId, sseEmitter);
count.getAndIncrement();
log.info("连接成功,客户端连接总数:{}", count.get());
return sseEmitter;
} catch (Exception e) {
log.info("创建新的sse连接异常,clientId:{}", clientId);
log.error(e.toString());
}
return null;
}
/**
* 给指定用户发送消息
* Send message.
*
* @param clientId the client id 客户端id
* @param message the message
*/
public static void sendMessage(String clientId, String message) {
if (sseEmitterMap.containsKey(clientId)) {
try {
sseEmitterMap.get(clientId).send(message);
} catch (IOException e) {
log.error("clientId:[{}],推送数据:{},推送异常:{}", clientId, message, e.getMessage());
removeClientId(clientId);
}
}
}
/**
* 断开连接
* Disconnect.
*
* @param clientId the client id
*/
public static void disconnect(String clientId) {
if (sseEmitterMap.containsKey(clientId)) {
log.info("连接断开,clientId:{}", clientId);
sseEmitterMap.get(clientId).complete();
} else {
log.error("不存在clientId为{}的客户端连接", clientId);
}
}
/**
* 移除连接客户端
* Remove client id.
*
* @param id the id 客户端连接ID
*/
private static void removeClientId(String id) {
sseEmitterMap.remove(id);
count.getAndDecrement();
log.info("剩余客户端连接数:{}", count.get());
}
}
工具类的使用
@RequestMapping("/sse")
public class SseController {
/**
* 连接SSE
* Push sse emitter.
*
* @param clientId the client id
* @return the sse emitter
*/
@GetMapping("/connect")
public SseEmitter push(String clientId) {
return SseEmitterUtil.connect(clientId);
}
/**
* 消息推送
* Push.
*
* @param clientId the client id
* @param content the content
*/
@GetMapping("/push")
public void push(String clientId, String content) {
SseEmitterUtil.sendMessage(clientId, content);
}
/**
* 断开连接
* Disconnect.
*
* @param clientId the client id
*/
@GetMapping("/disconnect")
public void disconnect(String clientId) {
SseEmitterUtil.disconnect(clientId);
}
}
PS:在使用过程是前后端约定好不同的场景使用特定的clientId进行消息传输,使用完毕调用关闭连接的接口即可
使用及优势分析
- 只需要服务端向客户端单向通信的场景下可以使用
- SSE 实现简单开发成本低,无需引入其他组件;WebSocket传输数据需做二次解析,开发门槛高一些
- SSE 默认支持断线重连;WebSocket则需要自己实现
- SSE 只能传送文本消息,二进制数据需要经过编码后传送;WebSocket默认支持传送二进制数据
文章来源:https://blog.csdn.net/Csdn_xiansheng/article/details/135337408
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!