【24】数据可视化:基于 Echarts + Python 动态实时大屏范例 - HTTP & WebSocket 网络传输协议

目录

WebSocket

握手协议

优点

效果展示

一、 确定需求方案

1、确定产品上线部署的屏幕分辨率

2、部署方式 

二、整体架构设计

三、编码实现 (基于篇幅及可读性考虑,此处展示部分关键代码)

1、前端html代码 - 页面整体布局 

2、前端JS代码 - Echarts option设置

3、 前端WebSocket通信

(1)建立连接

(2)连接建立成功事件

(3)监听数据事件

(4)连接断开事件

4、前端 Echarts 更新 option 中3个图表的data

5、后端python实现的httpserver服务代码

6、后端python实现的WebSocket服务代码

(1)响应客户端消息,统一为json格式

(2)主动发送消息给客户端

四、上线运行效果

五、启动命令

六、源码下载

更多精彩案例


写在前面,最近收到了小伙伴们的问题,大屏的数据传输可否不是客户端去http get获取,而是由服务器端主动推送,那要怎么实现那? 答:websocket协议。本案例讲述了 HTTP GET  Web Socket 两种获取数据的方式。

之前小伙伴们建议我出一些视频课程来学习Echarts,这样可以更快上手,所以我就追星赶月的录制了《Echart-0基础入门》系列课程(共14节课) ,希望小伙伴们多多支持。

话不多说,开始分享干货,欢迎讨论!QQ微信同号: 6550523

WebSocket

WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。

很多网站为了实现推送技术,所用的技术都是轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

在这种情况下,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

握手协议

  • WebSocket 是独立的、创建在 TCP 上的协议。
  • Websocket 通过HTTP/1.1 协议的101状态码进行握手。
  • 为了创建Websocket连接,需要通过浏览器发出请求,之后服务器进行回应,这个过程通常称为“握手”(handshaking)。

优点

  • 较少的控制开销。在连接创建后,服务器和客户端之间交换数据时,用于协议控制的数据包头部相对较小。在不包含扩展的情况下,对于服务器到客户端的内容,此头部大小只有2至10字节(和数据包长度有关);对于客户端到服务器的内容,此头部还需要加上额外的4字节的掩码。相对于HTTP请求每次都要携带完整的头部,此项开销显著减少。
  • 更强的实时性。由于协议是全双工的,所以服务器可以随时主动给客户端下发数据。
  • 保持连接状态。与HTTP不同的是,Websocket需要先创建连接,这就使得其成为一种有状态的协议,之后通信时可以省略部分状态信息。而HTTP请求可能需要在每个请求都携带状态信息(如身份认证等)。

效果展示

 

一、 确定需求方案

1、确定产品上线部署的屏幕分辨率

根据电脑分辨率屏幕自适应显示,F11全屏查看;

2、部署方式 

B/S方式:支持Windows、Linux、Mac等各种主流操作系统;支持主流浏览器Chrome,Microsoft Edge,360等;服务器采用python语言编写,配置下python依赖即可。

二、整体架构设计

  1. 前端Echarts开源库:使用WebStorm编辑器;
  2. 后端 http服务器:基于 Python 实现,使用Pycharm或VSCode编辑器;
  3. 数据传输格式:JSON;
  4. 数据源类型:JSON文件。实际开发需求中,支持定制HTTP API接口方式或其它各种类型数据库,如PostgreSQL、MySQL、Oracle、Microsoft SQL Server、SQLite、Excel表格等。
  5. 数据更新方式:采用http get 轮询方式 和 WebSocket 推送方式。在实际开发需求中,采用后端数据实时更新,实时推送到前端这种方式具有实用性;

三、编码实现 (基于篇幅及可读性考虑,此处展示部分关键代码)

1、前端html代码 - 页面整体布局 


