springboot實現(xiàn)SSE(Server?Sent?Event)的示例代碼
一、概述
1.SSE是何方神圣
SSE 全稱Server Sent Event,直譯一下就是服務(wù)器發(fā)送事件。
其最大的特點,可以簡單概括為兩個
- 長連接
- 服務(wù)端可以向客戶端推送信息
2.sse與websocket區(qū)別
sse 是單通道,只能服務(wù)端向客戶端發(fā)消息;而 websocket 是雙通道
那么為什么有了 websocket 還要搞出一個 sse 呢?既然存在,必然有著它的優(yōu)越之處
| sse | websocket |
|---|---|
| http 協(xié)議 | 獨立的 websocket 協(xié)議 |
| 輕量,使用簡單 | 相對復雜 |
| 默認支持斷線重連 | 需要自己實現(xiàn)斷線重連 |
| 文本傳輸 | 二進制傳輸 |
| 支持自定義發(fā)送的消息類型 | - |
二、實現(xiàn)過程
下面我們以springboot工程為例,實現(xiàn)服務(wù)器端不間斷向客戶端推送數(shù)據(jù)
1.效果展示
http://124.71.129.204:8080/index

2. 簡要流程

3. 源碼放送
SSE工具類
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.http.MediaType;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import lombok.extern.slf4j.Slf4j;
/**
* Server-Sent Events <BR>
* https://blog.csdn.net/hhl18730252820/article/details/126244274
*/
@Slf4j
public class SSEServer
{
/**
* 當前連接數(shù)
*/
private static AtomicInteger count = new AtomicInteger(0);
private static Map<String, SseEmitter> sseEmitterMap = new ConcurrentHashMap<>();
public static SseEmitter connect()
{
String userId = RandomStringUtils.randomAlphanumeric(10);
SseEmitter sseEmitter = new SseEmitter(0L); // 設(shè)置超時時間,0表示不過期,默認是30秒,超過時間未完成會拋出異常
// 注冊回調(diào)
sseEmitter.onCompletion(completionCallBack(userId));
sseEmitter.onError(errorCallBack(userId));
sseEmitter.onTimeout(timeOutCallBack(userId));
sseEmitterMap.put(userId, sseEmitter);
log.info("create new sse connect ,current user:{}, count: {}", userId, count.incrementAndGet());
return sseEmitter;
}
public static void batchSendMessage(String message)
{
sseEmitterMap.forEach((k, v) -> {
try
{
v.send(message, MediaType.APPLICATION_JSON);
}
catch (IOException e)
{
log.error("user id:{}, send message error:{}", k, e.getMessage());
removeUser(k);
}
});
}
public static void removeUser(String userId)
{
sseEmitterMap.remove(userId);
log.info("remove user id:{}, count: {}", userId, count.decrementAndGet());
}
public static int getUserCount()
{
return count.intValue();
}
private static Runnable completionCallBack(String userId)
{
return () -> {
log.info("結(jié)束連接,{}", userId);
removeUser(userId);
};
}
private static Runnable timeOutCallBack(String userId)
{
return () -> {
log.info("連接超時,{}", userId);
removeUser(userId);
};
}
private static Consumer<Throwable> errorCallBack(String userId)
{
return throwable -> {
log.error("連接異常,{}", userId);
removeUser(userId);
};
}
}
sse接口
import java.util.concurrent.TimeUnit;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import com.fly.hello.service.SSEServer;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Api(tags = "sse接口")
@RestController
@RequestMapping("/sse")
public class SSEController
{
long i = -1;
@ApiOperation("初始化")
@GetMapping("/connect/{userId}")
public SseEmitter connect(@PathVariable String userId)
{
SseEmitter sseEmitter = SSEServer.connect();
if (i < 0)
{
new Thread(() -> sendMessage()).start();
}
return sseEmitter;
}
private void sendMessage()
{
if (i < 0) // 保證僅觸發(fā)一次
{
log.info("Server-Sent Events start");
while (true)
{
try
{
TimeUnit.MILLISECONDS.sleep(1000);
}
catch (InterruptedException e)
{
}
i = ++i % 101;
SSEServer.batchSendMessage(String.valueOf(i));
}
}
}
}
頁面引用sse
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<link href="css/bootstrap.min.css" rel="external nofollow" rel="stylesheet" type="text/css" />
<style>
body {
margin: 10;
font-size: 62.5%;
line-height: 1.5;
}
.blue-button {
background: #25A6E1;
padding: 3px 20px;
color: #fff;
font-size: 10px;
border-radius: 2px;
-moz-border-radius: 2px;
-webkit-border-radius: 4px;
border: 1px solid #1A87B9
}
table {
width: 60%;
}
th {
background: SteelBlue;
color: white;
}
td, th {
border: 1px solid gray;
font-size: 12px;
text-align: left;
padding: 5px 10px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
max-width: 200px;
white-space: nowrap;
text-overflow: ellipsis;
text-overflow: ellipsis;
}
</style>
</head>
<title>Hello World!</title>
<script>
let data = new EventSource("/sse/connect/001")
data.onmessage = function(event) {
document.getElementById("result").innerText = event.data + '%';
document.getElementById("my-progress").value = event.data;
}
</script>
<body>
<div class="wrapper-page">
<table align="center">
<tr>
<th colspan="4">Navigate</th>
</tr>
<tr>
<td><a href="/index" rel="external nofollow" target="_self">index</a></td>
<td><a href="/404" rel="external nofollow" target="_self">出錯頁面</a></td>
<td><a href="/doc.html" rel="external nofollow" target="_blank">doc.html</a></td>
<td><a href="/h2-console" rel="external nofollow" target="_blank">h2-console</a></td>
</tr>
</table>
<div class="ex-page-content text-center">
<h2 align="center">
<a href="index" rel="external nofollow" >reload</a>
<div><progress style="width: 60%" id="my-progress" value="0" max="100"></progress></div>
<div id="result"></div>
</h2>
<img src="show/girl" width="600" height="600" />
<img src="show/pic" width="600" height="600" />
</div>
</div>
</body>
</html>
4.完整項目
https://gitcode.com/00fly/springboot-hello
git clone https://gitcode.com/00fly/springboot-hello.git
到此這篇關(guān)于springboot實現(xiàn)SSE(Server Sent Event)的示例代碼的文章就介紹到這了,更多相關(guān)springboot實現(xiàn)SSE內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 仿天貓服裝商城系統(tǒng)的實現(xiàn)流程
讀萬卷書不如行萬里路,只學書上的理論是遠遠不夠的,只有在實戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+SSM+jsp+mysql+maven實現(xiàn)一個仿天貓服裝商城系統(tǒng),大家可以在過程中查缺補漏,提升水平2021-11-11
Java使用fill()數(shù)組填充的實現(xiàn)
這篇文章主要介紹了Java使用fill()數(shù)組填充的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-01-01
全網(wǎng)最精細詳解二叉樹,2萬字帶你進入算法領(lǐng)域
大家好,我是哪吒,一個熱愛編碼的Java工程師,本著"欲速則不達,欲達則欲速"的學習態(tài)度,在程序猿這條不歸路上不斷成長,所謂成長,不過是用時間慢慢擦亮你的眼睛,少時看重的,年長后卻視若鴻毛,少時看輕的,年長后卻視若泰山,成長之路,亦是漸漸放下執(zhí)念,內(nèi)心歸于平靜的旅程2021-08-08
Java請求調(diào)用參數(shù)格式為form-data類型的接口代碼示例
這篇文章主要給大家介紹了關(guān)于Java請求調(diào)用參數(shù)格式為form-data類型的接口的相關(guān)資料,文中給出了詳細的代碼示例,對大家的學習或者工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-08-08

