SSE即 server send event 服务器发送事件,在在早期可能会使用ajax向服务器轮询的方式,使浏览器第一时间接受到服务器的消息,但这种频率不好控制,消耗也比较大。
但是对于SSE来说,当客户端向服务端发送请求,服务端会抓住这个请求不放,等到有数据时才返回给客户端,但客户端手动消息后,再向服务器发送请求,周而复始。这种方式好处是减少了服务器的请求数量,也大大减少了服务器的压力。
以下是第一种方式的代码的演示,浏览器不断向服务器请求,服务器用线程睡眠5s再返回结果。
1、SseController 控制器
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Random;
/**
* @description: 服务器端推送控制器
*
* @author: Shenshuaihu
* @version: 1.0
* @data: 2019-06-25 23:29
*/
@Controller
public class SseController {
/**
* 输出类型 text/event-stream 是对服务器端SSE的支持
* 此处每5s向浏览器推送随机消息
* @return
*/
@RequestMapping(value = "/push", produces = "text/event-stream")
public @ResponseBody String push() {
Random random = new Random();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "data:Testing 1,2,3: " + random.nextInt() + "\n\n";
}
}
2、显示结果的页面 sse.jsp
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<title>SSE-Code</title>
<link rel="stylesheet" type="text/css" value="">
</head>
<body>
<h2> sse.jsp </h2>
服务器推送 可以用于消息订阅
<br/>
解决长短轮询不是解决问题
<br/>
server send event 当客户端方服务器发送请求时 服务器抓住不放 等有数据时 再回复给客户端,客户端收到消息时发给送给服务器,如此循环
<br/>
参考内容:
https://www.jianshu.com/p/bc5a9b4a1cd1
<div id="msgFromPush"></div>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script>
console.log("!!Window EventSource: " + !!Window.EventSource)
if (!!window.EventSource) {
var source = new EventSource('push');
s = '';
source.addEventListener('message', function (evt) {
s += evt.data + "<br/>";
$("#msgFromPush").html(s);
});
source.addEventListener('open', function (evt) {
console.log("连接打开.")
})
// 添加SSE客户端监听,获取服务端推送的消息
source.addEventListener('error', function (evt) {
if (evt.readyState == EventSource.CLOSED) {
console.log("连接关闭.")
} else {
console.log(evt.readyState)
}
}, false);
} else {
console.log("你的浏览器不支持SSE.")
}
/*if(window.EventSource){
var eventSource = new EventSource("http://localhost:8080/push");
//只要和服务器连接,就会触发open事件
eventSource.addEventListener("open",function(){
console.log("和服务器建立连接");
});
//处理服务器响应报文中的load事件
eventSource.addEventListener("load",function(e){
console.log("服务器发送给客户端的数据为:" + e.data);
});
//如果服务器响应报文中没有指明事件,默认触发message事件
eventSource.addEventListener("message",function(e){
console.log("服务器发送给客户端的数据为:" + e.data);
});
//发生错误,则会触发error事件
eventSource.addEventListener("error",function(e){
console.log("服务器发送给客户端的数据为:" + e.data);
});
}
else{
console.log("服务器不支持EvenSource对象");
}*/
</script>
</body>
</html>
显示的结果
SSE-.png二、使用Servlet 3.0 + 异步方法处理,第二种方式演示,浏览器循环请求服务端,服务端用定时任务,每5S设置一下数据,返回给浏览器
1、开启异步方法的支持 WebInitializer.java
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic;
/**
* @description: Web配置 代替web.xml
* @author: Shenshuaihu
* @version: 1.0
* @data: 2019-06-13 23:22
*/
public class WebInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
context.register(MyMvcConfig.class);
// 新建的webApplicationContext ,注册配置类,并将其和当前servletContext关联。
context.setServletContext(servletContext);
// 注册SpringMVC 的 DispatcherServlet
Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(context));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
// 开启对异步的支持
servlet.setAsyncSupported(true);
}
}
2、AsyncController.java 控制层,只用掉service
import com.ch4.service.PushService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.async.DeferredResult;
/**
* @description:
*
* @author: Shenshuaihu
* @version: 1.0
* @data: 2019-06-27 08:32
*/
@Controller
public class AsyncController {
@Autowired
private PushService pushService;
@RequestMapping("/defer")
@ResponseBody
public DeferredResult<String> deferredCall() {
return pushService.getAsyncUpdate();
}
}
3、PushService.java
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.async.DeferredResult;
/**
* @description: SSE 定时任务
* 在PushService里面产生 DeferredResult 给控制器使用,
* 通过 @Scheduled 定时更新DeferredResult
*
* @author: Shenshuaihu
* @version: 1.0
* @data: 2019-06-27 08:32
*/
@Service
public class PushService {
private DeferredResult<String> deferredResult;
public DeferredResult<String> getAsyncUpdate() {
deferredResult = new DeferredResult<String>();
return deferredResult;
}
@Scheduled(fixedDelay = 5000)
public void refresh() {
if (deferredResult != null) {
deferredResult.setResult(new Long(System.currentTimeMillis()).toString());
}
}
}
3、数据页面async.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
<title>async support-Code</title>
<link rel="stylesheet" type="text/css" value="">
</head>
<body>
<h2> defer.jsp </h2>
<div id="defer"></div>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script>
deferred();
function deferred() {
$.get('defer', function (data) {
console.log(data);
s = '';
s += data + "<br/>";
$('#defer').html(s)
// 完成后在向服务器请求
deferred();
}
);
}
</script>
</body>
</html>
4、需要在核销配置类用开启任务
@Configuration
@EnableWebMvc
@ComponentScan("com.ch4")
@EnableScheduling
public class MyMvcConfig extends WebMvcConfigurerAdapter {}
SSE-aync.png
总结:
SSE用于订阅消息,是需要浏览器不断的请求,与websocket有相似之处
2019/06/30晚于成都
网友评论