ARCHITECTURE.md。
实时系统
AIS 中继背压架构
AIS 船舶追踪中继维护到 AISStream.io 的持久 WebSocket 连接,在高峰海运流量时每秒可投递数百条位置报告。如果没有流控,慢消费者(例如网络状况差的客户端)会导致中继消息队列的无界内存增长。 中继实现了三水位背压系统:
此外,中继将追踪船舶总数上限设为 20,000 个位置(每个 MMSI 的最新位置)。辅助密度单元系统将位置聚合到 2°×2° 地理网格单元(最多 5,000 个单元)中,在完整船舶列表超出渲染能力时提供概览可视化。
船舶历史轨迹上限为每船 30 个位置点。新位置到达时,最旧轨迹点被驱逐。这创建了”彗尾”可视化,展示近期移动方向而无无界内存增长。
中继还在前端和中继服务器之间实现 HMAC 认证,防止未授权客户端消费昂贵的 AIS 数据流。
ONNX Runtime 能力检测
浏览器端 ML 管道(嵌入、NER、情感、摘要)使用 ONNX Runtime Web 进行推理。模型执行速度因浏览器和设备及可用硬件加速而异。 系统在初始化时使用级联能力检测策略:- WebGPU — 通过
navigator.gpu存在性检测。提供 GPU 加速推理,延迟最低。Chrome 113+ 和 Edge 113+ 可用 - WebGL — WebGPU 不可用时的回退。通过 WebGL 计算着色器使用现有 GPU。所有现代浏览器可用
- WASM + SIMD — 仅 CPU 回退。探测
SharedArrayBuffer和 WASM SIMD 可用性。SIMD 对向量操作比普通 WASM 快约 2–4 倍
deviceMemory API 守卫在低内存设备(RAM <4GB 的手机)上完全排除 ML 管道,防止加载 384 维 float32 嵌入模型与地图渲染器和实时视频流并发时的内存溢出崩溃。
地图与可视化
地缘政治边界叠加
地图支持带关联元数据的类型化地缘政治边界多边形。每个边界携带一个boundaryType 判别字段(demilitarized、ceasefire、disputed、armistice),控制渲染样式和弹窗内容。
朝鲜非军事区 — 第一个实现的边界是朝鲜非军事区,定义为源自 OpenStreetMap Way 369265305 和《朝鲜停战协定》第一条分界线的 43 点闭合环多边形。在平面地图上,它以 GeoJsonLayer 渲染,带半透明蓝色填充和带标签的工具提示。在 3D 地球仪上,它作为冲突图层下的 polygonsData 渲染。该边界有专门的帮助条目和图层开关,仅默认在 full 变体上启用。
边界系统设计为可扩展 — 额外的地缘政治边界(克什米尔实际控制线、戈兰高地、北塞浦路斯绿线)可添加到 GEOPOLITICAL_BOUNDARIES 常量并配以适当类型,将自动在两种地图引擎上渲染。
CII 等值线热力图
国家不稳定性指数可在两种地图引擎上投影为全覆盖等值线图层,根据每个国家的实时 CII 评分(0–100)以五段色阶为每个国家多边形着色:
在平面地图(deck.gl)上,
GeoJsonLayer 通过 getLevel() 阈值函数将 ISO 3166-1 alpha-2 国家代码映射为固定 RGBA 值。更新由单调版本计数器(ciiScoresVersion)触发 — 图层在每次渲染传递时比较计数器,仅在其递增时重新计算填充色,避免 O(n) 数据展开。
在 3D 地球仪(globe.gl)上,CII 国家多边形与地缘政治边界合并到同一个 polygonsData 数组中。每个多边形对象中的 _kind 判别字段('boundary' | 'cii')让单个 .polygonCapColor() 回调为两种类型分发渲染逻辑。CII 多边形以 polygonAltitude: 0.002 渲染(低于冲突区轮廓使用的 0.006 高度),防止视觉 Z-fighting。
国家 GeoJSON 从共享的 getCountriesGeoJson() 函数惰性加载,首次抓取后缓存,并在 CII 图层和国家检测射线投射服务间共享。
统一图层开关目录
所有地图图层开关定义 — 图标、本地化键、回退显示标签和支持的渲染器类型 — 合并到单一共享注册表(src/config/map-layer-definitions.ts)。每个条目通过 renderers: MapRenderer[] 字段声明其支持的地图渲染器(例如,dayNight 仅平面,ciiChoropleth 平面和地球仪均支持),防止两个地图组件显示不一致的图层选项。
def() 工厂函数减少了每条目的样板代码。变体特定的图层排序(VARIANT_LAYER_ORDER)为六个仪表盘变体定义显示顺序,而无需重复定义本身。添加新地图图层只需一条注册表条目 — 平面地图和 3D 地球仪均自动从此目录派生其开关面板。
同一注册表还承载了高采用率图层的 v1 图层说明目录。每个精选说明记录用途、提供商/来源、新鲜度预期、置信度说明、限制、相关面板/操作和接地路径;不支持的图层有意回退到通用卡片,使 UI 永不暗示虚构的新鲜度或置信度值。
条件数据加载
API 调用成本高昂。系统仅对已启用图层抓取数据,减少不必要的网络流量和速率限制消耗。图层感知加载
当图层关闭时:- 不对该数据源进行 API 调用
- 不调度刷新间隔
- WebSocket 连接关闭(AIS)
- 立即抓取数据
- 开始刷新间隔
- 切换按钮上显示加载指示器
未配置服务
某些数据源需要 API 密钥(AIS 中继、Cloudflare Radar)。如果未配置凭证:- 图层开关完全隐藏
- 无失败请求污染控制台
- 用户只看到可用的图层
性能优化
仪表盘实时处理数千数据点。多种技术保持 UI 在重数据负载下仍响应。用于分析的 Web Worker
CPU 密集操作在专用 Web Worker 中运行以避免阻塞主线程:
Worker 管理器实现:
- 惰性初始化:首次使用时生成 Worker
- 10 秒就绪超时:Worker 初始化失败则拒绝
- 30 秒请求超时:防止卡住操作挂起
- 自动清理:致命错误时终止 Worker
虚拟滚动
大型列表(100+ 新闻条目)使用虚拟化渲染: 固定高度模式(VirtualList):- 仅渲染视口中可见条目 + 3 条目 overscan 缓冲
- 元素池化 — 复用 DOM 节点而非创建新节点
- 不可见占位器维持滚动位置而无需渲染所有条目
- 基于分块的渲染(每块 10 条目)
- 滚动时渲染分块,带 1 分块缓冲
- CSS containment 用于性能隔离
请求去重
短窗口内的相同请求被去重:- 行情报价将多个标的代码批量到单次 API 调用
- 并发图层切换不会产生重复抓取
Promise.allSettled确保一个失败请求不会阻塞其他
高效数据更新
刷新数据时:- 增量更新:仅变化条目触发重新渲染
- 过期同时重新验证:抓取完成前显示旧数据
- 增量压缩:基线存储 7 天/30 天增量,而非原始历史
跨模块集成
情报模块不孤立运行。数据在系统间流动以实现复合分析。数据流架构
模块依赖
告警传播
当阈值被跨越时:- 来源模块 生成告警(例如 CII 激增)
- 告警合并 与相关告警(相同国家/地区)
- 战略风险 接收复合告警
- UI 更新 标题栏徽章和面板指示器
刷新间隔
不同数据源基于波动性和 API 约束以不同频率更新。轮询计划
实时流
AIS 船舶追踪使用 WebSocket 实现真正实时:- 连接:到 Railway 中继的持久 WebSocket
- 消息:船舶发射时的位置更新
- 重连:自动配合指数退避(5s → 10s → 20s)
用户控制
时间范围选择器影响显示数据,不影响抓取频率:
历史筛选在客户端 — 所有数据均已抓取,仅用于显示筛选。
