成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

全棧實戰!用 WebSocket 實現實時消息推送 + 動態進度條可視化

開發 前端
借助 WebSocket 實現的實時通信機制,我們有效地解決了輪詢帶來的性能瓶頸和用戶體驗問題。無論是消息推送,待辦提醒,還是任務進度的動態刷新,WebSocket 都提供了更優雅與高效的解決方案。

在傳統 Web 應用中,任務狀態查詢或通知推送往往依賴前端定時輪詢接口獲取數據。雖然這種方式實現簡單,但在數據頻繁變化或用戶量激增的場景下,頻繁的 HTTP 請求會引起數據庫壓力增大,響應延遲甚至系統性能下降。

本文將基于 Spring Boot + WebSocket 的技術棧,構建一個服務端主動推送消息的實時提醒系統,并可視化每項任務的進度。前端將通過 WebSocket 進行一次性連接,并實時響應后端推送的最新數據,從而極大提升用戶體驗與系統性能。

系統功能概覽

  • 待辦數量實時推送
  • 通知紅點自動刷新
  • 支持 WebSocket 持久連接
  • 動態進度條展示任務完成情況
  • 前后端獨立交互,解耦式開發結構

依賴配置(Maven)

添加必要的依賴于 pom.xml 文件中:

<!-- MyBatis Plus & MySQL -->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-boot-starter</artifactId>
    <version>3.4.2</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <scope>runtime</scope>
</dependency>


<!-- WebSocket -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
    <version>2.1.6.RELEASE</version>
</dependency>

數據庫結構設計

建立兩張表用于模擬待辦任務及其子任務進度:

CREATE TABLE t_todo (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  user_name VARCHAR(255) COMMENT '用戶名稱',
  name VARCHAR(255) COMMENT '待辦標題'
) COMMENT='待辦任務主表';


CREATE TABLE t_todo_attr (
  id BIGINT PRIMARY KEY AUTO_INCREMENT,
  todo_id BIGINT COMMENT '主表ID',
  status INT COMMENT '完成狀態 1為已完成'
) COMMENT='待辦任務進度子表';

一條 t_todo 記錄表示一個任務,對應若干 t_todo_attr 子任務進度項。

WebSocket 服務端配置

WebSocket 注冊配置

// /src/main/java/com/icoderoad/config/WebSocketConfig.java
@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

實現主任務通知服務

// /src/main/java/com/icoderoad/ws/WebSocketTodoServer.java
@ServerEndpoint("/ws/todo/{username}")
@Component
public class WebSocketTodoServer {
    private static final Map<String, Session> sessions = new ConcurrentHashMap<>();


    @OnOpen
    public void open(Session session, @PathParam("username") String username) {
        sessions.put(username, session);
        int count = SpringContextUtil.getBean(TodoService.class)
                .count(new LambdaQueryWrapper<Todo>().eq(Todo::getUserName, username));
        send(session, String.valueOf(count));
    }


    @OnClose
    public void close(@PathParam("username") String username) {
        sessions.remove(username);
    }


    @OnMessage
    public void message(String msg) {}


    @OnError
    public void error(Session session, Throwable throwable) {
        throwable.printStackTrace();
    }


    public void sendInfo(String username, String msg) {
        Session session = sessions.get(username);
        send(session, msg);
    }