<body style="background-color:#21202e">
 
    <div class="container_fluid">
        <div class="row_fluid" id="vue_app">
 
            <div style="padding:2px; background-color:#292835;" class="col-xs-12 col-md-12">
                <div style="padding: 0 0" class="col-xs-12 col-md-4">
                    <dv-decoration-1 style="width:80%;height:8%;" />
                </div>
                <div style="padding: 0 0;text-align: center;" class="col-xs-12 col-md-4">
                    <div style="padding: 0; height:8%;" ><h3 id="container_0" style="color:#17c0ff; " ></h3></div>
                </div>
                <div style="padding: 0 0; text-align: right;" class="col-xs-12 col-md-4">
                    <dv-decoration-1 style="width:80%;height:8%;" />
                </div>
            </div>
 
            <div style="padding:0;" class="col-xs-12 col-md-4">
                <div style="padding:2px;" class="col-xs-12 col-md-6">
                    <div style="padding:5%; height:26%; background-color:#292835; " id="container_1"></div>
                </div>
 
                <div style="padding:2px;" class="col-xs-12 col-md-6">
                    <div style="padding: 5%; height:26%;background-color:#292835;" id="container_2"></div>
                </div>
 
                <div style="padding:2px;" class="col-xs-12 col-md-6">
                    <div style="padding: 5%; height:26%; background-color:#292835;" id="container_3"></div>
                </div>
 
                <div style="padding:2px;" class="col-xs-12 col-md-6">
                    <div style="padding: 5%; height:26%; background-color:#292835;" id="container_4"></div>
                </div>
            </div>
 
            <div style="padding:0px;" class="col-xs-12 col-md-4">
                <div style="padding:2px; " class="col-xs-12 col-md-12">
                    <div style="padding:3%; height:52.8%; background-color:#292835; " id="container_5"></div>
                </div>
            </div>
 
            <div style="padding: 0;" class="col-xs-12 col-md-4">
                <!-- <div style="padding: 2px; " class="col-xs-12 col-md-12">
                    <div style="padding:5%; height:26%;background-color:#292835;" id="container_6"></div>
                </div> -->
 
                <div style="padding: 2px; " class="col-xs-12 col-md-12">
                    <div style="padding:5%;height:52.8%;background-color:#292835;" id="container_7"></div>
                </div>
            </div>
 
            <div style="padding:0;" class="col-xs-12 col-md-12">
                <div style="padding:2px;" class="col-xs-12 col-md-2">
                    <div style="padding: 5%; height:40%;background-color:#292835;" id="container_8"></div>
                </div>
 
                <div style="padding:2px;" class="col-xs-12 col-md-2">
                    <div style="padding: 5%;height:40%;background-color:#292835;" id="container_9"></div>
                </div>
 
                <div style="padding:2px;" class="col-xs-12 col-md-2">
                    <div style="padding: 5%;height:40%;background-color:#292835;" id="container_10"></div>
                </div>
 
                <div style="padding:2px;" class="col-xs-12 col-md-3">
                    <div style="padding: 5%;height:40%;background-color:#292835;" id="container_11"></div>
                </div>
 
                <div style="padding:2px;" class="col-xs-12 col-md-3">
                    <div style="padding: 5%;height:40%;background-color:#292835;" id="container_12"></div>
                </div>
            </div>
 
        </div>
    </div>
</body>

2、前端JS代码 - Echarts option设置


option = {
    title: {
        text: '微信公众号',
        left: 'left',
        textStyle: {
            color: '#17c0ff',
            fontSize: '12'
        }
    },
    tooltip: {
        trigger: 'axis',
        axisPointer: {
            type: 'cross',
            label: {
                backgroundColor: '#6a7985'
            }
        }
    },
    legend: {
        data: ['总粉丝数', '活跃粉丝数', '昨日净增粉丝数'],
        top: '10%',
        left: 'center',
        textStyle: {
            color: '#fff'
        },
    },
    toolbox: {
        feature: {
            saveAsImage: {}
        }
    },
    grid: {
        left: '3%',
        right: '4%',
        bottom: '3%',
        top: '35%',
        containLabel: true
    },
    xAxis: [
        {
            type: 'category',
            boundaryGap: false,
            data: ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
        }
    ],
    yAxis: [
        {
            type: 'value'
        }
    ],
    series: [
        {
            name: '昨日净增粉丝数',
            type: 'line',
            stack: '总量',
            areaStyle: {},
            emphasis: {
                focus: 'series'
            },
            data: []
        },
        {
            name: '活跃粉丝数',
            type: 'line',
            stack: '总量',
            areaStyle: {},
            emphasis: {
                focus: 'series'
            },
            data:[]
        },
        {
            name: '总粉丝数',
            type: 'line',
            stack: '总量',
            label: {
                show: true,
                position: 'top'
            },
            areaStyle: {},
            emphasis: {
                focus: 'series'
            },
            data: []
        }
    ]
};

3、 前端WebSocket通信

微信公众号 模块(area_stack.js) 图表使用了WebSocket方式主动推送数据到前端

(1)建立连接

 // 第2种 web socket 方式:  Echarts area_stacked
    var ws = new WebSocket('ws://127.0.0.1:8888');

(2)连接建立成功事件

  // 建立 web socket 连接成功触发事件
    ws.onopen = function () {
        // 连接成功后发送登录消息
        login = {"req":"login","id":"wsdemo"}
        ws.send(JSON.stringify(login));
    };

(3)监听数据事件


    //接收消息的回调方法
    ws.onmessage = function (event) {
        //解析成 json
        data = JSON.parse(event.data)
        console.log(data.rsp, data.data);
        //更新echarts图表
        if(data.rsp == 'area_stacked') {
            wsSetOption_12(data.data);
        }
    }

