news 2026/4/23 12:15:16

效果炸裂!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/4/21 19:51:04

EcomGPT-7B实战教程:电商运营如何用AI 10秒完成100条商品标题中译英

EcomGPT-7B实战教程&#xff1a;电商运营如何用AI 10秒完成100条商品标题中译英 1. 这不是普通翻译工具&#xff0c;是专为电商人打磨的“标题加速器” 你有没有遇到过这样的场景&#xff1a; 凌晨两点&#xff0c;运营同事还在Excel里一行行敲英文标题——“加厚纯棉儿童睡衣…

作者头像 李华
网站建设 2026/4/13 22:59:58

Yi-Coder-1.5B与Git集成实战:代码自动补全与版本控制

Yi-Coder-1.5B与Git集成实战&#xff1a;代码自动补全与版本控制 1. 开发团队的日常痛点&#xff1a;为什么需要Git智能助手 每天打开终端&#xff0c;输入git status、git add .、git commit -m "..."这些命令时&#xff0c;你有没有想过——这些重复操作其实可以…

作者头像 李华
网站建设 2026/4/22 7:34:05

LLaVA-1.6保姆级教程:Ollama部署多模态聊天机器人

LLaVA-1.6保姆级教程&#xff1a;Ollama部署多模态聊天机器人 1. 你不需要GPU&#xff0c;也能和图片“对话” 你有没有试过把一张商品截图发给AI&#xff0c;让它告诉你这是什么品牌、多少钱、值不值得买&#xff1f;或者拍张孩子画的涂鸦&#xff0c;问它画的是不是恐龙&am…

作者头像 李华
网站建设 2026/4/23 8:59:32

ERNIE-4.5-0.3B-PT保姆级教程:从部署到问答全流程

ERNIE-4.5-0.3B-PT保姆级教程&#xff1a;从部署到问答全流程 1. 为什么你需要这篇教程 你是不是也遇到过这些情况&#xff1a; 想试试最新的ERNIE模型&#xff0c;但被复杂的环境配置、依赖冲突、GPU显存报错卡在第一步&#xff1f;看到别人演示“一键调用大模型”&#xf…

作者头像 李华
网站建设 2026/4/23 6:35:50

Qwen-Ranker Pro部署教程:安全加固(JWT鉴权+请求限流)

Qwen-Ranker Pro部署教程&#xff1a;安全加固&#xff08;JWT鉴权请求限流&#xff09; 1. 为什么需要给精排服务加把锁&#xff1f; 你可能已经用过Qwen-Ranker Pro&#xff0c;也体验过它在语义重排序上的强大能力——输入一个查询和几十个候选文档&#xff0c;几秒内就能…

作者头像 李华