一.常见定时推送实现方式
1.客户端轮询:ajax定时拉取
2.服务端主动推送:WebSocket
全双工的,本质上是一个额外的tcp连接,建立和关闭时握手使用http协议,其他数据传输不使用http协议,更加复杂一些,适用于需要进行复杂双向数据通讯的场景。
3.服务端主动推送:SSE (Server Send Event):
html5新标准,用来从服务端实时推送数据到浏览器端,
直接建立在当前http连接上,本质上是保持一个http长连接,轻量协议
简单的服务器数据推送的场景,使用服务器推送事件
二.SSE介绍
2.1 sse扫盲
sse:sever send event;直译为服务器发送事件,顾名思义,也就是服务端向客户端推送信息的一种技术。通俗解释起来就是一种基于HTTP的,以流的形式由服务端持续向客户端发送数据的技术
2.2 sse的流程
常规流程:我们常见的 http 交互方式是客户端发起请求,服务端响应,然后一次请求完毕;
sse模式下:在 sse 的场景下,客户端发起请求,连接一直保持,服务端有数据就可以返回数据给客户端,这个返回可以是多次间隔的方式。
2.3 sse与websocket的区别
SSE 最大的特点,可以简单规划为两个
- 长连接
- 服务端可以向客户端推送信息
sse 是单通道,只能服务端向客户端发消息;而 webscoket 是双通道。和websocket相比,只能单工通信,建立连接后,只能由服务端发往客户端,且占用一个连接,如需客户端向服务端通信,需额外打开一个连接
2.4 应用场景
从 sse 的特点出发,我们可以大致的判断出它的应用场景,需要轮询获取服务端最新数据的 case 下,多半是可以用它的,比如显示当前网站在线的实时人数,法币汇率显示当前实时汇率,电商大促的实时成交额等等...
参考文章:http://www.manongjc.com/detail/8-ufnkqsjgmxnyhdw.html
https://www.cnblogs.com/yihuihui/p/12622729.html
https://www.jianshu.com/p/ed94c8005f2c
https://blog.csdn.net/tenyears940326/article/details/109616786
三. 应用案例
3.1 工程结构
3.2 pom文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
3.3 新建一个html页面
为了直接能够访问,在resource目录下新建一个static目录,用于存放静态页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript">
//需要判断浏览器支不支持,可以去w3c进行查看
var source = new EventSource('/get_data');
source.onmessage = function (event) {
console.info(event.data);
document.getElementById('result').innerText = event.data
};
</script>
</head>
<body>
<div id="result"></div>
</body>
</html>
3.4 后端服务
注意事项:
//1.produces = "text/event-stream;charset=UTF-8"一定要带上
//2.!!!注意,EventSource返回的参数必须以data:开头,"\n\n"结尾,不然onmessage方法无法执行。
package com.ljf.spring.boot.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.text.DecimalFormat;
/**
* @ClassName: PushDataController
* @Description: TODO
* @Author: liujianfu
* @Date: 2021/04/05 15:34:03
* @Version: V1.0
**/
@RestController
public class PushDataController {
//1.produces = "text/event-stream;charset=UTF-8"一定要带上
@RequestMapping(value = "/get_data", produces = "text/event-stream;charset=UTF-8")
public String getData(){
try {
Thread.sleep(1000);
}catch (Exception e) {
e.printStackTrace();
}
double moeny=Math.random()*10;
System.out.println(String.format("%.2f",moeny));
DecimalFormat df = new DecimalFormat(".00");
String canshu=df.format(moeny);
//2.!!!注意,EventSource返回的参数必须以data:开头,"\n\n"结尾,不然onmessage方法无法执行。
return "data:白菜价格行情:" + canshu+"元"+ "\n\n";
}
}
3.5 访问
需要把response的类型 改为 text/event-stream,才是sse的类型
输入地址:直接访问页面:http://localhost:8080/index.html ;而直接访问请求地址:http://localhost:8080/get_data 则只显示一次。主要是没有建立长链接。