服务端通过SSE向前端推送消息
2018-07-22 本文已影响0人
Stalary
SSE(Server-Sent Events)相比Websocket来说更加轻量,在代码书写时也更加的方便,所以今天来讲一下SSE的简单使用。
当时接到一个需求是上传word进行解析题目存储,并且前端要显示解析过程,当时首先想到的就是Websocket,后来仔细一想,有没有更轻量级的方法呢,果然还是有的
下面来讲一下具体的使用方法
-
我们使用Spring项目来进行讲解,所以需要引入的Spring依赖就不再说明了,然后我们可以惊奇的发现引入完Spring的一些依赖,我们竟然就可以使用SSE了。果然够轻量
-
通过以下简单的代码我们就完成了SSE消息的发送,一个方法通过sse监控解析进度,一个方法进行文件解析
/**
* sse
*/
@GetMapping("/sse")
public SseEmitter getSSE(
HttpServletRequest request) {
SseEmitter emitter;
String userId = UserUtils.getUserId(request);
if (userMap.containsKey(userId)) {
emitter = userMap.get(userId);
} else {
emitter = new SseEmitter();
userMap.put(userId, emitter);
emitter.onTimeout(() -> userMap.remove(userId));
}
return emitter;
}
@PostMapping("/submit")
public ResponseMessage submit(
HttpServletRequest request,
@RequestParam MultipartFile file,
@RequestParam String category,
@RequestParam String type,
@RequestParam(required = false, defaultValue = "") String year) {
String editor = UserUtils.getUserId(request);
return ResponseMessage.successMessage(uploadService.upload(file, category, type, year, editor));
}
返回数据
data:"文件开始解析"
event:upload_start
- 接下来说一下我们该如何进行解析操作
/**
* 存储解析进度key:userId,value:schedule
*/
private final Map<String, Integer> scheduleMap = Maps.newConcurrentMap();
/**
* 是否已经完成解析key:userId,value:是否已经完成
*/
private final Map<String, Boolean> isFinishedMap = Maps.newConcurrentMap();
/**
* 解析完成后返回的数据
*/
private final Map<String, Map<String, Object>> returnMap = Maps.newConcurrentMap();
@Scheduled(fixedRate = 500)
public void uploadSchedule() {
for (Map.Entry<String, SseEmitter> entry : UploadCtrl.userMap.entrySet()) {
try {
String userId = entry.getKey();
Boolean isFinished = isFinishedMap.get(userId);
if (isFinished == null) {
return;
}
if (!isFinished) {
SseEmitter.SseEventBuilder builder = SseEmitter
.event()
.data(scheduleMap.get(userId))
.name("upload_schedule");
entry.getValue().send(builder);
log.info("sse send" + userId + " schedule: " + scheduleMap.get(userId));
} else {
// 代表解析完毕
SseEmitter.SseEventBuilder builder = SseEmitter
.event()
.data(returnMap.get(userId))
.name("upload_end");
entry.getValue().send(builder);
log.info("sse send" + userId + " end: " + gson.toJson(returnMap.get(userId)));
// 解析完毕后从Map中移除
UploadCtrl.userMap.remove(userId);
isFinishedMap.remove(userId);
scheduleMap.remove(userId);
returnMap.remove(userId);
// 关闭emitter
entry.getValue().complete();
}
} catch (Exception e) {
log.warn("upload Error of " + entry.getKey(), e);
UploadCtrl.userMap.remove(entry.getKey());
}
}
}
可以看出我通过是否完成map,解析结果map,解析进度map来进行解析进度的存储,然后通过定时任务,每0.5s向前端发送一次进度。
我们可以发现,使用SSE推送消息是十分的方便快捷的,并且自身封装有一些失败重试,自动重连的机制,相比websocket来说,使用更加方便。
今天前端同事对接接口,才发现sse是没法使用post方法的,这一点大家要注意,所以对博客进行了更改
每当我们想要松懈时,就想想之前的汗水,那是我们今天所拥有的一切的源泉