news 2026/3/8 12:18:20

效果炸裂!10分钟把 Elasticsearch 数据变成科幻级 3D 地球,这波操作稳了

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
效果炸裂!10分钟把 Elasticsearch 数据变成科幻级 3D 地球,这波操作稳了

unsetunset一、写在前面:为什么要做这个项目?unsetunset

做后端的朋友都知道,Kibana 很强,但有时候客户要的是更炫酷的演示效果。

比如:

  • 实时网络攻击地图,要能看到飞线在地球上穿梭

  • 全球服务器热力图,要3D效果,要能拖拽旋转

  • 电商实时交易流,要科幻感,要一眼抓住眼球

这些需求,通常让后端工程师头疼:

WebGL、Three.js 这些前端技术,我们不太熟啊!

但有了 Cursor + AI 辅助,这事儿就简单了。今天我就用大白话,手把手带你从零搭建一个

3D 攻防态势大屏,全程不写一行 CSS(Tailwind 搞定),10 分钟让 ES 数据变成会动的 3D 地球。


unsetunset二、技术栈选型:为什么选这些?unsetunset

2.1 前端技术栈

React + TypeScript:类型安全,开发体验好

Vite:启动快,热更新快,适合快速迭代

react-globe.gl:基于 Three.js 封装,代码量少,效果炸裂

Tailwind CSS:不用手写 CSS,样式全靠类名

ECharts:图表库,做 TOP5 排行榜

为什么不用原生 Three.js?因为

react-globe.gl

已经把地球、飞线、标签这些常用功能封装好了,我们只需要传数据,不用关心 WebGL 细节。

2.2 后端技术栈

FastAPI:Python 异步框架,写接口快Elasticsearch 8.x:聚合查询,拿 TOP50 攻击对python-dotenv:环境变量管理

为什么选 FastAPI?因为它自动生成 Swagger 文档,接口测试方便,而且性能好。


unsetunset三、核心实现:三步走策略unsetunset

3.1 第一步:先让地球转起来(视觉底座)

不管数据,先把炫酷的 3D 地球做出来,镇住场子。

3.1.1 安装依赖
cd frontend npm install react-globe.gl three @types/three npm install -D tailwindcss @tailwindcss/vite
3.1.2 创建地球组件

核心代码在src/components/CyberGlobe.tsx