(4)连接断开事件

    //断开 web socket 连接成功触发事件
    ws.onclose = function () {
        ws.close();
    };
});

4、前端 Echarts 更新 option 中3个图表的data

function wsSetOption_12(data) {
    console.log(data.length)
    var myChart = echarts.init(document.getElementById(idContainer_12));
    myChart.setOption({
        series: [{ data: data[0] }, { data: data[1] }, { data: data[2] }],
    });
}

5、后端python实现的httpserver服务代码

def HttpServer():
    try:
        server = HTTPServer((ip, port), MyRequestHandler)
        listen = "http://%s:%d" % (ip, port)
        print("服务器监听地址: ", listen)
        server.serve_forever()
    except ValueError as e:
        print("Exception", e)
        server.socket.close()

6、后端python实现的WebSocket服务代码

# 监听ip ,端口 注册 ws_on 函数
async def main():
    async with serve(ws_on, "127.0.0.1", 8888):
        await asyncio.Future()  # run forever

(1)响应客户端消息,统一为json格式

async def ws_on(ws):
    async for message in ws:
        data = json.loads(message)
        print("服务器收到消息:", data)

        # 登录验证后对连接做标记, 保存到 websockets_list 字典中
        if data["req"] == "login":
            id = data["id"]
            print("客户端登录: ", id)
            websockets_list[id] = ws
        # 处理其他数据
        else:
            handle(data)

        data["rsp"] = "ok"
        await ws.send("服务器响应消息:" + json.dumps(data))
        print("服务器响应消息:", data)

(2)主动发送消息给客户端

async def ws_send():
    while 1:
        ws= None
        data = {}
        try:
            ws = websockets_list[id_wsdemo]
            data["rsp"] = "area_stacked"
            data["data"] = get_json("myjson/area_stacked.json")
            await ws.send(json.dumps(data))
            print("已发送:", data)
        except Exception as e:
            print("不存在的websocket:", e)

        await asyncio.sleep(2)

四、上线运行效果

五、启动命令

<!-- 启动server命令 -->
python httpserver.py 

<!-- 浏览器中输入网址查看大屏(端口为 httpserver.py 中的 port 参数定义) -->
http://localhost:88xx 

<!-- 更多资料参考我的博客主页  -->
https://yydatav.blog.csdn.net/

<!-- 更多案例参考 -->
https://blog.csdn.net/lildkdkdkjf/article/details/120705616

六、源码下载

24【源码】数据可视化:基于Echarts+Python动态实时大屏范例-WebSocket协议.zip-企业管理文档类资源-CSDN下载

注:源码包括python的httpserver + Websocket  + ECharts

本次分享结束,欢迎讨论!QQ微信同号: 6550523

更多精彩案例

YYDatav的数据可视化《精彩案例汇总》_YYDataV的博客-CSDN博客

