最近工作中这样一个需求,在商城首页需要实时展示每个品类商品的成交量,大致效果(滚动效果)如下所示:
相信很多小伙伴一收到这个需求,立马就会想到客户端需要实时获取服务端消息,本文就主要介绍一种简单实现方案
Server-Sent Events
Server-Sent Events(SSE) , 是一种服务器端到客户端(浏览器)的单项消息推送 SSE 它是基于HTTP
协议的,我们知道一般意义上的HTTP协议是客户端发起请求,服务端响应。但SSE是个例外,允许服务器在任何时候将数据推送到连接的客户端,而不需要客户端发起请求。
SpringBoot实现SSE服务端示例
在我们平常使用的 SpringBoot 中,里面有个 SseEmitter 类已经封装好了相关操作,我们通过该类就可以实现SSE功能。
java
@Slf4j
@Service
public class ServerSentEventsClient {
private final static Map<String, SseEmitter> sseEmitterMap = new ConcurrentHashMap<>();
public SseEmitter connect(String userId) {
// 设置超时时间,0表示不过期。默认30秒
SseEmitter sseEmitter = new SseEmitter(0L);
try {
sseEmitter.send("/// 服务端链接成功");
} catch (IOException e) {
log.error("用户[{}]推送链接信息异常:{}", userId, e.getMessage());
sseEmitterMap.remove(userId);
}
// 注册回调
sseEmitter.onCompletion(() -> sseEmitterMap.remove(userId));
sseEmitter.onError(throwable -> {
log.info("连接异常:{}", userId);
sseEmitterMap.remove(userId);
});
sseEmitter.onTimeout(() -> sseEmitterMap.remove(userId));
sseEmitterMap.put(userId, sseEmitter);
return sseEmitter;
}
public void sendMessage(String userId) {
if (sseEmitterMap.containsKey(userId)) {
try {
sseEmitterMap.get(userId).send("Hello SSE");
} catch (IOException e) {
log.error("用户[{}]推送异常:{}", userId, e.getMessage());
sseEmitterMap.remove(userId);
}
}
}
}
核心方法就是这么简单,我们就可以完成服务端向客户端发送实时消息,接下来就是添加测试controller(省略。。。),进行测试看效果。
- 测试效果
SSE VS WebSocket
SSE | WebSocket | |
---|---|---|
通信 | 单向通信 | 双向通信 |
协议 | HTTP协议 | WebSocket |
数据格式 | 文本格式 | 文本格式/二进制数据 |
断线重连 | 支持 | 需要自己实现 |
总结
SSE 的本质其实就是一个HTTP的长连接 ,只不过它给客户端发送的不是一次性的数据包,而是一个stream流,格式为text/event-stream。所以客户端不会关闭连接,会一直等着服务器发过来的新的数据流。
最后,还是那句话,每一种方案都有自己的优缺点,而我们能做的就是在不同场景下,选用合适的方案,再通过一些机制来弥补方案的缺点。