<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
SSE簡單的來說就是伺服器主動向前端推播資料的一種技術,它是單向的,也就是說前端是不能向伺服器傳送資料的。SSE適用於訊息推播,監控等只需要伺服器推播資料的場景中,下面是使用Spring Boot 來實現一個簡單的模擬向前端推動進度資料,前端頁面接受後展示進度條。
在Spring Boot中使用時需要注意,最好使用Spring Web 提供的SseEmitter這個類來進行操作,我在剛開始時使用網上說的將Content-Type設定為text-stream這種方式發現每次前端每次都會重新建立接。最後參考該文實現了最終想要的效果:
SSEServer.java
package vip.huhailong.catchat.sse; import lombok.extern.slf4j.Slf4j; import org.springframework.http.MediaType; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; /** * @author Huhailong */ @Slf4j public class SSEServer { /** * 當前連線數 */ private static AtomicInteger count = new AtomicInteger(0); private static Map<String, SseEmitter> sseEmitterMap = new ConcurrentHashMap<>(); public static SseEmitter connect(String userId){ //設定超時時間,0表示不過期,預設是30秒,超過時間未完成會丟擲異常 SseEmitter sseEmitter = new SseEmitter(0L); //註冊回撥 sseEmitter.onCompletion(completionCallBack(userId)); sseEmitter.onError(errorCallBack(userId)); sseEmitter.onTimeout(timeOutCallBack(userId)); sseEmitterMap.put(userId,sseEmitter); //數量+1 count.getAndIncrement(); log.info("create new sse connect ,current user:{}",userId); return sseEmitter; } /** * 給指定使用者發訊息 */ public static void sendMessage(String userId, String message){ if(sseEmitterMap.containsKey(userId)){ try{ sseEmitterMap.get(userId).send(message); }catch (IOException e){ log.error("user id:{}, send message error:{}",userId,e.getMessage()); e.printStackTrace(); } } } /** * 想多人傳送訊息,組播 */ public static void groupSendMessage(String groupId, String message){ if(sseEmitterMap!=null&&!sseEmitterMap.isEmpty()){ sseEmitterMap.forEach((k,v) -> { try{ if(k.startsWith(groupId)){ v.send(message, MediaType.APPLICATION_JSON); } }catch (IOException e){ log.error("user id:{}, send message error:{}",groupId,message); removeUser(k); } }); } } 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 batchSendMessage(String message, Set<String> userIds){ userIds.forEach(userId->sendMessage(userId,message)); } public static void removeUser(String userId){ sseEmitterMap.remove(userId); //數量-1 count.getAndDecrement(); log.info("remove user id:{}",userId); } public static List<String> getIds(){ return new ArrayList<>(sseEmitterMap.keySet()); } public static int getUserCount(){ return count.intValue(); } private static Runnable completionCallBack(String userId) { return () -> { log.info("結束連線,{}",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的工具類,下面我們使用一下它
package vip.huhailong.catchat.controller; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import vip.huhailong.catchat.sse.SSEServer; /** * @author Huhailong */ @Slf4j @RestController @CrossOrigin @RequestMapping("/sse") public class SSEController { @GetMapping("/connect/{userId}") public SseEmitter connect(@PathVariable String userId){ return SSEServer.connect(userId); } @GetMapping("/process") public void sendMessage() throws InterruptedException { for(int i=0; i<=100; i++){ if(i>50&&i<70){ Thread.sleep(500L); }else{ Thread.sleep(100L); } SSEServer.batchSendMessage(String.valueOf(i)); } } }
上面的connect是用來連線sse的,它返回一個SseEmitter範例,這時候連線就已經建立了,然後下面的process介面是用來推播資料的,我這裡是準備讓前端實現一個進度條的效果,所以推播的是數位,為了效果明顯,我在推播到50到70的時候速度放慢,其餘都是100ms
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Home</title> <script> let data = new EventSource("/cat-chat/sse/connect/huhailong") data.onmessage = function(event){ console.log("test=>",event) document.getElementById("result").innerText = event.data+'%'; document.getElementById("my-progress").value = event.data; } </script> </head> <body> <div id="result"></div> <progress style="width: 300px" id="my-progress" value="0" max="100"></progress> </body> </html>
最終效果:
到此這篇關於Spring Boot 使用 SSE 方式向前端推播資料詳解的文章就介紹到這了,更多相關Spring Boot SSE內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45