import * as THREE from 'three' export default function CyberGlobe() { return ( <Globe backgroundColor="#000011" // 深邃太空黑 enablePointerInteraction // 允许拖拽 globeMaterial={ new THREE.MeshStandardMaterial({ map: createTechTexture(), // 科技感贴图 emissive: new THREE.Color('#1d4ed8'), emissiveIntensity: 0.18, }) } /> ) }

关键点:

1.背景色#000011是深蓝黑,比纯黑更有层次

2.地球材质:用程序化生成的 Canvas 贴图,不用找图片资源

3.自转:通过controls.autoRotate = true实现

3.1.3 添加大气层辉光

为了让地球更有“科幻感”,我们加一层青色发光边缘

useEffect(() => { const scene = globe.scene() const glowGeom = new THREE.SphereGeometry(101.2, 64, 64) const glowMat = new THREE.MeshBasicMaterial({ color: '#22d3ee', transparent: true, opacity: 0.18, blending: THREE.AdditiveBlending, // 叠加混合 side: THREE.BackSide, // 只渲染背面,形成边缘光 }) scene.add(new THREE.Mesh(glowGeom, glowMat)) }, [])

效果:地球边缘会有一圈淡淡的青色光晕,像科幻电影里的星球。


3.2 第二步:接入攻击数据(飞线动画)

地球有了,接下来让攻击流量在地球上飞起来。

3.2.1 数据结构定义
type AttackArc = { startLat: number // 攻击源纬度 startLng: number // 攻击源经度 endLat: number // 目标纬度 endLng: number // 目标经度 label: string // 攻击类型:DDoS、SQL注入等 color: string // 颜色:红色=严重,黄色=中等 count: number // 攻击次数 sourceCountry: string // 来源国家 }
3.2.2 渲染飞线
<Globe arcsData={attacks} // 攻击数据数组 arcStartLat={(d) => d.startLat} arcStartLng={(d) => d.startLng} arcEndLat={(d) => d.endLat} arcEndLng={(d) => d.endLng} arcColor={(d) => d.color} arcDashLength={ 0.38 } // 虚线长度 arcDashGap={ 1.6 } // 虚线间隔 arcDashAnimateTime={ 2200 } // 动画时长(毫秒) />

效果:每条攻击会显示成一条带动画的虚线,从源点飞向目标,像导弹轨迹。

3.2.3 添加攻击标签
<Globe labelsData={attacks} labelLat={(d) => d.startLat} labelLng={(d) => d.startLng} labelText={(d) => shortLabel(d.label)} // 避免中文变 ???? labelColor={(d) => d.color} />

注意react-globe.gl的标签默认不支持中文(会显示????),所以我们用shortLabel()转成英文短码(如DDoSSQLi)。


3.3 第三步:对接 Elasticsearch(数据聚合)

前端效果有了,现在把真实的 ES 数据接进来。

3.3.1 ES 聚合查询 DSL

核心逻辑在backend/main.pyget_attacks()函数:

body = { "size": 0, # 只要聚合结果,不要原始文档 "query": { "bool": { "filter": [ {"range": {"@timestamp": {"gte": "now-15m", "lte": "now"}}}, {"exists": {"field": "source.geo.location"}}, {"exists": {"field": "dest.geo.location"}}, ] } }, "aggs": { "top_pairs": { "multi_terms": { # 组合键聚合:源IP + 目的IP "terms": [ {"field": "source.ip"}, {"field": "dest.ip"} ], "size": 50, # TOP50 "order": {"_count": "desc"} }, "aggs": { "sample": { "top_hits": { # 从每个 bucket 里取一条样本 "size": 1, "_source": { "includes": [ "source.geo.location", "dest.geo.location", "event.action", "source.geo.country_name" ] } } } } } } }

这个查询的逻辑:

1.时间过滤:只查最近 15 分钟的数据

2.multi_terms 聚合:按“源IP + 目的IP”组合键分组,统计攻击次数

3.top_hits 子聚合:从每个分组里取一条样本,拿到地理坐标和国家名

4.排序:按_count降序,取前 50 个

为什么用 multi_terms?

因为我们要找的是“哪些 IP 对攻击最频繁”,而不是单个 IP。

multi_terms可以同时按两个字段分组,正好满足需求。

3.3.2 数据清洗

ES 返回的是 buckets 结构,我们需要转成前端需要的格式:

for bucket in buckets: source_ip, dest_ip = bucket['key'] count = bucket['doc_count'] # 从 top_hits 里取地理坐标 hit = bucket['sample']['hits']['hits'][0] source_geo = hit['_source']['source']['geo']['location'] dest_geo = hit['_source']['dest']['geo']['location'] out.append({ "startLat": source_geo['lat'], "startLng": source_geo['lon'], "endLat": dest_geo['lat'], "endLng": dest_geo['lon'], "label": hit['_source']['event']['action'], "color": _color_for_label(...), # 根据攻击类型选颜色 "count": count, "sourceIp": source_ip, "destIp": dest_ip, "sourceCountry": hit['_source']['source']['geo']['country_name'] })

关键点:

-geo.location可能是{"lat": 1.0, "lon": 2.0}[lon, lat],需要统一处理

  • 攻击类型映射颜色:DDoS=红色,SQL注入=黄色,XSS=橙色

3.3.3 容错处理

ES 连接失败时,返回 mock 数据,保证演示不中断:

try: resp = es.search(index=ES_INDEX, body=body) except (ESConnectionError, ApiError): if MOCK_ON_ES_DOWN: return _mock_attacks(20) # 返回模拟数据 return []

unsetunset四、UI 完善:HUD 面板 + 核心国家unsetunset

4.1 左右 HUD 面板

4.1.1 左侧:实时攻击列表

样式要点:

  • hud-panel:半透明黑底 + 青色细线边框 + 背景模糊

  • 等宽字体(JetBrains Mono):数字对齐好看

4.1.2 右侧:来源国家 TOP5


用 ECharts 做横向柱状图:

国家名中文化:

i18n-iso-countries自动翻译:

import countries from 'i18n-iso-countries' countries.registerLocale(zh) function normalizeCountryName(name: string) { if (hasCJK(name)) return name // 已经是中文,直接返回 return countries.getName(name, 'zh') || '其他' }

4.2 核心国家标注

在地球上固定标注 8 个核心国家(美国、中国、俄罗斯等),用脉冲光圈显示攻击强度:

const corePoints = [ { code: '中国', name: '中国', lat: 39.9042, lng: 116.4074 }, { code: '美国', name: '美国', lat: 38.9072, lng: -77.0369 }, // ... ] <Globe pointsData={corePoints} // 固定点位 ringsData={coreRings} // 脉冲环 ringMaxRadius={(d) => 2.8 + d.intensity * 5.5} // 强度越大,环越大 ringPropagationSpeed={(d) => 0.9 + d.intensity * 2.0} // 强度越大,速度越快 />

效果:每个核心国家会有一个青色脉冲环,攻击越多,环越大、越快。


unsetunset五、数据造数:一键灌入演示数据unsetunset

5.1 造数脚本

backend/seed_data.py可以快速生成大量测试数据:

python seed_data.py --count 200000 --hot-pairs 200 --minutes 15 --refresh

参数说明:

--count:总文档数(建议 20 万起步)

--hot-pairs:热点 IP 对数量(越小越集中,飞线更“爆炸”)

--minutes:时间窗口(默认 15 分钟,匹配接口查询)

--delete-index:删除旧索引,重新开始(危险操作)

核心逻辑:

1.预生成若干“热点 IP 对”,78% 的数据走这些热点,保证 TOP50 有戏

2.国家名直接用中文(从ZH_COUNTRIES列表选),避免前端翻译问题

3.用helpers.streaming_bulk()批量写入,性能好


unsetunset六、部署与运行unsetunset

6.1 环境准备

后端:

cd backend pip install -r requirements.txt # 配置 .env 文件(ES 地址、账号密码) python -m uvicorn main:app --reload --port 8000

前端:

cd frontend npm install npm run dev # 开发模式 # 或 npm run build && npm run preview # 生产模式

6.2 访问地址

前端大屏http://localhost:5173

后端接口http://127.0.0.1:8000/api/attacks

API 文档http://127.0.0.1:8000/docs


unsetunset七、踩坑总结unsetunset

7.1 中文标签显示

????

问题react-globe.gllabelsData用 Canvas 渲染,默认字体不支持中文。

解决: 用htmlElementsData+ DOM 渲染,或者用shortLabel()转成英文短码。

7.2 国家名中英混杂

问题:ES 返回的国家名可能是英文,前端显示混乱。

解决

1.造数时直接用中文国家名(seed_data.py

2.前端用i18n-iso-countries自动翻译 3.未命中映射的统一显示“其他”,避免混杂

7.3 ES 连接失败导致 500

问题:ES 未启动时,接口返回 500,前端报错。

解决:加 try-catch,ES 失败时返回 mock 数据,保证演示不中断。


unsetunset八、项目结构unsetunset

es3dPrj/ ├── backend/ │ ├── main.py # FastAPI 接口 │ ├── seed_data.py # 数据造数脚本 │ ├── requirements.txt # Python 依赖 │ └── .env # ES 配置 └── frontend/ ├── src/ │ ├── components/ │ │ ├── CyberGlobe.tsx # 3D 地球组件 │ │ ├── HudLeft.tsx # 左侧攻击列表 │ │ └── HudRight.tsx # 右侧 TOP5 图表 │ ├── lib/ │ │ ├── api.ts # 接口调用 │ │ ├── attackTypes.ts # 数据类型定义 │ │ ├── coreCountries.ts # 核心国家配置 │ │ └── countryNormalize.ts # 国家名标准化 │ └── App.tsx # 主入口 └── package.json

unsetunset九、总结unsetunset

这个项目展示了如何用AI 辅助开发,快速实现一个“看起来很难”的 3D 可视化大屏:

1.前端:React + Three.js,10 分钟出效果

2.后端:FastAPI + ES 聚合,数据清洗简单

3.数据:一键造数,演示不愁

核心价值:

-后端工程师也能做出炫酷的前端效果-ES 聚合查询 + 3D 可视化,数据展示更直观-代码结构清晰,易于扩展和维护

unsetunset十、参考资料unsetunset

  • react-globe.gl 官方文档

https://github.com/vasturiano/react-globe.gl

  • FastAPI 官方文档

https://fastapi.tiangolo.com/


Text2DSL——自然语言转 Elasticsearch / Easysearch DSL 神器

基于 Easysearch + Flip 的多模态图像搜索引擎系统实战指南

打造你的企业级智能文档问答系统——Everything plus RAG 实战指南

更短时间更快习得更多干货!

和全球 2100+ Elastic 爱好者一起精进!

AI时代,比同事抢先一步学习进阶干货!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/3/1 10:36:16

Xshell远程部署Qwen3-ASR:Linux服务器配置全指南

Xshell远程部署Qwen3-ASR&#xff1a;Linux服务器配置全指南 1. 为什么选择Xshell来部署语音识别服务 当你第一次打开终端&#xff0c;准备把Qwen3-ASR这个强大的语音识别模型搬到服务器上时&#xff0c;可能会被各种连接工具搞晕。PuTTY、MobaXterm、Termius……但真正用过X…

作者头像 李华
网站建设 2026/2/28 14:26:08

小白必看:阿里小云语音唤醒模型使用常见问题解答

小白必看&#xff1a;阿里小云语音唤醒模型使用常见问题解答 你是不是也遇到过这样的情况&#xff1a;刚部署好“小云”语音唤醒模型&#xff0c;运行 python test.py 后却没反应&#xff1f;或者音频明明说了“小云小云”&#xff0c;结果返回 rejected&#xff1f;又或者换了…

作者头像 李华
网站建设 2026/3/7 9:36:41

漫画脸描述生成部署教程:NVIDIA驱动+Ollama+Qwen3-32B镜像全链路配置

漫画脸描述生成部署教程&#xff1a;NVIDIA驱动OllamaQwen3-32B镜像全链路配置 1. 为什么需要一个专属于二次元的AI角色设计工具&#xff1f; 你有没有过这样的经历&#xff1a;脑子里已经浮现出一个穿着水手服、扎双马尾、眼神狡黠的少女形象&#xff0c;却卡在“怎么把脑海…

作者头像 李华
网站建设 2026/3/7 22:38:29

Qwen3-32B在Clawdbot中的YOLOv5目标检测应用实践

Qwen3-32B在Clawdbot中的YOLOv5目标检测应用实践 1. 多模态目标检测的新思路&#xff1a;当大模型遇见经典CV 最近在调试一个视频分析系统时&#xff0c;发现传统目标检测方案总在几个地方卡壳&#xff1a;检测框画得挺准&#xff0c;但对“这个人在做什么”“为什么这个物体…

作者头像 李华
网站建设 2026/3/4 8:09:36

Atelier of Light and Shadow实现智能技能评估:开发者能力分析系统

Atelier of Light and Shadow实现智能技能评估&#xff1a;开发者能力分析系统 1. 当代码不再只是执行&#xff0c;而是开始“读懂”开发者 你有没有过这样的经历&#xff1a;刚接手一个新项目&#xff0c;面对成千上万行代码&#xff0c;却不知道从哪下手&#xff1f;或者团…

作者头像 李华
网站建设 2026/2/27 11:12:45

MedGemma-X部署指南:3步完成Linux环境下的智能诊断系统搭建

MedGemma-X部署指南&#xff1a;3步完成Linux环境下的智能诊断系统搭建 1. 为什么选择MedGemma-X做医疗影像分析 刚接触医疗AI的朋友可能会问&#xff0c;市面上这么多模型&#xff0c;为什么特别推荐MedGemma-X&#xff1f;它不是那种需要调参、改代码、反复调试的“实验室玩…

作者头像 李华