    private void send(Session session, String msg) {
        if (session != null) {
            synchronized (session) {
                try {
                    session.getBasicRemote().sendText(msg);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

后端接口實現

// /src/main/java/com/icoderoad/controller/TodoController.java
@RestController
@RequestMapping("/todo")
public class TodoController {


    @Autowired private TodoService todoService;
    @Autowired private WebSocketTodoServer wsServer;


    @PostMapping("/insert")
    public ResponseUtils insert(@RequestParam String todoName, @RequestParam String userName) {
        Todo todo = new Todo();
        todo.setName(todoName);
        todo.setUserName(userName);
        todoService.save(todo);


        int count = todoService.count(new LambdaQueryWrapper<Todo>().eq(Todo::getUserName, userName));
        wsServer.sendInfo(userName, String.valueOf(count));


        return ResponseUtils.success(todoName);
    }


    @GetMapping("/list")
    public ResponseUtils list(@RequestParam String userName) {
        List<Todo> todos = todoService.list(new LambdaQueryWrapper<Todo>().eq(Todo::getUserName, userName));
        return ResponseUtils.success(todos);
    }
}

前端頁面展示

<!-- /src/main/resources/static/index.html -->
<div class="message-container" onclick="toggleTodo()">
    <div class="bell-icon"></div>
    <span class="message-count">0</span>
</div>
<div class="todo-section" id="todoSection" style="display:none;"></div>


<script>
    const socket = new WebSocket('ws://localhost:8077/ws/todo/張三');
    socket.onmessage = (event) => {
        document.querySelector('.message-count').textContent = event.data;
    };


    async function toggleTodo() {
        const section = document.getElementById('todoSection');
        section.style.display = section.style.display === 'none' ? 'block' : 'none';
        if (section.style.display === 'block') {
            const res = await fetch('/todo/list?userName=張三');
            const data = await res.json();
            section.innerHTML = data.data.map(t => `<div>${t.name}</div>`).join('');
        }
    }
</script>

子任務進度 WebSocket(進度條)

// /src/main/java/com/icoderoad/ws/WebSocketTodoAttrServer.java
@ServerEndpoint("/ws/todo/attr/{todoId}")
@Component
public class WebSocketTodoAttrServer {
    private static final Map<String, Session> attrSessions = new ConcurrentHashMap<>();


    @OnOpen
    public void onOpen(Session session, @PathParam("todoId") String todoId) {
        attrSessions.put(todoId, session);
        String progress = SpringContextUtil.getBean(TodoAttrService.class).progress(Long.valueOf(todoId));
        send(session, progress);
    }


    public void sendInfo(String todoId, String msg) {
        send(attrSessions.get(todoId), msg);
    }


    private void send(Session session, String msg) {
        try {
            if (session != null) session.getBasicRemote().sendText(msg);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

任務進度更新接口

// /src/main/java/com/icoderoad/controller/TodoAttrController.java
@PostMapping("/attr/update")
public ResponseUtils updateAttr(@RequestParam Long id) {
    todoAttrService.updateById(new TodoAttr(id, 1));
    TodoAttr attr = todoAttrService.getById(id);
    webSocketTodoAttrServer.sendInfo(String.valueOf(attr.getTodoId()),
            todoAttrService.progress(attr.getTodoId()));
    return ResponseUtils.success();
}

結語:高性能實時系統構建的利器

借助 WebSocket 實現的實時通信機制,我們有效地解決了輪詢帶來的性能瓶頸和用戶體驗問題。無論是消息推送,待辦提醒,還是任務進度的動態刷新,WebSocket 都提供了更優雅與高效的解決方案。

未來在構建具有實時性要求的系統(如 IM 聊天、實時告警、系統監控等)時,WebSocket 可以作為首選的通信技術基礎,而非傳統的“輪詢 + 回調”。

責任編輯:武曉燕 來源: 路條編程
相關推薦

2023-12-27 13:45:00

Python進度條代碼

2024-09-02 09:31:19

2024-08-06 14:29:37

2009-08-17 14:41:47

C#進度條實現

2009-08-17 17:15:48

C# 進度條效果

2021-09-27 08:31:01

數據可視化柱狀圖折現圖

2015-07-31 11:19:43

數字進度條源碼

2023-12-11 17:15:05

應用開發波紋進度條ArkUI

2024-06-13 08:15:00

2013-04-12 10:05:49

HTML5WebSocket

2023-11-17 09:35:58

2021-09-27 10:43:18

鴻蒙HarmonyOS應用

2009-08-17 15:48:47

C# WinForm進

2021-02-05 07:28:11

SpringbootNettyWebsocke

2017-10-14 13:54:26

數據可視化數據信息可視化

2009-07-21 14:49:55

XmlHttpRequ文件上傳進度條

2023-11-30 11:38:29

CSS網頁進度條

2011-07-05 15:16:00

QT 進度條

2024-07-25 08:55:47

進度條水缸進度動畫效果

2022-05-16 09:34:17

Python可視化圖表
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91精品国产乱码麻豆白嫩 | 亚洲欧美精品在线 | 精品一区二区三区四区 | 成人在线免费网站 | 精品国产欧美在线 | 9久9久| 日韩在线观看 | 国产成人久久精品一区二区三区 | 99精品免费久久久久久日本 | 欧美综合在线观看 | 一区二区三区在线 | 男女免费观看在线爽爽爽视频 | 国产精品日韩欧美 | 国产欧美在线播放 | 免费同性女女aaa免费网站 | 日一日操一操 | 国产午夜亚洲精品不卡 | 国产精品永久免费观看 | 免费黄色片在线观看 | 在线色网 | 午夜精品久久久久久久99黑人 | 精品视频一二区 | 国产精品99久久久久久久久久久久 | 成人网视频 | 99久久精品免费看国产四区 | 国产一在线| 日韩成人专区 | 极品国产视频 | 成人精品国产免费网站 | 一区二区免费 | 亚洲国产精品久久久久婷婷老年 | 欧美成人第一页 | 在线超碰| 亚洲精品视频在线观看免费 | 人人九九精 | 欧美日韩精品久久久免费观看 | 日韩三区| 亚洲aⅴ一区二区 | 日本人麻豆 | 成人区一区二区三区 | 国产一级片网站 |