使用SSE从后端往前端推送数据
最近在做项目的时候,有个需求是要实时验证设备的存在性,http不行啊,因为和设备之前通讯使用的是mqtt,只有收到订阅才你能返回给前端,如果说在springboot后台中进行睡眠等待或者线程池等待的话感觉也不现实,毕竟访问的人少可以,人一多就全部阻塞了,所以就想到21年用过的一个技术,SSE,这个东西呢,相比较websocket来说比较轻,怎么说呢,websocket是双向通讯,而这个是单向的,而且一般浏览器都支持,使用起来也比较简单,而且,springboot web已经集成了这个技术,我们不需要添加任何其他的依赖
1.后端代码编写
在后端这块呢,首先保证你是springboot项目,那项目启动肯定少不了web,那就加入web依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
然后就是编写你的controller
首先,这个controller和我们之前写的controller是一样的,之前怎么写,这个就怎么写,因为我写在demo里边了,所以就直接写在了启动类中。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* controller
*
* @author 王祁
*/
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
@RestController
@RequestMapping("/sse")
@CrossOrigin
public class App {
private Map<String, SseEmitter> map = new ConcurrentHashMap<>();
public static void main (String[] args) {
SpringApplication.run(App.class, args);
}
@GetMapping(value = "check/{id}")
public SseEmitter sub (@PathVariable String id) throws IOException {
if (null != map.get(id)) {
SseEmitter sseEmitter = new SseEmitter();
sseEmitter.send(SseEmitter.event().name("disconnected").data("断开连接"));
sseEmitter.complete();
map.remove(id);
return sseEmitter;
}
SseEmitter sseEmitter = new SseEmitter();
sseEmitter.send(SseEmitter.event().name("info").data("连接成功"));
map.put(id, sseEmitter);
sseEmitter.onError((e) -> {
System.out.println("错误" + e);
map.remove(id);
});
sseEmitter.onTimeout(() -> {
});
sseEmitter.onCompletion(() -> {
System.out.println("完成");
});
return sseEmitter;
}
@GetMapping("/push/{id}/{content}")
public void push (@PathVariable String id, @PathVariable String content) {
Map<String, Object> obj = new HashMap<>();
obj.put("code", 200);
obj.put("data", content);
obj.put("msg", "成功");
SseEmitter sseEmitter = map.get(id);
if (null != sseEmitter) {
try {
sseEmitter.send(SseEmitter.event().name("message").data(obj, MediaType.APPLICATION_JSON));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
就这么简单,后端已经完成了,我给你们分析分析哈
这个呢,是存储已经创建了的sse实例,方便我们调用接口的时候拿出来发送private Map<String, SseEmitter> map = new ConcurrentHashMap<>();
这个是创建一个实例,它有构造方法SseEmitter sseEmitter = new SseEmitter();
这个构造方法可以设置过期时间,默认的过期时间是你web容器的过期时间,一般是30秒(tomcat),具体设置容器的过期时间,请自行百度,这里设置这个过期时间是Long类型,单位是秒,如果是10秒我可以输入 10L
这个是发送消息,为了让前端区分连接成功,断开以及正常消息,我们可以设置发送消息的eventName
,就是 SseEmitter.event().name("info")
这个name,我这块的话info
是连接成功,disconnected
是连接失败,message
是正常消息。发送消息呢,消息体在data里边,可以设置消息体的类型,可以是json格式的,就使用MediaType
就行了sseEmitter.send(SseEmitter.event().name("info").data("连接成功"));
这块是设置当执行完成也就是调用了completion、出现错误、连接超时的时候的处理方法
sseEmitter.onError((e) -> {
System.out.println("错误" + e);
map.remove(id);
});
sseEmitter.onTimeout(() -> {
});
sseEmitter.onCompletion(() -> {
System.out.println("完成");
});
最后将实例return出去,前端需要接受SseEmitter
类型
2.前端代码编写
前端呢,其实也很简单
var url = 'http://localhost:8080/sse/check/12'
var es = new EventSource(url)
es.addEventListener("info", e => {
console.log("连接成功", e)
})
es.addEventListener('disconnected', e => {
console.log("断开连接",e.data)
es.close()
})
es.addEventListener("message", (e) => {
console.log("消息", e)
})
es.addEventListener("error", e => {
if (e.error) {
console.log("错误,关闭连接", e.error)
es.close()
}
})
es.addEventListener("open", e => {
console.log("打开连接")
})
就这就完了
- 本文标签: Spring Boot
- 本文链接: https://blog.wangqi2020.top/article/55
- 版权声明: 本文由王祁原创发布,转载请遵循《署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0)》许可协议授权