从 0 到 1 搭建实时数据看板:RabbitMQ+WebSocket 实战指南

在当今数据驱动的时代,实时数据可视化已成为企业决策的核心需求。无论是电商平台的实时交易监控,还是物联网系统的设备状态看板,都需要高效、稳定的数据实时采集与推送能力。本文将带你构建一套完整的实时数据处理 pipeline,通过 RabbitMQ 实现高效的数据采集,结合 WebSocket 技术将数据毫秒级推送到前端看板,让你轻松掌握实时数据处理的核心技术。

技术选型与架构设计

核心技术栈解析

实现实时数据看板需要解决三个关键问题:数据采集、消息传递和实时推送。我们选择以下技术栈组合:

  • 数据采集层:多样化数据源接入,包括数据库变更、API 接口、日志文件等
  • 消息中间件:RabbitMQ 3.13.0,提供可靠的消息投递和灵活的路由策略
  • 实时推送层:WebSocket,基于 Spring WebSocket 6.1.2 实现双向通信
  • 后端框架:Spring Boot 3.2.0,简化开发流程
  • 前端框架:Vue 3 + ECharts 5.4.3,实现数据可视化
  • 数据库:MySQL 8.0.35,存储元数据和历史数据
  • ORM 框架:MyBatis-Plus 3.5.5,简化数据库操作
  • JSON 处理:FastJSON2 2.0.45,高效 JSON 序列化 / 反序列化

整体架构设计

下面是系统的整体架构图,清晰展示了数据从产生到最终展示的完整流程:

数据流转流程

  1. 多源数据通过采集器接入系统,统一格式后发送到 RabbitMQ 交换机
  2. 交换机根据路由规则将数据分发到不同队列:
    • 正常数据进入数据处理队列
    • 异常数据进入死信队列等待后续处理
  3. 应用服务消费数据处理队列中的消息,进行业务处理
  4. 处理后的数据一方面持久化到数据库,另一方面通过 WebSocket 推送到前端
  5. 前端接收数据后,通过 ECharts 实时更新可视化图表

环境搭建与项目初始化

开发环境准备

首先确保你的开发环境满足以下要求:

  • JDK 17+(推荐 Amazon Corretto 17.0.10)
  • Maven 3.9.6
  • MySQL 8.0.35+
  • RabbitMQ 3.13.0(带 Management 插件)
  • Node.js 18.19.0+(用于前端开发)
  • IDE:IntelliJ IDEA 2023.3+

RabbitMQ 安装与配置

  1. 安装 RabbitMQ(以 Linux 为例):
# 安装Erlang依赖
sudo apt update
sudo apt install erlang

# 安装RabbitMQ
sudo apt install rabbitmq-server

# 启动服务
sudo systemctl start rabbitmq-server

# 启用管理插件
sudo rabbitmq-plugins enable rabbitmq_management

# 配置管理员用户
sudo rabbitmqctl add_user admin password
sudo rabbitmqctl set_user_tags admin administrator
sudo rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"
  1. 访问 RabbitMQ 管理界面:http://localhost:15672,使用账号 admin/password 登录

  2. 创建所需的交换机和队列:

    • 交换机:data.collect.exchange(类型:topic)
    • 数据处理队列:data.process.queue,绑定键:data.*
    • 异常队列:data.error.queue,绑定键:error.*

项目初始化(Spring Boot 后端)

  1. 创建 Maven 项目,pom.xml 配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.0</version>
        <relativePath/>
    </parent>
    <groupId>com.realtime</groupId>
    <artifactId>data-dashboard</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>data-dashboard</name>
    <description>实时数据看板系统</description>
    
    <properties>
        <java.version>17</java.version>
        <mybatis-plus.version>3.5.5</mybatis-plus.version>
        <fastjson2.version>2.0.45</fastjson2.version>
        <lombok.version>1.18.30</lombok.version>
        <swagger.version>3.0.0</swagger.version>
    </properties>
    
    <dependencies>
        <!-- Spring Boot核心 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        
        <!-- 数据库 -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        
        <!-- MyBatis-Plus -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>${mybatis-plus.version}</version>
        </dependency>
        
        <!-- JSON处理 -->
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>${fastjson2.version}</version>
        </dependency>
        
        <!-- 工具类 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>33.1.0-jre</version>
        </dependency>
        
        <!-- API文档 -->
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>2.2.0</version>
        </dependency>
        
        <!-- 测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
  1. 配置文件(application.yml):
spring:
  application:
    name: data-dashboard
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/realtime_dashboard?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password: root
  rabbitmq:
    host: localhost
    port: 5672
    username: admin
    password: password
    virtual-host: /
    listener:
      simple:
        acknowledge-mode: manual  # 手动确认消息
        concurrency: 3  # 消费者并发数
        max-concurrency: 10  # 最大消费者并发数
        prefetch: 100  # 每次从队列获取的消息数
    template:
      retry:
        enabled: true  # 启用重试
        max-attempts: 3  # 最大重试次数
        initial-interval: 1000ms  # 初始重试间隔

server:
  port: 8080
  servlet:
    context-path: /api

