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
SSE-Codesse.jsp
?
服務器推送 可以用于消息訂閱
解決長短輪詢不是解決問題
server send event 當客戶端方服務器發送請求時 服務器抓住不放 等有數據時 再回復給客戶端,客戶端收到消息時發給送給服務器,如此循環
參考內容:
https://www.jianshu.com/p/bc5a9b4a1cd1
?
?
console.log("!!Window EventSource: " + !!Window.EventSource)
if (!!window.EventSource) {
var source = new EventSource('push');
s = '';
source.addEventListener('message', function (evt) {
s += evt.data + "
";
$("#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對象");
}*/
?
顯示的結果
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 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 deferredResult;
?
public DeferredResult getAsyncUpdate() {
deferredResult = new DeferredResult();
return deferredResult;
}
?
@Scheduled(fixedDelay = 5000)
public void refresh() {
if (deferredResult != null) {
deferredResult.setResult(new Long(System.currentTimeMillis()).toString());
}
}
}
3、數據頁面async.jsp
pageEncoding="UTF-8"%>
async support-Codedefer.jsp
?
?
?
?
?
deferred();
?
function deferred() {
$.get('defer', function (data) {
console.log(data);
s = '';
s += data + "
";
$('#defer').html(s)
// 完成后在向服務器請求
deferred();
}
?
);
}
?
4、需要在核銷配置類用開啟任務
@Configuration
@EnableWebMvc
@ComponentScan("com.ch4")
@EnableScheduling
public class MyMvcConfig extends WebMvcConfigurerAdapter {}
SSE-aync.png
總結:
SSE用于訂閱消息,是需要瀏覽器不斷的請求,與websocket有相似之處
2019/06/30晚于成都