&lt;think&gt;好的,我现在需要帮助用户了解如何使用WebSocket实现数据的更新功能,并且根据他们的引用资料给出最佳实践。首先,我得回顾一下用户提供的引用内容,看看里面有没有可以直接应用的信息。 用户提到在Java开发实时数据仪表盘时,常用技术包括Spring Boot、WebSocket、Vue.js或React。前后端分离通过RESTful API或WebSocket实现,前端通过AJAX或WebSocket获取数据更新。这说明用户可能已经有了后端用Spring Boot,前端用Vue或React的基础架构,需要整合WebSocket来实现实时数据推送。 接下来,我需要思考WebSocket的基本原理。WebSocket是一种全双工通信协议,允许服务端主动向客户端推送数据,这对于实时更新非常重要,相比传统的轮询方式更高效。然后,考虑如何在Spring Boot中集成WebSocket。可能需要添加依赖,比如Spring WebSocket库,然后配置WebSocket处理器和拦截器。 然后,前端的部分,用户可能使用Vue.js或React,所以需要编写对应的WebSocket客户端代码,建立连接,监听消息,并在收到数据时更新界面。同时,需要考虑错误处理,比如连接断开后的重连机制,以及数据格式的选择,比如JSON,方便解析和处理。 另外,用户提到的引用[2]中提到了前后端分离,所以需要确保后端提供WebSocket端点,前端通过WebSocket连接来获取数据,而不是传统的HTTP请求。这样能够实现实时更新,无需页面刷新。 可能还需要考虑性能优化,比如数据压缩、心跳机制保持连接活跃,以及如何处理高并发情况下的资源管理。还有安全性,比如使用wss协议,进行身份验证等。 然后,需要将这些步骤结构化,分成后端实现、前端实现、优化策略等部分。每个部分详细说明具体步骤,比如后端如何配置WebSocket,前端如何建立连接和处理数据,以及优化措施。 还要确保示例代码的正确性,比如Spring Boot中使用@ServerEndpoint注解创建WebSocket端点,前端使用JavaScript的WebSocket API。同时,数据格式使用JSON,前后端统一数据结构。 最后,生成相关问题,可能涉及其他实时通信技术对比、身份验证方法、性能优化等,帮助用户进一步深入学习。 总结一下,我需要按照以下结构组织回答: 1. WebSocket的基础原理和优势 2. 后端实现(Spring Boot配置) 3. 前端实现(Vue/React连接处理) 4. 优化策略(性能、安全等) 5. 示例代码 6. 相关问题 确保每个部分都结合用户提供的引用内容,并补充必要的细节,比如具体依赖项、代码示例、优化方法等。同时检查是否符合用户的要求,比如使用中文、LaTeX格式正确、生成相关问题等。&lt;/think&gt;### 如何使用 WebSocket 实现数据更新功能 #### 一、WebSocket 的核心原理与优势 WebSocket 是一种全双工通信协议,允许服务端主动推送数据到客户端,避免了传统 HTTP 轮询的低效问题。其优势包括: 1. **低延迟**:无需频繁建立连接,一次握手后即可保持长连接; 2. **高效传输**:数据帧轻量,适合实时更新场景; 3. **双向通信**:服务端和客户端均可主动发送消息。 #### 二、后端实现(基于 Spring Boot) 1. **添加依赖** 在 `pom.xml` 中引入 Spring WebSocket 支持: ```xml &lt;dependency&gt; &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt; &lt;artifactId&gt;spring-boot-starter-websocket&lt;/artifactId&gt; &lt;/dependency&gt; ``` 2. **配置 WebSocket 端点** 创建 WebSocket 处理器类,定义消息推送逻辑: ```java @Component @ServerEndpoint(&quot;/data-screen&quot;) public class DataScreenWebSocket { private static final Set&lt;Session&gt; sessions = ConcurrentHashMap.newKeySet(); @OnOpen public void onOpen(Session session) { sessions.add(session); } @OnClose public void onClose(Session session) { sessions.remove(session); } // 主动推送数据到所有客户端 public static void broadcast(String message) { sessions.forEach(session -&gt; { try { session.getBasicRemote().sendText(message); } catch (IOException e) { e.printStackTrace(); } }); } } ``` 3. **配置拦截器与启用 WebSocket** 注册端点并设置跨域支持: ```java @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new DataScreenWebSocket(), &quot;/data-screen&quot;) .setAllowedOrigins(&quot;*&quot;); } } ``` #### 三、前端实现(以 Vue.js 为例) 1. **建立 WebSocket 连接** 在 Vue 组件中初始化连接: ```javascript mounted() { this.socket = new WebSocket(&#39;ws://localhost:8080/data-screen&#39;); this.socket.onmessage = (event) =&gt; { this.updateData(JSON.parse(event.data)); // 更新数据 }; } ``` 2. **数据渲染优化** 使用响应式框架(如 ECharts动态更新图表: ```javascript methods: { updateData(newData) { this.chart.setOption({ series: [{ data: newData.values }] }); } } ``` #### 四、优化策略与最佳实践 1. **数据传输优化** - **数据压缩**:对 JSON 数据使用 GZIP 压缩,减少带宽占用; - **增量更新**:仅推送变化部分而非全量数据,例如: ```json { &quot;type&quot;: &quot;delta&quot;, &quot;changes&quot;: [{ &quot;id&quot;: 1, &quot;value&quot;: 42 }] } ``` 2. **连接稳定性** - **心跳机制**:定期发送 `ping/pong` 帧保持连接活跃; - **断线重连**:前端检测断开后自动重连: ```javascript function reconnect() { setTimeout(() =&gt; { this.socket = new WebSocket(this.url); }, 3000); } ``` 3. **安全加固** - **鉴权**:在连接建立时验证 Token,例如通过 URL 参数传递: ``` ws://localhost:8080/data-screen?token=xxxx ``` - **加密传输**:生产环境使用 `wss://` 协议WebSocket over TLS)。 #### 五、完整流程示例 1. 后端通过定时任务或消息队列获取实时数据; 2. 调用 `DataScreenWebSocket.broadcast()` 推送数据; 3. 前端接收数据并触发可视化组件更新。 ```mermaid sequenceDiagram participant 数据源 participant 后端 participant WebSocket participant 前端 数据-&gt;&gt;后端: 推送实时数据 后端-&gt;&gt;WebSocket: 调用 broadcast() WebSocket-&gt;&gt;前端: 发送 JSON 数据 前端-&gt;&gt;ECharts: 更新图表 ``` #### 六、扩展应用场景 此方案可应用于: - 实时股票行情展示 - 物联网设备状态监控 - 双十一电商
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HelloCode5110

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值