mybatis-plus:
  mapper-locations: classpath*:/mapper/**/*.xml
  global-config:
    db-config:
      id-type: auto
      logic-delete-field: deleted
      logic-delete-value: 1
      logic-not-delete-value: 0
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

springdoc:
  api-docs:
    path: /api-docs
  swagger-ui:
    path: /swagger-ui.html
    operationsSorter: method

logging:
  level:
    com.realtime: info
    org.springframework.amqp.rabbit.listener: warn
  1. 数据库初始化脚本(MySQL):
CREATE DATABASE IF NOT EXISTS realtime_dashboard DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE realtime_dashboard;

-- 数据采集记录表
CREATE TABLE IF NOT EXISTS data_collection (
    id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
    data_type VARCHAR(50) NOT NULL COMMENT '数据类型',
    data_content JSON NOT NULL COMMENT '数据内容',
    source VARCHAR(100) NOT NULL COMMENT '数据来源',
    status TINYINT NOT NULL DEFAULT 0 COMMENT '状态:0-新采集 1-已处理 2-异常',
    create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
    process_time DATETIME NULL COMMENT '处理时间',
    deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除:0-未删除 1-已删除',
    INDEX idx_create_time (create_time),
    INDEX idx_data_type (data_type)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据采集记录表';

-- 设备状态表(示例)
CREATE TABLE IF NOT EXISTS device_status (
    id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
    device_id VARCHAR(50) NOT NULL COMMENT '设备ID',
    device_name VARCHAR(100) NOT NULL COMMENT '设备名称',
    status TINYINT NOT NULL COMMENT '状态:0-离线 1-在线 2-异常',
    temperature DECIMAL(5,2) NULL COMMENT '温度',
    humidity DECIMAL(5,2) NULL COMMENT '湿度',
    last_update_time DATETIME NOT NULL COMMENT '最后更新时间',
    deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除:0-未删除 1-已删除',
    UNIQUE KEY uk_device_id (device_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='设备状态表';

-- 系统配置表
CREATE TABLE IF NOT EXISTS system_config (
    id BIGINT AUTO_INCREMENT PRIMARY KEY COMMENT '主键ID',
    config_key VARCHAR(50) NOT NULL COMMENT '配置键',
    config_value VARCHAR(255) NOT NULL COMMENT '配置值',
    config_desc VARCHAR(255) NULL COMMENT '配置描述',
    update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
    deleted TINYINT NOT NULL DEFAULT 0 COMMENT '逻辑删除:0-未删除 1-已删除',
    UNIQUE KEY uk_config_key (config_key)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统配置表';

核心数据模型设计

良好的数据模型设计是系统稳定运行的基础,我们需要设计一套灵活且可扩展的数据结构来应对不同类型的实时数据。

通用数据传输对象(DTO)

首先定义一个通用的数据传输对象,作为所有实时数据的载体:

package com.realtime.dto;

import com.alibaba.fastjson2.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;

/**
 * 通用数据传输对象,用于在系统各组件间传递数据
 *
 * @author ken
 */
@Data
@Schema(description = "通用数据传输对象")
public class GenericDataDTO {
    /**
     * 数据唯一标识
     */
    @Schema(description = "数据唯一标识")
    private String dataId;

    /**
     * 数据类型(用于路由和处理)
     */
    @Schema(description = "数据类型", example = "device.status, order.payment")
    private String dataType;

    /**
     * 数据来源
     */
    @Schema(description = "数据来源", example = "iot-gateway, order-service")
    private String source;

    /**
     * 数据内容(JSON格式)
     */
    @Schema(description = "数据内容(JSON格式)")
    private String content;

    /**
     * 数据产生时间
     */
    @Schema(description = "数据产生时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime timestamp;
}

设备状态数据模型

以设备状态数据为例,展示具体业务数据模型的设计:

package com.realtime.dto;

import com.alibaba.fastjson2.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;

/**
 * 设备状态数据DTO
 *
 * @author ken
 */
@Data
@Schema(description = "设备状态数据DTO")
public class DeviceStatusDTO {
    /**
     * 设备ID
     */
    @Schema(description = "设备ID", example = "device-1001")
    private String deviceId;

    /**
     * 设备名称
     */
    @Schema(description = "设备名称", example = "温度传感器-01")
    private String deviceName;

    /**
     * 设备状态:0-离线 1-在线 2-异常
     */
    @Schema(description = "设备状态:0-离线 1-在线 2-异常", example = "1")
    private Integer status;

    /**
     * 温度(摄氏度)
     */
    @Schema(description = "温度(摄氏度)", example = "25.6")
    private Double temperature;

    /**
     * 湿度(百分比)
     */
    @Schema(description = "湿度(百分比)", example = "45.2")
    private Double humidity;

    /**
     * 信号强度
     */
    @Schema(description = "信号强度", example = "95")
    private Integer signalStrength;

    /**
     * 数据采集时间
     */
    @Schema(description = "数据采集时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime collectTime;
}

实体类设计(MyBatis-Plus)

对应数据库表的实体类设计:

package co
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值