前面几篇已经完成了前端3D疫情地球的实现和疫情数据的爬虫,如何将这两个链接起来,需要在这两个程序之间搭建一个服务
服务类型很多,这里使用websocket实现
1 websocket服务端
2 websocket客户端
websocket服务端
这里使用python实现一个简易的websocket服务端,借助python组件websockets即可实现
新建一个server.py,做一个简单连接验证,代码最后设定服务地址为 localhost:9998
import asyncio import websockets import json from mysqlDataAccess import MysqlDataAccess import logging from datetime import datetime import time interval = 60 logging.basicConfig(format='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s', level=logging.INFO) logging.info('websocket服务启动') # 检测客户端权限,用户名密码通过才能退出循环 async def check_permit(websocket): while True: recv_str = await websocket.recv() cred_dict = recv_str.split(":") if cred_dict[0] == "admin" and cred_dict[1] == "123456": logging.info('认证通过') return True else: content = {"code": 0, "msg": "authentication failed"} logging.info('认证失败') await websocket.send(json.dumps(content)) # 接收客户端消息并处理,这里只是简单把客户端发来的返回回去 async def send(websocket): await send_data(websocket) while True: await send_data(websocket) time.sleep(interval) # 服务器端主逻辑 async def main_logic(websocket, path): await check_permit(websocket) # await recv_msg(websocket) await send(websocket) async def send_data(websocket): access = MysqlDataAccess() data = access.getData() logging.info('数据获取完成') await websocket.send(json.dumps(data)) logging.info('信息发送中') start_server = websockets.serve(main_logic, 'localhost', 9998) asyncio.get_event_loop().run_until_complete(start_server) asyncio.get_event_loop().run_forever()
里面调用了一个MysqlDataAccess类,这个用来定时的从mysql数据库获取数据,相关代码如下,实现了MySQL的疫情数据的读写
import pymysql MYSQL_HOST = 'localhost' MYSQL_DB = 'covid19' MYSQL_USER = 'root' MYSQL_PASSWORD = '123456' MYSQL_PORT = 3306 class MysqlDataAccess: def __init__(self,host = MYSQL_HOST,database=MYSQL_DB,user=MYSQL_USER,password=MYSQL_PASSWORD,port=MYSQL_PORT): try: self.db = pymysql.connect(host,user,password,database,charset='utf8',port=port) self.cursor = self.db.cursor(cursor=pymysql.cursors.DictCursor) except pymysql.MySQLError as e : print(e.args) def updateCountryPosition(self,name,lng,lat,type): sql_update = 'insert into dic_lnglat (name,lng,lat,type) values(%s,%s,%s,%s) on duplicate key update' sql_update += ' name= %s,lng=%s,lat=%s,type=%s' try: self.cursor.execute(sql_update,(name,lng,lat,type) * 2) self.db.commit() print(name) except pymysql.MySQLError as e: print(e.args) self.db.rollback() def getData(self): sql_query = 'SELECT a.name,b.lng,b.lat,IFNULL(a.new,0) as new,IFNULL(a.now,0) as now,IFNULL(a.total,0) as total,IFNULL(a.cure,0) as cure,IFNULL(a.death,0) as death from covid19 a inner join dic_lnglat b on a.`name`=b.`name`' try: self.cursor.execute(sql_query) return self.sql_fetch_json() except pymysql.MySQLError as e: print(e.args) def sql_fetch_json(self): keys = [] for column in self.cursor.description: keys.append(column[0]) key_number = len(keys) json_data = [] for row in self.cursor.fetchall(): item = dict() for q in range(key_number): item[keys[q]] = row[keys[q]] json_data.append(item) return json_data
这样一个简单的websocket服务就完成了
为了启动简单,在windows环境下,新建一个socketServer.bat用来启动服务
@echo off F: rem ִ开启socker服务 cd F:mygithubVDDataServerSocketServer python3 server.py TIMEOUT /T 10 exit
执行bat就可以打开服务
websocket客户端
websocket的客户端,VDEarth是基于js的前端可视化应用,这里需要实现一个前端的websocket客户端,这里直接对之前的VDEarth前端组件进行改造
在VDEarth项目文件夹src文件夹下新增一个socket.js, 新增socket访问类
import _ from 'lodash'; function reconnect() { console.log('重新连接'); } function open() { // Web Socket 已连接上,使用 send() 方法发送数据 this.ws.send(this.account + ':' + this.password); } // 接受数据执行回调方法 function receive(evt, callback) { var data = evt.data; if (_.isFunction(callback)) { callback(JSON.parse(data)); } } function close() { // 关闭 websocket console.log('连接已关闭...'); reconnect(); } function reload() { this.ws.send('reload'); } class socket { constructor(server, port, account, password) { this.account = account; this.password = password; this.server = server; this.port = port; } init(onmessage) { if ('WebSocket' in window) { try { this.uri = 'ws://' + this.server + ':' + this.port; this.ws = new WebSocket(this.uri); this.ws.onopen = () => { open.call(this); }; this.ws.onmessage = (evt) => { receive.call(this, evt, onmessage); }; this.ws.onclose = () => { close.call(this); }; window.onbeforeunload = () => { reload(); }; } catch (error) { console.log(error); } } else { // 浏览器不支持 WebSocket console.log('您的浏览器不支持 WebSocket!'); } } } export default socket;
修改VDEarth.js
数据的加载主要是用在了柱形图和名称标记那里,所以修改那里的代码,即initObj方法,VDEarth类的options方法配置默认的socket链接,server = localhost,port = 9998,account = admin,password = 123456
// 创建模型 function initObj() { let self = this; let fontloader = new FontLoader(); // 创建地球模型组 this.baseGroup = new Group(); // 创建地球 this.radius = minSize(this.contentWidth, this.contentHeight) * 0.2; let globalMesh = new model().createGlobe(this.radius, this.textrue); this.baseGroup.add(globalMesh); // 创建星点 let stars = new model().createStars(); this.baseGroup.add(stars); // 创建连接 var mySocket = new socket( this.options.server, this.options.port, this.options.account, this.options.password ); mySocket.init(function (data) { // 创建一个标记组 self.markerGroup = new Group(); // 创建标记 var myMarkers = new marker(); // 柱形 myMarkers.addBoxMarkers(self.markerGroup, self.radius, data); // 加载字体 if (self.font) { myMarkers.addNameMarkers(self.markerGroup, self.radius, data, self.font); } else { fontloader.load('./fonts/SimHei_Regular.json', function (font) { self.font = font; myMarkers.addNameMarkers( self.markerGroup, self.radius, data, self.font ); }); } self.baseGroup.add(self.markerGroup); }); this.scene.add(this.baseGroup); }
至此定时数据推送和VDEarth的展示更新就完成了
打开爬虫服务,打开websokcet服务,打开浏览器就输入VDEarth 的web容器的地址就可以了
相关链接
从0开始疫情3D地球 - 3D疫情地球VDEarth - 1- 引言
从0开始疫情3D地球 - 3D疫情地球VDEarth - 2 - 前端代码构建
从0开始疫情3D地球 - 3D疫情地球VDEarth - 3 - 3D地球组件实现(1)
从0开始疫情3D地球 - 3D疫情地球VDEarth - 4 - 3D地球组件实现(2)