一、介绍
所有的服务器推送的方案都是基于:
当客户端像服务端发送请求,服务端会抓住这个请求不放,等有数据更新的时候才会返回给客户端,当客户端接收到消息的时候,再向服务端发送请求,周而复始。
这样做的好处是:减少了服务器请求的次数,减少了服务器的压力。
二、需求
分别使用SSE和基于Servlet3.0+异步方法,每5秒向浏览器推送随机消息。其中SSE需要新式浏览器的支持,Servlet3.0+是跨浏览器的。
三、实现
1.SSE
(1)Controller实现
package com.eleven.controller;
import java.util.Random;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller // 该注解表示返回的是字符串,与页面直接渲染
public class SseController {
@RequestMapping(value = "/push", produces = "text/event-stream") // 输出媒体的类型为event-stream,表示这是服务端SSE的支持,
public @ResponseBody String push() {
Random r = new Random(); // 创建一个随机数的对象
try {
Thread.sleep(5000); // 默认单位是millis,表示等待5秒钟
} catch (Exception e) {
e.printStackTrace();
}
return "data:Testing 1,2,3" + r.nextInt() + "\n\n";
}
}
(2)sse.jsp页面
在src/main/resources/views新建sse.jsp文件
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>SSE Demo</title>
</head>
<body>
<div id="msgFromPush">
<script src="<c:url value="https://code.jquery.com/jquery-3.4.1.min.js"/>"></script>
<script type="text/javascript">
if (!!window.EventSource) { // EventSource对象只有新式浏览器才有,是SSE的客户端
alert();
var source = new EventSource('push');
s = '';
source.addEventListener('message', function(e) { // 添加SSE客户端监听,再此获得服务器推送的消息
s += e.data + "<br/>";
$("#msgFromPush").html(s);
});
source.addEventListener('open', function(e) {
console.log("连接打开.");
}, false);
source.addEventListener('error', function(e) {
if (e.readyState == EventSource.CLOSED) {
console.log("连接关闭.");
} else {
console.log(e.readyState);
}
}, false);
} else {
console.log("你的浏览器不支持SSE");
}
</script>
</div>
</body>
</html>
(3)添加配置
在MyMvcConfig.java下面添加如下配置。
registry.addViewController("/sse").setViewName("sse"); // 添加转向sse.jsp页面的映射
(4)运行访问
访问:http://localhost:8080/springmvc/sse
2.基于Servlet3.0的异步支持方法
(1)开启异步方法支持
在WebInitializer.java里面加入,开启异步方法支持。
servlet.setAsyncSupported(true); // 开启异步方法支持
(2)Controller实现
package com.eleven.controller;
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;
import com.eleven.service.PushService;
@Controller // 返回的是字符串,与html页面进行渲染
public class AsyncController {
@Autowired
private PushService pushService; // 将PushService注入到AsyncController中,定时任务,定时更新DeferredResult
@RequestMapping("/defer")
@ResponseBody
public DeferredResult<String> deferredCall(){ // 返回客户端DeferredResult
return pushService.getAsyncUpdate();
}
}
(3)Service定时任务
package com.eleven.service;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.async.DeferredResult;
@Service
public class PushService {
private DeferredResult<String> deferredResult; // 在PushService里面声明一个DeferredResult给控制器使用
public DeferredResult<String> getAsyncUpdate() {
deferredResult = new DeferredResult<String>();
return deferredResult;
}
@Scheduled(fixedDelay = 5000) // 定时更新DeferredResult
public void refresh() {
if(deferredResult != null) {
deferredResult.setResult(new Long(System.currentTimeMillis()).toString());
}
}
}
(4)async.jsp页面
在src/main/resources/views下新建async.jsp页面。
因为此处的代码使用的是jQuery的Ajax请求,所以没有浏览器兼容性问题。
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script type="text/javascript">
deferred(); // 页面打开后就像后台发送请求
function deferred() {
$.get('defer', function(data) {
console.log(data); // 在控制台输出服务端推送的数据
deferred(); // 一次请求完成后在继续像后台发送请求
});
}
</script>
</body>
</html>
(5)添加配置
在MyMvcConfig.java加入注解,开始计划任务的支持.
并添加到Async.jsp页面的映射
@EnableScheduling // 开始计划任务的支持
registry.addViewController("/async").setViewName("async"); // 添加转向async.jsp页面的映射
(6)运行访问
输入地址:http://localhost:8080/springmvc02/async