Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
12d21b0d23 | ||
|
|
a83eb04094 | ||
|
|
a1a982fb06 | ||
|
|
74809305ae | ||
|
|
fe1dccde17 | ||
|
|
3715bafcb4 | ||
|
|
df71e4b63e | ||
|
|
50dd513b78 | ||
|
|
a471814a22 | ||
|
|
e172802a21 | ||
|
|
f45bfc9f84 | ||
|
|
32171d11f3 |
1
.gitignore
vendored
@ -7,3 +7,4 @@ build
|
|||||||
/api/db.sqlite3
|
/api/db.sqlite3
|
||||||
/emblemscanner-release
|
/emblemscanner-release
|
||||||
models
|
models
|
||||||
|
emblem5/ai/data/
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
stages:
|
stages:
|
||||||
|
- ailab
|
||||||
- test-and-build
|
- test-and-build
|
||||||
- build-docker
|
- build-docker
|
||||||
- deploy
|
- deploy
|
||||||
- ailab
|
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
GIT_DEPTH: 1
|
GIT_DEPTH: 1
|
||||||
@ -86,7 +86,7 @@ ailab:
|
|||||||
when: manual
|
when: manual
|
||||||
tags:
|
tags:
|
||||||
- ailab
|
- ailab
|
||||||
timeout: 36000 seconds
|
timeout: 100 hours
|
||||||
before_script:
|
before_script:
|
||||||
- test -d $HOME/venv || python3 -m venv $HOME/venv
|
- test -d $HOME/venv || python3 -m venv $HOME/venv
|
||||||
- source $HOME/venv/bin/activate
|
- source $HOME/venv/bin/activate
|
||||||
|
|||||||
141
README.md
@ -1 +1,140 @@
|
|||||||
# themblem.com project
|
# 徵象 Themblem
|
||||||
|
|
||||||
|
**AI 驱动的智能防伪溯源平台**
|
||||||
|
|
||||||
|
徵象基于"无复制性原理",利用二维码在印刷过程中形成的微观差异作为不可复制的"图像指纹",结合深度学习模型实现高精度真伪鉴别。消费者仅需通过微信小程序扫码,即可在 1-2 秒内完成防伪验证。
|
||||||
|
|
||||||
|
## 架构总览
|
||||||
|
|
||||||
|
```
|
||||||
|
┌──────────────────────┐ ┌────────────────────┐
|
||||||
|
│ 微信小程序 (scanner) │ │ 管理后台 (web) │
|
||||||
|
│ · 扫码采集 │ │ · Vue 3 + CoreUI │
|
||||||
|
│ · WASM QR 检测 │ │ · 产品/批次管理 │
|
||||||
|
│ · 实时验证 │ │ · 扫描数据分析 │
|
||||||
|
└──────────┬───────────┘ └──────────┬─────────┘
|
||||||
|
│ │
|
||||||
|
▼ ▼
|
||||||
|
┌──────────────────────────────────────────────────┐
|
||||||
|
│ Django API (api) │
|
||||||
|
│ · REST API (Tastypie) · 多租户隔离 │
|
||||||
|
│ · AI 对话 (DeepSeek) · 阿里云 OSS 存储 │
|
||||||
|
│ · 设备摄像头适配 · PostgreSQL │
|
||||||
|
└──────────────────────┬───────────────────────────┘
|
||||||
|
│
|
||||||
|
┌───────────────┼───────────────┐
|
||||||
|
▼ ▼ ▼
|
||||||
|
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||||
|
│ alg (C++) │ │ emblem5 (AI)│ │ Kubernetes │
|
||||||
|
│ · libqr │ │ · ResNet18 │ │ · K3s │
|
||||||
|
│ · WASM 编译 │ │ · 训练/推理 │ │ · Traefik │
|
||||||
|
│ · 特征提取 │ │ · wandb │ │ · Nginx │
|
||||||
|
└─────────────┘ └─────────────┘ └─────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
├── alg/ QR 码检测与特征提取 (C++/WASM)
|
||||||
|
├── api/ Django REST API 后端
|
||||||
|
├── web/ Vue 3 管理后台
|
||||||
|
├── scanner/ 微信小程序
|
||||||
|
├── emblem5/ AI 训练与推理流水线 (PyTorch)
|
||||||
|
├── deploy/ Kubernetes 部署配置
|
||||||
|
├── scripts/ 部署脚本与 CLI 工具
|
||||||
|
├── dataset/ 训练数据集样本
|
||||||
|
├── doc/ 产品与技术文档
|
||||||
|
├── Makefile 构建、训练、部署一站式入口
|
||||||
|
└── .gitlab-ci.yml CI/CD 流水线
|
||||||
|
```
|
||||||
|
|
||||||
|
## 核心模块
|
||||||
|
|
||||||
|
### QR 检测引擎 (`alg/`)
|
||||||
|
|
||||||
|
基于 OpenCV 构建的 QR 码检测与特征提取库,核心用 C++ 编写,编译产出:
|
||||||
|
|
||||||
|
- **qrtool** — CLI 工具,用于离线处理
|
||||||
|
- **qrtool.web.wasm** — 浏览器端 WebAssembly
|
||||||
|
- **qrtool.wx.wasm** — 微信小程序 WebAssembly
|
||||||
|
|
||||||
|
关键算法包括 QR 码定位、透视矫正、微观特征提取和相似度比对(CellWeight / FuzzyPixelCmp)。
|
||||||
|
|
||||||
|
### AI 训练流水线 (`emblem5/`)
|
||||||
|
|
||||||
|
基于 PyTorch ResNet18 的迁移学习方案,完整覆盖数据采集到模型部署全流程:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make fetch # 从 API 拉取扫描数据
|
||||||
|
make sbs # 生成 side-by-side 训练图对
|
||||||
|
make train # 训练模型 (30 epochs)
|
||||||
|
make check-accuracy MODEL=... # 评估模型准确率
|
||||||
|
```
|
||||||
|
|
||||||
|
训练方法:将扫描帧与标准图像分别切割为 3×3 网格,逐块对比学习微观特征差异,最终通过投票机制判定真伪。当前最佳模型准确率达 **98.94%** (正样本) / **96.13%** (负样本)。
|
||||||
|
|
||||||
|
### API 后端 (`api/`)
|
||||||
|
|
||||||
|
Django + Tastypie REST API,核心功能:
|
||||||
|
|
||||||
|
- 多租户隔离与权限管理
|
||||||
|
- 二维码批次生成与生命周期管理
|
||||||
|
- 扫描数据采集与验证结果分析
|
||||||
|
- AI 客服对话 (DeepSeek + RAG)
|
||||||
|
- 设备摄像头参数动态适配 (覆盖 1500+ 机型)
|
||||||
|
|
||||||
|
### 管理后台 (`web/`)
|
||||||
|
|
||||||
|
Vue 3 + CoreUI 构建的 SPA 管理面板,提供产品管理、批次操作、扫描数据分析、AI 对话配置、模型管理等功能。
|
||||||
|
|
||||||
|
### 微信小程序 (`scanner/`)
|
||||||
|
|
||||||
|
消费者端扫码验证入口。内置 WebAssembly QR 检测引擎,通过 Worker 线程实现非阻塞实时检测,支持自动亮度感应、设备特定参数适配等优化。
|
||||||
|
|
||||||
|
## 快速开始
|
||||||
|
|
||||||
|
### 环境要求
|
||||||
|
|
||||||
|
- Python 3.10+, Node.js 16+
|
||||||
|
- PostgreSQL
|
||||||
|
- Docker (构建镜像)
|
||||||
|
|
||||||
|
### 本地开发
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# API 后端
|
||||||
|
cd api && pip install -r ../requirements.txt
|
||||||
|
./manage.py migrate && ./manage.py runserver
|
||||||
|
|
||||||
|
# Web 前端
|
||||||
|
cd web && npm install && npm run serve
|
||||||
|
|
||||||
|
# AI 训练 (需 GPU)
|
||||||
|
make fetch && make sbs && make train
|
||||||
|
```
|
||||||
|
|
||||||
|
### 构建与部署
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make web # 构建前端
|
||||||
|
make docker-build # 构建 Docker 镜像
|
||||||
|
make docker-push # 推送至镜像仓库
|
||||||
|
make deploy-api-dev # 部署到开发环境
|
||||||
|
make deploy-api-prod # 部署到生产环境
|
||||||
|
```
|
||||||
|
|
||||||
|
## 技术栈
|
||||||
|
|
||||||
|
| 层级 | 技术 |
|
||||||
|
|------|------|
|
||||||
|
| 前端 | Vue 3, CoreUI, Chart.js |
|
||||||
|
| 后端 | Django, Tastypie, Gunicorn, Nginx |
|
||||||
|
| AI/ML | PyTorch, ResNet18, Kornia, wandb |
|
||||||
|
| 计算机视觉 | OpenCV, C++, WebAssembly (Emscripten) |
|
||||||
|
| 存储 | PostgreSQL, 阿里云 OSS |
|
||||||
|
| 部署 | Docker, Kubernetes (K3s), Traefik |
|
||||||
|
| 小程序 | 微信原生 + WASM Worker |
|
||||||
|
|
||||||
|
## 许可证
|
||||||
|
|
||||||
|
Proprietary — 广州市诚投科技有限公司 © 2024
|
||||||
|
|||||||
@ -95,12 +95,10 @@ bool detect_qr(ProcessState &ps, float margin_ratio, bool warp, string &err)
|
|||||||
}
|
}
|
||||||
int qr_width = max_x - min_x;
|
int qr_width = max_x - min_x;
|
||||||
int qr_height = max_y - min_y;
|
int qr_height = max_y - min_y;
|
||||||
float min_ratio = 0.3;
|
int min_px = 200;
|
||||||
int min_width = std::max(180, (int)(ps.orig->cols * min_ratio));
|
if (qr_width < min_px && qr_height < min_px) {
|
||||||
int min_height = std::max(180, (int)(ps.orig->rows * min_ratio));
|
err = string_format("qr too small: %d x %d, min: %d",
|
||||||
if (qr_width < min_width && qr_height < min_height) {
|
qr_width, qr_height, min_px);
|
||||||
err = string_format("qr too small: %d x %d, thres: %d x %d",
|
|
||||||
qr_width, qr_height, min_width, min_height);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
479
doc/11b.md
Normal file
@ -0,0 +1,479 @@
|
|||||||
|
Project 11b - Hardware Specification v0.1
|
||||||
|
|
||||||
|
## 1. Requirements
|
||||||
|
|
||||||
|
Annual amount of data: 11 billion QR codes, each 50kb - 200kb.
|
||||||
|
- **Total storage:** 550 TB - 2.2 PB per year
|
||||||
|
- **Import window:** 1/20 to 1/10 of year (18-36 days)
|
||||||
|
- **Write throughput:** 3,480 - 6,960 QR codes/sec sustained
|
||||||
|
- **Data rate during import:** 348 MB/s - 1.39 GB/s sustained
|
||||||
|
- **Peak burst (3x):** up to 21K QR codes/sec, 4 GB/s
|
||||||
|
|
||||||
|
**Solution: 13 storage + 3 control/compute + 1 import node (JBOD + 3-replica, SINGLE RACK)**
|
||||||
|
- **Usable capacity:** 2.29 PB (covers ALL 11B QR codes at maximum 200KB size)
|
||||||
|
- **Raw capacity:** 6.86 PB (24 × 22TB × 13 storage nodes)
|
||||||
|
- **Aggregate IOPS:** ~1.25M (96K per node × 13, adequate for sequential QR storage)
|
||||||
|
- **Aggregate throughput:** 39 GB/s read, 19.5 GB/s write (exceeds 1.39 GB/s requirement)
|
||||||
|
- **Redundancy:** 3x replication (JBOD, software-managed, can tolerate 10 node failures)
|
||||||
|
- **Hardware (Lenovo + Huawei, RECOMMENDED):**
|
||||||
|
- 13x Lenovo ThinkSystem SR650 V2 storage nodes (24x 22TB SAS each)
|
||||||
|
- 3x Lenovo ThinkSystem SR630 V2 control/compute nodes (multi-purpose)
|
||||||
|
- 1x Lenovo ThinkSystem SR650 V2 import node (front-panel hot-plug: USB/NVMe/SATA/SAS)
|
||||||
|
- 2x Huawei CloudEngine 6800 switches (48-port 25GbE)
|
||||||
|
- **Import capability:** 10-32 GB/s physical media import (no internet bottleneck)
|
||||||
|
- **Cost (China, Lenovo+Huawei):** ¥1,973,000 CapEx (~$274K USD)
|
||||||
|
- **Note:** UPS excluded (out of project scope). Pricing based on Lenovo China and Huawei public pricing.
|
||||||
|
- **Fits in:** Single 42U rack (35U used, 7U for expansion)
|
||||||
|
|
||||||
|
## 2. Hardware Configuration
|
||||||
|
|
||||||
|
**Vendor: Lenovo (RECOMMENDED for China deployment)**
|
||||||
|
- **Servers:** Lenovo ThinkSystem SR650 V2 and SR630 V2
|
||||||
|
- Global brand with excellent China presence
|
||||||
|
- 20-25% cheaper than Dell in China market
|
||||||
|
- Same Intel Xeon CPUs, enterprise-grade quality
|
||||||
|
- Public pricing available: ~¥95,000-120,000 per configured node
|
||||||
|
- **Networking:** Huawei CloudEngine 6800
|
||||||
|
- Industry-leading in China market
|
||||||
|
- Public pricing: ~¥90,000 per 48-port 25GbE switch
|
||||||
|
- Better integration with China networks (China Telecom/Unicom/Mobile)
|
||||||
|
- **Management:** Lenovo XClarity Controller (IPMI/Redfish standard)
|
||||||
|
- **Support:** Lenovo China 4-hour onsite response
|
||||||
|
- **Drives:** Seagate Exos or WD Ultrastar (assembled in China, no import VAT)
|
||||||
|
|
||||||
|
### 2.1 Rack Layout (SINGLE 42U Standard Rack)
|
||||||
|
|
||||||
|
**Complete Deployment in One Rack (13 storage + 3 control/compute + 1 import node)**
|
||||||
|
|
||||||
|
- **U1-U2:** 2x Huawei CloudEngine 6800-48S-EI switches (48-port 25GbE, redundant, CSS)
|
||||||
|
- **U3-U4:** 2x Domestic PDUs (20x C13, 4x C19 outlets, 208V 3-phase, A+B feeds)
|
||||||
|
- **U5-U30:** 13x Storage nodes (2U each, Lenovo ThinkSystem SR650 V2 with 24x 22TB SAS)
|
||||||
|
- 2.29 PB usable (covers all 11B QR codes at max 200KB size)
|
||||||
|
- **U31-U33:** 3x Control/Compute nodes (1U each, Lenovo ThinkSystem SR630 V2, multi-purpose)
|
||||||
|
- Runs: Ingestion workers, API servers, load balancers, control plane
|
||||||
|
- All services on all 3 nodes for redundancy
|
||||||
|
- **U34-U35:** 1x Import node (2U, Lenovo ThinkSystem SR650 V2, front-panel hot-plug for physical media)
|
||||||
|
- USB 3.2 / NVMe / SATA / SAS hot-plug bays
|
||||||
|
- Direct physical media import (no network bottleneck)
|
||||||
|
- **U36-U42:** Reserved for future expansion (7U available)
|
||||||
|
|
||||||
|
**Total rack space used: 35U of 42U**
|
||||||
|
|
||||||
|
|
||||||
|
### 2.2 Network Design
|
||||||
|
|
||||||
|
**Top-of-Rack Switches:**
|
||||||
|
- Model: Huawei CloudEngine 6800-48S-EI (RECOMMENDED)
|
||||||
|
- 48x 25GbE SFP28 ports + 6x 100GbE QSFP28 uplink ports
|
||||||
|
- 2x redundant PSU (AC/DC)
|
||||||
|
- 2x redundant fans
|
||||||
|
- VRP OS (Huawei Versatile Routing Platform)
|
||||||
|
- Public pricing: ~¥90,000 per switch in China
|
||||||
|
- Industry-leading in China market, 25% cheaper than Dell
|
||||||
|
- Configuration:
|
||||||
|
- CSS (Cluster Switch System) pair for redundancy
|
||||||
|
- LACP bonding for all server connections (2x25GbE per node)
|
||||||
|
- VLANs: Management (VLAN10), Storage (VLAN20), API (VLAN30)
|
||||||
|
- Jumbo frames enabled (MTU 9000)
|
||||||
|
|
||||||
|
**Uplink:**
|
||||||
|
- 2x 100GbE fiber to core/distribution switches
|
||||||
|
- ECMP routing for load distribution
|
||||||
|
|
||||||
|
**Cables:**
|
||||||
|
- DAC (Direct Attach Copper) Twinax for intra-rack (< 5m)
|
||||||
|
- OM4 MMF or SMF for inter-rack connections
|
||||||
|
|
||||||
|
### 2.3 Power Design
|
||||||
|
|
||||||
|
**Power Requirements (Single Rack - With Import Node: 13 storage + 3 compute + 1 import):**
|
||||||
|
- Storage nodes: 13 nodes × 500W = 6,500W (HDDs use much less power than NVMe)
|
||||||
|
- Each HDD: ~10W idle, ~12W active
|
||||||
|
- 24 HDDs per node: ~300W
|
||||||
|
- CPU + RAM + fans: ~200W
|
||||||
|
- Control/Compute nodes: 3 nodes × 600W = 1,800W
|
||||||
|
- Multi-purpose nodes running all services
|
||||||
|
- Higher CPU utilization but consolidated
|
||||||
|
- Import node: 1 node × 700W = 700W
|
||||||
|
- Higher power due to NVMe staging drives + multiple controllers
|
||||||
|
- Peak during import operations
|
||||||
|
- Network switches: 2 switches × 400W = 800W
|
||||||
|
- **Total: ~9,800W = 9.8 kW**
|
||||||
|
- **With 30% headroom: 12.7 kW single rack**
|
||||||
|
|
||||||
|
**Power Summary:**
|
||||||
|
- Storage nodes: 6.5 kW (13 nodes × 500W)
|
||||||
|
- Control/Compute nodes: 1.8 kW (3 nodes × 600W)
|
||||||
|
- Import node: 0.7 kW (1 node × 700W)
|
||||||
|
- Network switches: 0.8 kW (2 switches × 400W)
|
||||||
|
- **Total: 9.8 kW (12.7 kW with 30% headroom)**
|
||||||
|
|
||||||
|
**PDU Configuration (Single Rack - 13 Storage Nodes):**
|
||||||
|
- 2x Domestic Metered Rack PDU (redundant feeds, A+B power)
|
||||||
|
- Input: 208V 3-phase, 30A per PDU
|
||||||
|
- Capacity: 208V × 30A × 1.732 = 10.8 kVA per PDU (3-phase)
|
||||||
|
- Total: 21.6 kVA for rack (sufficient for 12.7 kW load with headroom)
|
||||||
|
- Each server: Dual PSU connected to separate PDUs (A+B feeds)
|
||||||
|
|
||||||
|
|
||||||
|
**Cooling (Single Rack - With Import Node, 13 Storage Nodes):**
|
||||||
|
- Heat dissipation: 12.7 kW × 3.41 = 43,307 BTU/hr (single rack)
|
||||||
|
- Requirement: Cooling capacity for 43,400 BTU/hr (~3.6 tons)
|
||||||
|
- Options:
|
||||||
|
- Use existing IT room CRAC/HVAC (if capacity available)
|
||||||
|
- Add 1x 20kW in-row cooling unit (¥40,000-50,000)
|
||||||
|
- Standard precision AC unit (4-5 ton capacity)
|
||||||
|
- No special containment needed for single rack
|
||||||
|
|
||||||
|
### 2.4 Storage Node Specifications
|
||||||
|
|
||||||
|
**Storage Capacity Planning:**
|
||||||
|
- Maximum requirement: 2.2 PB per year
|
||||||
|
- Replication strategy affects raw capacity needs:
|
||||||
|
- RAID 10 + 2x replication: 4.4 PB raw (complex, dual-layer redundancy)
|
||||||
|
- JBOD + 3x replication: 6.6 PB raw (simpler, software-only redundancy)
|
||||||
|
|
||||||
|
**Architecture Choice: JBOD vs RAID**
|
||||||
|
|
||||||
|
**JBOD + 3-replica (RECOMMENDED for software simplicity):**
|
||||||
|
- No RAID controller complexity
|
||||||
|
- Software handles all redundancy (Ceph, MinIO, etc.)
|
||||||
|
- Each drive independent, easier failure recovery
|
||||||
|
- Better performance observability
|
||||||
|
- Standard approach for distributed storage
|
||||||
|
- Easier rebalancing and maintenance
|
||||||
|
|
||||||
|
**RAID 10 + 2-replica (traditional approach):**
|
||||||
|
- Hardware RAID provides local redundancy
|
||||||
|
- Lower network replication traffic
|
||||||
|
- Faster local reads (RAID striping)
|
||||||
|
- More complex failure scenarios (RAID + cluster)
|
||||||
|
- RAID rebuild overhead on drive failure
|
||||||
|
|
||||||
|
**Storage Node: 13 nodes with 22TB SAS HDD + JBOD + 3-replica (covers all 11B QR codes)**
|
||||||
|
|
||||||
|
**Model: Lenovo ThinkSystem SR650 V2 (2U, 24x 2.5" drive bays)**
|
||||||
|
**Public pricing: ~¥95,000-105,000 per configured node in China**
|
||||||
|
|
||||||
|
**Per Storage Node:**
|
||||||
|
- **CPU:** 2x Intel Xeon Silver 4316 (20 cores, 2.3 GHz, 30MB cache)
|
||||||
|
- Total: 40 cores, 80 threads per node
|
||||||
|
- Part#: 4XG7A42589
|
||||||
|
- **RAM:** 256 GB DDR4-3200 ECC RDIMM (8x 32GB)
|
||||||
|
- Part#: 4X77A08633 or equivalent
|
||||||
|
- **Boot:** 2x 480GB SATA SSD (RAID 1, OS)
|
||||||
|
- Lenovo 2.5" 6Gb SATA SSD
|
||||||
|
- **Storage:** 24x 22TB SAS HDD 12Gbps 7.2K RPM
|
||||||
|
- Seagate Exos X22 (ST22000NM00 series) or WD Ultrastar
|
||||||
|
- Public pricing: ~¥2,800-3,200 per drive (~$390-445)
|
||||||
|
- JBOD configuration (no RAID, all drives independent)
|
||||||
|
- Per node: 528 TB raw (24 × 22TB)
|
||||||
|
- Total cluster: 6.86 PB raw (13 nodes × 528 TB)
|
||||||
|
- With 3x replication: 2.29 PB usable
|
||||||
|
- Performance: 4K IOPS per drive, ~96K IOPS per node, 1.5 GB/s per node
|
||||||
|
- **Network:** 2x Lenovo ThinkSystem Broadcom 57504 25GbE (4-port, bonded)
|
||||||
|
- **HBA:** Lenovo ThinkSystem 430-8i SAS/SATA 12Gb HBA (pass-through mode)
|
||||||
|
- **PSU:** 2x 1100W Platinum (redundant, 208V)
|
||||||
|
- **Rack space:** 13 nodes × 2U = 26U
|
||||||
|
|
||||||
|
### 2.5 Import Node Specifications (1 node, dedicated for physical media import)
|
||||||
|
|
||||||
|
**Model: Lenovo ThinkSystem SR650 V2 (2U, front-accessible hot-plug)**
|
||||||
|
**Public pricing: ~¥140,000 per configured node**
|
||||||
|
|
||||||
|
**Per Node:**
|
||||||
|
- **CPU:** 2x Intel Xeon Gold 6338 (32 cores, 2.0 GHz, 48MB cache)
|
||||||
|
- Total: 64 cores, 128 threads
|
||||||
|
- High core count for parallel import processing
|
||||||
|
- **RAM:** 256 GB DDR4-3200 ECC
|
||||||
|
- Large buffer for staging imported data
|
||||||
|
- **Boot:** 2x 480GB SATA SSD (RAID 1, OS)
|
||||||
|
- **Import staging storage:** 4x 7.68TB NVMe (JBOD)
|
||||||
|
- Total: ~31TB staging buffer
|
||||||
|
- High-speed local cache before copying to storage cluster
|
||||||
|
- **Hot-plug bays (front panel):** 12 bays supporting:
|
||||||
|
- **USB 3.2 Gen 2x2:** 4x front-panel USB-C ports (2.5 GB/s each)
|
||||||
|
- **NVMe U.2:** 4x hot-swap NVMe bays (up to 8 GB/s per drive)
|
||||||
|
- **SATA/SAS:** 4x hot-swap 3.5" bays (12 Gbps SAS)
|
||||||
|
- **Controllers:**
|
||||||
|
- Lenovo ThinkSystem 430-8i SAS/SATA HBA (pass-through mode)
|
||||||
|
- PCIe Gen4 NVMe switch
|
||||||
|
- USB 3.2 Gen 2x2 controller
|
||||||
|
- **Network:** 2x 25GbE (bonded, LACP)
|
||||||
|
- **PSU:** 2x 1100W Platinum
|
||||||
|
|
||||||
|
**Import Workflow:**
|
||||||
|
1. Operator inserts physical media (USB, NVMe, SATA, SAS) into front panel
|
||||||
|
2. Drive auto-mounts or hot-detects
|
||||||
|
3. Import software reads data from media → staging NVMe (fast local)
|
||||||
|
4. Processes/validates QR codes in parallel (32-48 cores)
|
||||||
|
5. Streams validated data to storage cluster over 25GbE (or 100GbE)
|
||||||
|
6. Operator removes media when import complete (LED indicator)
|
||||||
|
|
||||||
|
**Performance:**
|
||||||
|
- **USB 3.2:** 4 ports × 2.5 GB/s = 10 GB/s aggregate
|
||||||
|
- **NVMe:** 4 drives × 8 GB/s = 32 GB/s aggregate
|
||||||
|
- **SATA/SAS:** 4 drives × 1.5 GB/s = 6 GB/s aggregate
|
||||||
|
- **Network:** 2x 25GbE = 6.25 GB/s to storage cluster
|
||||||
|
- Staging NVMe: 31TB buffer allows offline processing before network transfer
|
||||||
|
|
||||||
|
**Benefits:**
|
||||||
|
- **No internet/WiFi bottleneck:** Direct physical media import at full speed
|
||||||
|
- **Parallel import:** Accept up to 12 drives simultaneously
|
||||||
|
- **Flexible media:** USB, NVMe, SATA, SAS all supported
|
||||||
|
- **High throughput:** Local staging eliminates network bottleneck during read
|
||||||
|
- **Operator-friendly:** Front-panel access, LED indicators, hot-plug safe
|
||||||
|
|
||||||
|
### 2.6 Control/Compute Node Specifications (3 nodes, consolidated)
|
||||||
|
|
||||||
|
**Model: Lenovo ThinkSystem SR630 V2 (1U, multi-purpose)**
|
||||||
|
**Public pricing: ~¥102,000 per configured node**
|
||||||
|
|
||||||
|
**Per Node (runs ALL services):**
|
||||||
|
- **CPU:** 2x Intel Xeon Gold 6338 (32 cores, 2.0 GHz)
|
||||||
|
- Total: 64 cores, 128 threads per node
|
||||||
|
- Sufficient for ingestion + API + control plane + load balancing
|
||||||
|
- **RAM:** 256 GB DDR4-3200 ECC (8x 32GB)
|
||||||
|
- Split: 128GB for ingestion, 64GB for API, 64GB for system/control
|
||||||
|
- **Boot:** 2x 480GB SATA SSD (RAID 1, OS)
|
||||||
|
- **Local storage:** 2x 3.84TB NVMe
|
||||||
|
- Ingestion write buffer + API read cache + monitoring data
|
||||||
|
- **Network:** 2x Lenovo ThinkSystem Broadcom 57504 25GbE (bonded, LACP)
|
||||||
|
- **PSU:** 2x 800W Platinum
|
||||||
|
|
||||||
|
## 3. Network Topology
|
||||||
|
```
|
||||||
|
Core Switch (100GbE uplink)
|
||||||
|
↓
|
||||||
|
Rack ToR Switches (Dell VLT pair, 25GbE to servers)
|
||||||
|
↓
|
||||||
|
Dell Servers (dual-homed, LACP bonded)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Cost Estimate (Approximate)
|
||||||
|
|
||||||
|
**Hardware (Single Rack - 13 storage + 3 control/compute + 1 import for FULL 2.29 PB):**
|
||||||
|
- Storage nodes (13x): $195K
|
||||||
|
- Control/Compute nodes (3x): $48K
|
||||||
|
- Import node (1x): $22K
|
||||||
|
- Network switches (2x): $30K
|
||||||
|
- Other hardware & infrastructure: $13K
|
||||||
|
- **Total: $308K CapEx**
|
||||||
|
- **Note:** UPS excluded (out of project scope)
|
||||||
|
|
||||||
|
|
||||||
|
**Power Requirements:**
|
||||||
|
- **Total rack power:** 9.5kW maximum
|
||||||
|
- 13x storage nodes @ 400W each = 5.2kW
|
||||||
|
- 3x control/compute nodes @ 300W each = 0.9kW
|
||||||
|
- 1x import node @ 500W = 0.5kW
|
||||||
|
- 2x switches @ 400W each = 0.8kW
|
||||||
|
- Margin: ~1.6kW reserve
|
||||||
|
- **PDU requirements:** 2x 5kW PDUs (A+B feeds)
|
||||||
|
- **Cooling requirement:** ~12kW thermal (9.5kW × 1.3 PUE)
|
||||||
|
|
||||||
|
## 5. Redundancy & High Availability (Single Rack, 13 Storage Nodes)
|
||||||
|
|
||||||
|
- **Storage:** JBOD + 3x replication distributed across 13 Lenovo ThinkSystem SR650 V2 nodes
|
||||||
|
- **Any 10 storage nodes can fail simultaneously without data loss (77% redundancy)**
|
||||||
|
- 312 independent drives (24 per node × 13), software-managed
|
||||||
|
- Single drive failure: automatic rebalancing to other nodes
|
||||||
|
- No RAID rebuild overhead - software handles recovery
|
||||||
|
- Drive recovery: ~22TB over 25GbE = ~2 hours (vs days for RAID rebuild)
|
||||||
|
- Data striped and replicated for parallel I/O
|
||||||
|
- Minimum 4 nodes needed to maintain data availability (with 3x replication)
|
||||||
|
- **Control/Compute:** 3 multi-purpose nodes (active-active-active)
|
||||||
|
- All 3 nodes run: ingestion workers, API servers, load balancers, control plane
|
||||||
|
- Any 2 of 3 nodes can handle full workload (N+1 redundancy)
|
||||||
|
- Can lose 1 control/compute node without service interruption
|
||||||
|
- Load balanced via DNS round-robin or floating VIP (Keepalived)
|
||||||
|
- **Network:** Huawei VLT/CSS switches, dual-homed servers (2x25GbE bonded)
|
||||||
|
- **Power:** Dual PSU per server, redundant PDUs (A+B feeds)
|
||||||
|
|
||||||
|
**Expansion Path (if future requirements increase):**
|
||||||
|
- Year 1: 13 storage + 3 control/compute + 1 import = 2.29 PB usable (covers all 11B QR codes)
|
||||||
|
- Future expansion: 7U available in rack = up to 3 more 2U storage nodes
|
||||||
|
- With 16 storage nodes: 2.82 PB usable (23% more capacity)
|
||||||
|
- Beyond 16 nodes: Would require second rack or higher capacity drives
|
||||||
|
|
||||||
|
## 6. Performance Validation (HDD-based, Single Rack)
|
||||||
|
|
||||||
|
**Target metrics (13 storage nodes):**
|
||||||
|
- Write throughput: 6,960 QR codes/sec sustained (1.39 GB/s)
|
||||||
|
- With 3x replication: 4.17 GB/s network write traffic
|
||||||
|
- HDD capability: 19.5 GB/s aggregate write (13 nodes × 1.5 GB/s) ✓ **EXCELLENT**
|
||||||
|
- Peak burst: 21,000 QR codes/sec (4.2 GB/s)
|
||||||
|
- With 3x replication: 12.6 GB/s network write traffic
|
||||||
|
- HDD capability: 19.5 GB/s aggregate write (peak) ✓ **EXCELLENT** (55% headroom)
|
||||||
|
- Read latency: < 50ms P99 (with cache) ✓
|
||||||
|
- HDD seek: 4-8ms, sequential: fast
|
||||||
|
- Storage IOPS: 1.25M aggregate (96K per node × 13, 4K per drive)
|
||||||
|
- Adequate for large sequential QR code writes (50-200 KB each)
|
||||||
|
- QR codes are large blobs, not small random I/O
|
||||||
|
- Network throughput: 650 Gbps aggregate (50 Gbps per node × 13)
|
||||||
|
- Network utilization: < 8% during sustained writes, < 24% during peak burst
|
||||||
|
- Storage capacity: 2.29 PB usable (6.86 PB raw with 3x replication)
|
||||||
|
- **Covers ALL 11 billion QR codes at maximum 200KB size**
|
||||||
|
- 4% buffer above 2.2 PB maximum requirement
|
||||||
|
- Drive failure recovery: Distributed across all healthy nodes
|
||||||
|
- Redundancy: Can tolerate up to 10 storage node failures (77% redundancy)
|
||||||
|
|
||||||
|
**HDD Performance Characteristics:**
|
||||||
|
- Sequential throughput: Excellent (150-250 MB/s per drive)
|
||||||
|
- Random IOPS: Lower than NVMe (4K IOPS vs 100K+ IOPS)
|
||||||
|
- QR code workload: Mostly sequential large-blob writes (50-200 KB)
|
||||||
|
- **Verdict:** HDD is well-suited for this workload (large sequential I/O)
|
||||||
|
|
||||||
|
## 7. Recommended Configuration Summary (13 Storage Nodes for Full Capacity)
|
||||||
|
|
||||||
|
**Configuration: 13 storage + 3 control/compute + 1 import (JBOD + 3-replica, SINGLE RACK)**
|
||||||
|
|
||||||
|
| Metric | Specification |
|
||||||
|
|--------|---------------|
|
||||||
|
| **Storage Servers** | 13x Lenovo ThinkSystem SR650 V2 (2U each) |
|
||||||
|
| **Control/Compute** | 3x Lenovo ThinkSystem SR630 V2 (1U each) |
|
||||||
|
| **Import Node** | 1x Lenovo ThinkSystem SR650 V2 (2U) |
|
||||||
|
| **Switches** | 2x Huawei CE6800 (25GbE) |
|
||||||
|
| **Drives per Storage Node** | 24x 22TB SAS (12Gbps, 7.2K RPM) |
|
||||||
|
| **Usable Capacity** | **2.29 PB** (covers all 11B QR codes at 200KB max) |
|
||||||
|
| **Raw Capacity** | 6.86 PB (with 3x replication) |
|
||||||
|
| **Rack Space** | 35U of 42U (7U expansion available) |
|
||||||
|
| **Aggregate IOPS** | 1.25M (96K per storage node) |
|
||||||
|
| **Aggregate Throughput** | 39 GB/s read, 19.5 GB/s write |
|
||||||
|
| **Power** | 12.7 kW (9.8 kW actual, 30% headroom) |
|
||||||
|
| **CapEx (China, Lenovo+Huawei)** | ¥1,973,000 (~$274K USD) |
|
||||||
|
| **Storage Redundancy** | Can lose 10 storage nodes (77% redundancy) |
|
||||||
|
| **Compute Redundancy** | N+1 (any 2 of 3 nodes) |
|
||||||
|
| **Import Speed** | 10-32 GB/s (physical media) |
|
||||||
|
| **Drive Reliability** | SAS enterprise-grade |
|
||||||
|
| **Expansion** | 7U available = up to 3 more storage nodes |
|
||||||
|
|
||||||
|
**Key Features:**
|
||||||
|
- ✅ **Full capacity:** 2.29 PB covers ALL 11 billion QR codes at maximum 200KB size
|
||||||
|
- ✅ **High redundancy:** Can tolerate 10 simultaneous storage node failures
|
||||||
|
- ✅ **Single rack:** All equipment in one 42U rack (35U used)
|
||||||
|
- ✅ **Domestic brands:** 20% cheaper than Dell, excellent local support
|
||||||
|
- ✅ **Physical import:** 10-32 GB/s via USB/NVMe/SATA/SAS hot-plug
|
||||||
|
- ✅ **Industry-standard:** JBOD + 3-replica architecture
|
||||||
|
|
||||||
|
## 10. Cost Estimate for China Private Datacenter (CNY)
|
||||||
|
|
||||||
|
**Exchange Rate: 1 USD = 7.2 CNY (approximate, January 2026)**
|
||||||
|
|
||||||
|
**Summary for China (13 Storage Nodes, Full Capacity, IT Room):**
|
||||||
|
- **Recommended:** Lenovo ThinkSystem + Huawei CE6800
|
||||||
|
- **CapEx:** ¥1,973,000 (~$274K USD, based on public pricing)
|
||||||
|
- **Capacity:** 2.29 PB usable (covers ALL 11B QR codes at max 200KB size)
|
||||||
|
- **Power requirement:** 12.7 kW total (9.8 kW actual + 30% headroom)
|
||||||
|
- **Import node benefit:** 10-32 GB/s physical media import (USB/NVMe/SATA/SAS)
|
||||||
|
- Eliminates internet/WiFi bottleneck
|
||||||
|
- Front-panel hot-plug for operator convenience
|
||||||
|
- 31TB staging buffer for offline processing
|
||||||
|
|
||||||
|
### China Pricing - Lenovo ThinkSystem + Huawei Configuration
|
||||||
|
|
||||||
|
**Hardware (Lenovo + Huawei - 13 Storage Nodes for 2.29 PB):**
|
||||||
|
- Storage nodes (13x Lenovo ThinkSystem SR650 V2 with 24x 22TB SAS): ¥1,235,000
|
||||||
|
- Base server: ~¥35,000 per node
|
||||||
|
- 24x 22TB SAS drives @ ¥3,000 each: ¥72,000 per node
|
||||||
|
- CPU, RAM, HBA upgrades: ~¥23,000 per node
|
||||||
|
- Total per node: ¥95,000
|
||||||
|
- Public pricing reference: Lenovo China website
|
||||||
|
- Control/Compute nodes (3x Lenovo ThinkSystem SR630 V2): ¥306,000
|
||||||
|
- Each node: ¥102,000 (~$14,200)
|
||||||
|
- Multi-purpose: ingestion + API + load balancing + control
|
||||||
|
- Import node (1x Lenovo ThinkSystem SR650 V2 with hot-plug): ¥140,000
|
||||||
|
- Front-panel USB-C, NVMe U.2, SATA/SAS hot-swap
|
||||||
|
- Network switches (2x Huawei CloudEngine 6800-48S-EI): ¥180,000
|
||||||
|
- Public pricing: ¥90,000 per switch
|
||||||
|
- PDUs, cabling, misc: ¥64,000
|
||||||
|
- **Hardware Subtotal (Lenovo + Huawei): ¥1,925,000**
|
||||||
|
|
||||||
|
**Infrastructure (if not existing in IT room):**
|
||||||
|
- Rack (42U, domestic): ¥8,000
|
||||||
|
- Cooling (1x 20kW in-row, if IT room HVAC insufficient): ¥40,000
|
||||||
|
- **Infrastructure Subtotal: ¥48,000**
|
||||||
|
- **Note:** May be partially/fully available in existing IT room (reduce CapEx accordingly)
|
||||||
|
|
||||||
|
**Total CapEx (13 Storage Nodes for Full 2.29 PB):**
|
||||||
|
- **With Lenovo + Huawei: ¥1,973,000** (~$274K USD, RECOMMENDED)
|
||||||
|
- **Note:** UPS excluded (out of project scope). Pricing based on Lenovo China public pricing and Huawei enterprise quotes.
|
||||||
|
|
||||||
|
**Power Requirements (13 Storage Nodes):**
|
||||||
|
- **Total power:** 12.7 kW
|
||||||
|
- 13x storage nodes @ 400W each = 5.2 kW
|
||||||
|
- 3x control/compute nodes @ 500W each = 1.5 kW
|
||||||
|
- 1x import node @ 600W = 0.6 kW
|
||||||
|
- 2x switches @ 400W each = 0.8 kW
|
||||||
|
- Overhead and margin = 4.6 kW
|
||||||
|
- **Cooling requirement:** 16.5 kW thermal (12.7 kW × 1.3 PUE)
|
||||||
|
- **Circuit requirement:** 2x 32A 3-phase 208V circuits minimum
|
||||||
|
|
||||||
|
### HDD Sourcing in China
|
||||||
|
|
||||||
|
**Recommended: Locally assembled enterprise drives**
|
||||||
|
- Seagate Exos X22 22TB (Suzhou facility): ¥2,800-3,200 per drive
|
||||||
|
- Western Digital Ultrastar DC HC570 22TB (Shanghai/Shenzhen): ¥2,900-3,300 per drive
|
||||||
|
- Local assembly = no import VAT (save 13%)
|
||||||
|
- Comes with VAT invoice (增值税发票) for tax deduction
|
||||||
|
- Same warranty as international versions
|
||||||
|
- Faster replacement (local stock)
|
||||||
|
|
||||||
|
### Quick Reference: China Pricing Summary (13 Storage Nodes, 2.29 PB)
|
||||||
|
|
||||||
|
| Item | Value |
|
||||||
|
|------|-------|
|
||||||
|
| **CapEx** | ¥1,973,000 (~$274K USD) |
|
||||||
|
| **Capacity** | 2.29 PB usable |
|
||||||
|
| **Power** | 12.7 kW total |
|
||||||
|
| **Rack space** | 35U of 42U |
|
||||||
|
|
||||||
|
**Lenovo ThinkSystem SR650 V2 + SR630 V2 + Huawei CE6800**
|
||||||
|
- Covers ALL 11 billion QR codes at maximum 200KB size
|
||||||
|
- Import node: 10-32 GB/s physical media import (no network bottleneck)
|
||||||
|
- UPS excluded (out of project scope)
|
||||||
|
|
||||||
|
**Recommendations for China Deployment (IT Room, 13 Storage Nodes):**
|
||||||
|
1. **Use Lenovo ThinkSystem + Huawei** - public pricing available, 20% savings vs Dell
|
||||||
|
2. **IT room requirements (13 storage nodes for full 2.29 PB):**
|
||||||
|
- Power: 12.7 kW capacity needed (208V 3-phase, 30A × 2 PDUs)
|
||||||
|
- Cooling: 43,400 BTU/hr heat dissipation (~3.6 tons, 12.7 kW × 3,412 BTU/kW)
|
||||||
|
- Floor loading: ~1,100 kg total weight (17 servers + switches + rack)
|
||||||
|
- Operator access for import node (front-panel hot-plug)
|
||||||
|
- Rack space: 35U of 42U used (7U for future expansion)
|
||||||
|
3. **Local support contracts** essential (Lenovo + Huawei have excellent China support with 4-hour response)
|
||||||
|
5. **Bulk purchase discount** - negotiate 10-15% off for large orders
|
||||||
|
6. **Payment terms:** Net 30-90 common, some vendors offer 6-12 month financing
|
||||||
|
7. **VAT invoice (增值税发票):** Essential for tax deduction, ensure all vendors provide
|
||||||
|
8. **Domestic drives:** Buy Seagate/WD from China facilities to avoid import VAT
|
||||||
|
9. **Network integration:** Plan uplink to existing corporate network
|
||||||
|
|
||||||
|
**Vendor Contact (China):**
|
||||||
|
- Lenovo: DCG (Data Center Group) - https://www.lenovo.com/cn/zh/data-center (联想数据中心)
|
||||||
|
- Public pricing on website for ThinkSystem servers
|
||||||
|
- Contact: 400-100-6000 (China hotline)
|
||||||
|
- Huawei: Enterprise network division - https://e.huawei.com (华为企业)
|
||||||
|
- CloudEngine switches require enterprise quote
|
||||||
|
- Contact local Huawei account manager
|
||||||
|
- Local integrators: Often 5-10% cheaper than direct (e.g., 神州数码 Digital China)
|
||||||
|
|
||||||
|
## 11. Final Recommendation
|
||||||
|
|
||||||
|
**RECOMMENDED CONFIGURATION: Lenovo ThinkSystem + Huawei in Tier B City**
|
||||||
|
|
||||||
|
**Hardware (13 Storage Nodes for Full 2.29 PB Capacity):**
|
||||||
|
- **13x Lenovo ThinkSystem SR650 V2** storage nodes (24x 22TB SAS each) = **2.29 PB usable**
|
||||||
|
- **3x Lenovo ThinkSystem SR630 V2** control/compute nodes (multi-purpose)
|
||||||
|
- **1x Lenovo ThinkSystem SR650 V2** import node (front-panel hot-plug)
|
||||||
|
- **2x Huawei CloudEngine 6800-48S-EI** switches (48-port 25GbE)
|
||||||
|
|
||||||
|
**Capacity:** 2.29 PB usable - covers ALL 11 billion QR codes at maximum 200KB size
|
||||||
|
|
||||||
|
**Cost:**
|
||||||
|
- **CapEx:** ¥1,973,000 (~$274K USD)
|
||||||
|
- **Note:** UPS excluded (out of project scope). Based on Lenovo China and Huawei public pricing.
|
||||||
|
|
||||||
|
**Power Requirements:**
|
||||||
|
- **Total:** 12.7 kW (9.8 kW actual + 30% headroom)
|
||||||
|
- **Cooling:** 43,400 BTU/hr (~3.6 tons)
|
||||||
|
|
||||||
681
doc/11b_cn.html
Normal file
@ -0,0 +1,681 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="generator" content="pandoc" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
|
||||||
|
<title>项目 11b - 硬件规格</title>
|
||||||
|
<style>
|
||||||
|
code{white-space: pre-wrap;}
|
||||||
|
span.smallcaps{font-variant: small-caps;}
|
||||||
|
span.underline{text-decoration: underline;}
|
||||||
|
div.column{display: inline-block; vertical-align: top; width: 50%;}
|
||||||
|
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
|
||||||
|
ul.task-list{list-style: none;}
|
||||||
|
</style>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 20px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
margin: 20px 0;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 8px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background-color: #f2f2f2;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
padding: 2px 4px;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
ul, ol {
|
||||||
|
margin: 10px 0;
|
||||||
|
padding-left: 30px;
|
||||||
|
}
|
||||||
|
li {
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header id="title-block-header">
|
||||||
|
<h1 class="title">项目 11b - 硬件规格</h1>
|
||||||
|
</header>
|
||||||
|
<p>项目 11b - 硬件规格 v0.1</p>
|
||||||
|
<h2 id="需求">1. 需求</h2>
|
||||||
|
<p>年度数据量:110 亿个二维码,每个 50KB - 200KB。</p>
|
||||||
|
<ul>
|
||||||
|
<li><p><strong>总存储量:</strong> 每年 550 TB - 2.2 PB</p></li>
|
||||||
|
<li><p><strong>导入窗口:</strong> 年度的 1/20 到 1/10(18-36 天)</p></li>
|
||||||
|
<li><p><strong>写入吞吐量:</strong> 持续 3,480 - 6,960 个二维码/秒</p></li>
|
||||||
|
<li><p><strong>导入期间数据速率:</strong> 持续 348 MB/s - 1.39 GB/s</p></li>
|
||||||
|
<li><p><strong>峰值突发(3倍):</strong> 最高 21,000 个二维码/秒,4 GB/s</p></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>解决方案:13 个存储节点 + 3 个控制/计算节点 + 1 个导入节点(JBOD + 3 副本,单机架)</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p><strong>可用容量:</strong> 2.29 PB(覆盖所有 110 亿个二维码,最大 200KB 大小)</p></li>
|
||||||
|
<li><p><strong>原始容量:</strong> 6.86 PB(24 × 22TB × 13 个存储节点)</p></li>
|
||||||
|
<li><p><strong>聚合 IOPS:</strong> ~125 万(每节点 96K × 13,足以满足顺序二维码存储)</p></li>
|
||||||
|
<li><p><strong>聚合吞吐量:</strong> 39 GB/s 读取,19.5 GB/s 写入(超过 1.39 GB/s 需求)</p></li>
|
||||||
|
<li><p><strong>冗余:</strong> 3 倍复制(JBOD,软件管理,可容忍 10 个节点故障)</p></li>
|
||||||
|
<li><p><strong>硬件(联想 + 华为,推荐):</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>13x 联想 ThinkSystem SR650 V2 存储节点(每个 24x 22TB SAS)</p></li>
|
||||||
|
<li><p>3x 联想 ThinkSystem SR630 V2 控制/计算节点(多用途)</p></li>
|
||||||
|
<li><p>1x 联想 ThinkSystem SR650 V2 导入节点(前面板热插拔:USB/NVMe/SATA/SAS)</p></li>
|
||||||
|
<li><p>2x 华为 CloudEngine 6800 交换机(48 端口 25GbE)</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>导入能力:</strong> 10-32 GB/s 物理介质导入(无互联网瓶颈)</p></li>
|
||||||
|
<li><p><strong>成本(中国,联想+华为):</strong> ¥1,973,000 资本支出(约 $274K USD)</p></li>
|
||||||
|
<li><p><strong>注意:</strong> 不包括 UPS(超出项目范围)。价格基于联想中国和华为公开定价。</p></li>
|
||||||
|
<li><p><strong>适合:</strong> 单个 42U 机架(使用 35U,7U 用于扩展)</p></li>
|
||||||
|
</ul>
|
||||||
|
<h2 id="硬件配置">2. 硬件配置</h2>
|
||||||
|
<p><strong>供应商:联想(推荐用于中国部署)</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p><strong>服务器:</strong> 联想 ThinkSystem SR650 V2 和 SR630 V2</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>全球品牌,在中国市场表现优异</p></li>
|
||||||
|
<li><p>比戴尔在中国市场便宜 20-25%</p></li>
|
||||||
|
<li><p>相同的 Intel Xeon CPU,企业级质量</p></li>
|
||||||
|
<li><p>公开定价:每个配置节点约 ¥95,000-120,000</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>网络:</strong> 华为 CloudEngine 6800</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>在中国市场处于行业领先地位</p></li>
|
||||||
|
<li><p>公开定价:每台 48 端口 25GbE 交换机约 ¥90,000</p></li>
|
||||||
|
<li><p>与中国网络(中国电信/联通/移动)集成更好</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>管理:</strong> 联想 XClarity Controller(IPMI/Redfish 标准)</p></li>
|
||||||
|
<li><p><strong>支持:</strong> 联想中国 4 小时现场响应</p></li>
|
||||||
|
<li><p><strong>驱动器:</strong> Seagate Exos 或 WD Ultrastar(中国组装,无进口增值税)</p></li>
|
||||||
|
</ul>
|
||||||
|
<h3 id="机架布局单个-42u-标准机架">2.1 机架布局(单个 42U 标准机架)</h3>
|
||||||
|
<p><strong>单机架完整部署(13 个存储 + 3 个控制/计算 + 1 个导入节点)</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p><strong>U1-U2:</strong> 2x 华为 CloudEngine 6800-48S-EI 交换机(48 端口 25GbE,冗余,CSS)</p></li>
|
||||||
|
<li><p><strong>U3-U4:</strong> 2x 国产 PDU(20x C13,4x C19 插座,208V 三相,A+B 供电)</p></li>
|
||||||
|
<li><p><strong>U5-U30:</strong> 13x 存储节点(每个 2U,联想 ThinkSystem SR650 V2,24x 22TB SAS)</p>
|
||||||
|
<ul>
|
||||||
|
<li>2.29 PB 可用(覆盖所有 110 亿个二维码,最大 200KB 大小)</li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>U31-U33:</strong> 3x 控制/计算节点(每个 1U,联想 ThinkSystem SR630 V2,多用途)</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>运行:摄取工作器、API 服务器、负载均衡器、控制平面</p></li>
|
||||||
|
<li><p>所有服务在所有 3 个节点上运行以实现冗余</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>U34-U35:</strong> 1x 导入节点(2U,联想 ThinkSystem SR650 V2,前面板热插拔用于物理介质)</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>USB 3.2 / NVMe / SATA / SAS 热插拔插槽</p></li>
|
||||||
|
<li><p>直接物理介质导入(无网络瓶颈)</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>U36-U42:</strong> 保留用于未来扩展(7U 可用)</p></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>使用的总机架空间:42U 中的 35U</strong></p>
|
||||||
|
<h3 id="网络设计">2.2 网络设计</h3>
|
||||||
|
<p><strong>机架顶部交换机:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>型号:华为 CloudEngine 6800-48S-EI(推荐)</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>48x 25GbE SFP28 端口 + 6x 100GbE QSFP28 上行链路端口</p></li>
|
||||||
|
<li><p>2x 冗余 PSU(AC/DC)</p></li>
|
||||||
|
<li><p>2x 冗余风扇</p></li>
|
||||||
|
<li><p>VRP OS(华为通用路由平台)</p></li>
|
||||||
|
<li><p>公开定价:中国每台交换机约 ¥90,000</p></li>
|
||||||
|
<li><p>在中国市场处于行业领先地位,比戴尔便宜 25%</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p>配置:</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>CSS(集群交换系统)配对以实现冗余</p></li>
|
||||||
|
<li><p>所有服务器连接的 LACP 绑定(每节点 2x25GbE)</p></li>
|
||||||
|
<li><p>VLAN:管理(VLAN10)、存储(VLAN20)、API(VLAN30)</p></li>
|
||||||
|
<li><p>启用巨型帧(MTU 9000)</p></li>
|
||||||
|
</ul></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>上行链路:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>2x 100GbE 光纤到核心/分发交换机</p></li>
|
||||||
|
<li><p>ECMP 路由用于负载分发</p></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>线缆:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>DAC(直连铜缆)Twinax 用于机架内(< 5m)</p></li>
|
||||||
|
<li><p>OM4 MMF 或 SMF 用于机架间连接</p></li>
|
||||||
|
</ul>
|
||||||
|
<h3 id="电源设计">2.3 电源设计</h3>
|
||||||
|
<p><strong>电源需求(单机架 - 带导入节点:13 个存储 + 3 个计算 + 1 个导入):</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>存储节点:13 个节点 × 500W = 6,500W(HDD 比 NVMe 耗电少得多)</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>每个 HDD:~10W 空闲,~12W 活动</p></li>
|
||||||
|
<li><p>每节点 24 个 HDD:~300W</p></li>
|
||||||
|
<li><p>CPU + RAM + 风扇:~200W</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p>控制/计算节点:3 个节点 × 600W = 1,800W</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>运行所有服务的多用途节点</p></li>
|
||||||
|
<li><p>CPU 利用率更高但已整合</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p>导入节点:1 个节点 × 700W = 700W</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>由于 NVMe 暂存驱动器 + 多个控制器而功耗更高</p></li>
|
||||||
|
<li><p>导入操作期间的峰值</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p>网络交换机:2 个交换机 × 400W = 800W</p></li>
|
||||||
|
<li><p><strong>总计:~9,800W = 9.8 kW</strong></p></li>
|
||||||
|
<li><p><strong>含 30% 余量:单机架 12.7 kW</strong></p></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>电源摘要:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>存储节点:6.5 kW(13 个节点 × 500W)</p></li>
|
||||||
|
<li><p>控制/计算节点:1.8 kW(3 个节点 × 600W)</p></li>
|
||||||
|
<li><p>导入节点:0.7 kW(1 个节点 × 700W)</p></li>
|
||||||
|
<li><p>网络交换机:0.8 kW(2 个交换机 × 400W)</p></li>
|
||||||
|
<li><p><strong>总计:9.8 kW(含 30% 余量为 12.7 kW)</strong></p></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>PDU 配置(单机架 - 13 个存储节点):</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>2x 国产计量机架 PDU(冗余供电,A+B 电源)</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>输入:208V 三相,每个 PDU 30A</p></li>
|
||||||
|
<li><p>容量:208V × 30A × 1.732 = 每个 PDU 10.8 kVA(三相)</p></li>
|
||||||
|
<li><p>总计:机架 21.6 kVA(足以满足 12.7 kW 负载,有余量)</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p>每个服务器:双 PSU 连接到单独的 PDU(A+B 供电)</p></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>冷却(单机架 - 带导入节点,13 个存储节点):</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>散热:12.7 kW × 3.41 = 43,307 BTU/hr(单机架)</p></li>
|
||||||
|
<li><p>需求:43,400 BTU/hr 的冷却容量(~3.6 吨)</p></li>
|
||||||
|
<li><p>选项:</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>使用现有 IT 机房 CRAC/HVAC(如果容量可用)</p></li>
|
||||||
|
<li><p>添加 1x 20kW 行内冷却单元(¥40,000-50,000)</p></li>
|
||||||
|
<li><p>标准精密空调单元(4-5 吨容量)</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p>单机架无需特殊围护</p></li>
|
||||||
|
</ul>
|
||||||
|
<h3 id="存储节点规格">2.4 存储节点规格</h3>
|
||||||
|
<p><strong>存储容量规划:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>最大需求:每年 2.2 PB</p></li>
|
||||||
|
<li><p>复制策略影响原始容量需求:</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>RAID 10 + 2 倍复制:4.4 PB 原始(复杂,双层冗余)</p></li>
|
||||||
|
<li><p>JBOD + 3 倍复制:6.6 PB 原始(更简单,仅软件冗余)</p></li>
|
||||||
|
</ul></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>架构选择:JBOD vs RAID</strong></p>
|
||||||
|
<p><strong>JBOD + 3 副本(推荐,软件简单):</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>无 RAID 控制器复杂性</p></li>
|
||||||
|
<li><p>软件处理所有冗余(Ceph、MinIO 等)</p></li>
|
||||||
|
<li><p>每个驱动器独立,故障恢复更容易</p></li>
|
||||||
|
<li><p>更好的性能可观测性</p></li>
|
||||||
|
<li><p>分布式存储的标准方法</p></li>
|
||||||
|
<li><p>更容易重新平衡和维护</p></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>RAID 10 + 2 副本(传统方法):</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>硬件 RAID 提供本地冗余</p></li>
|
||||||
|
<li><p>更低的网络复制流量</p></li>
|
||||||
|
<li><p>更快的本地读取(RAID 条带化)</p></li>
|
||||||
|
<li><p>更复杂的故障场景(RAID + 集群)</p></li>
|
||||||
|
<li><p>驱动器故障时的 RAID 重建开销</p></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>存储节点:13 个节点,22TB SAS HDD + JBOD + 3 副本(覆盖所有 110 亿个二维码)</strong></p>
|
||||||
|
<p><strong>型号:联想 ThinkSystem SR650 V2(2U,24x 2.5" 驱动器插槽)</strong> <strong>公开定价:中国每个配置节点约 ¥95,000-105,000</strong></p>
|
||||||
|
<p><strong>每个存储节点:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p><strong>CPU:</strong> 2x Intel Xeon Silver 4316(20 核,2.3 GHz,30MB 缓存)</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>总计:每节点 40 核,80 线程</p></li>
|
||||||
|
<li><p>零件号:4XG7A42589</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>RAM:</strong> 256 GB DDR4-3200 ECC RDIMM(8x 32GB)</p>
|
||||||
|
<ul>
|
||||||
|
<li>零件号:4X77A08633 或同等产品</li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>启动:</strong> 2x 480GB SATA SSD(RAID 1,操作系统)</p>
|
||||||
|
<ul>
|
||||||
|
<li>联想 2.5" 6Gb SATA SSD</li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>存储:</strong> 24x 22TB SAS HDD 12Gbps 7.2K RPM</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>Seagate Exos X22(ST22000NM00 系列)或 WD Ultrastar</p></li>
|
||||||
|
<li><p>公开定价:每个驱动器约 ¥2,800-3,200(约 $390-445)</p></li>
|
||||||
|
<li><p>JBOD 配置(无 RAID,所有驱动器独立)</p></li>
|
||||||
|
<li><p>每节点:528 TB 原始(24 × 22TB)</p></li>
|
||||||
|
<li><p>总集群:6.86 PB 原始(13 个节点 × 528 TB)</p></li>
|
||||||
|
<li><p>3 倍复制:2.29 PB 可用</p></li>
|
||||||
|
<li><p>性能:每驱动器 4K IOPS,每节点 ~96K IOPS,每节点 1.5 GB/s</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>网络:</strong> 2x 联想 ThinkSystem Broadcom 57504 25GbE(4 端口,绑定)</p></li>
|
||||||
|
<li><p><strong>HBA:</strong> 联想 ThinkSystem 430-8i SAS/SATA 12Gb HBA(直通模式)</p></li>
|
||||||
|
<li><p><strong>PSU:</strong> 2x 1100W 铂金(冗余,208V)</p></li>
|
||||||
|
<li><p><strong>机架空间:</strong> 13 个节点 × 2U = 26U</p></li>
|
||||||
|
</ul>
|
||||||
|
<h3 id="导入节点规格1-个节点专用于物理介质导入">2.5 导入节点规格(1 个节点,专用于物理介质导入)</h3>
|
||||||
|
<p><strong>型号:联想 ThinkSystem SR650 V2(2U,前面板可访问热插拔)</strong> <strong>公开定价:每个配置节点约 ¥140,000</strong></p>
|
||||||
|
<p><strong>每个节点:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p><strong>CPU:</strong> 2x Intel Xeon Gold 6338(32 核,2.0 GHz,48MB 缓存)</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>总计:64 核,128 线程</p></li>
|
||||||
|
<li><p>高核数用于并行导入处理</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>RAM:</strong> 256 GB DDR4-3200 ECC</p>
|
||||||
|
<ul>
|
||||||
|
<li>用于暂存导入数据的大缓冲区</li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>启动:</strong> 2x 480GB SATA SSD(RAID 1,操作系统)</p></li>
|
||||||
|
<li><p><strong>导入暂存存储:</strong> 4x 7.68TB NVMe(JBOD)</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>总计:~31TB 暂存缓冲区</p></li>
|
||||||
|
<li><p>复制到存储集群之前的高速本地缓存</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>热插拔插槽(前面板):</strong> 12 个插槽支持:</p>
|
||||||
|
<ul>
|
||||||
|
<li><p><strong>USB 3.2 Gen 2x2:</strong> 4x 前面板 USB-C 端口(每个 2.5 GB/s)</p></li>
|
||||||
|
<li><p><strong>NVMe U.2:</strong> 4x 热插拔 NVMe 插槽(每个驱动器最高 8 GB/s)</p></li>
|
||||||
|
<li><p><strong>SATA/SAS:</strong> 4x 热插拔 3.5" 插槽(12 Gbps SAS)</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>控制器:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>联想 ThinkSystem 430-8i SAS/SATA HBA(直通模式)</p></li>
|
||||||
|
<li><p>PCIe Gen4 NVMe 交换机</p></li>
|
||||||
|
<li><p>USB 3.2 Gen 2x2 控制器</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>网络:</strong> 2x 25GbE(绑定,LACP)</p></li>
|
||||||
|
<li><p><strong>PSU:</strong> 2x 1100W 铂金</p></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>导入工作流程:</strong> 1. 操作员将物理介质(USB、NVMe、SATA、SAS)插入前面板 2. 驱动器自动挂载或热检测 3. 导入软件从介质读取数据 → 暂存 NVMe(快速本地) 4. 并行处理/验证二维码(32-48 核) 5. 通过 25GbE(或 100GbE)将验证后的数据流式传输到存储集群 6. 导入完成后操作员移除介质(LED 指示灯)</p>
|
||||||
|
<p><strong>性能:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p><strong>USB 3.2:</strong> 4 端口 × 2.5 GB/s = 10 GB/s 聚合</p></li>
|
||||||
|
<li><p><strong>NVMe:</strong> 4 驱动器 × 8 GB/s = 32 GB/s 聚合</p></li>
|
||||||
|
<li><p><strong>SATA/SAS:</strong> 4 驱动器 × 1.5 GB/s = 6 GB/s 聚合</p></li>
|
||||||
|
<li><p><strong>网络:</strong> 2x 25GbE = 6.25 GB/s 到存储集群</p></li>
|
||||||
|
<li><p>暂存 NVMe:31TB 缓冲区允许在网络传输之前进行离线处理</p></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>优势:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p><strong>无互联网/WiFi 瓶颈:</strong> 以全速直接物理介质导入</p></li>
|
||||||
|
<li><p><strong>并行导入:</strong> 同时接受最多 12 个驱动器</p></li>
|
||||||
|
<li><p><strong>灵活介质:</strong> 支持 USB、NVMe、SATA、SAS</p></li>
|
||||||
|
<li><p><strong>高吞吐量:</strong> 本地暂存消除了读取期间的网络瓶颈</p></li>
|
||||||
|
<li><p><strong>操作员友好:</strong> 前面板访问、LED 指示灯、热插拔安全</p></li>
|
||||||
|
</ul>
|
||||||
|
<h3 id="控制计算节点规格3-个节点整合">2.6 控制/计算节点规格(3 个节点,整合)</h3>
|
||||||
|
<p><strong>型号:联想 ThinkSystem SR630 V2(1U,多用途)</strong> <strong>公开定价:每个配置节点约 ¥102,000</strong></p>
|
||||||
|
<p><strong>每个节点(运行所有服务):</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p><strong>CPU:</strong> 2x Intel Xeon Gold 6338(32 核,2.0 GHz)</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>总计:每节点 64 核,128 线程</p></li>
|
||||||
|
<li><p>足以满足摄取 + API + 控制平面 + 负载均衡</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>RAM:</strong> 256 GB DDR4-3200 ECC(8x 32GB)</p>
|
||||||
|
<ul>
|
||||||
|
<li>分配:128GB 用于摄取,64GB 用于 API,64GB 用于系统/控制</li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>启动:</strong> 2x 480GB SATA SSD(RAID 1,操作系统)</p></li>
|
||||||
|
<li><p><strong>本地存储:</strong> 2x 3.84TB NVMe</p>
|
||||||
|
<ul>
|
||||||
|
<li>摄取写入缓冲区 + API 读取缓存 + 监控数据</li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>网络:</strong> 2x 联想 ThinkSystem Broadcom 57504 25GbE(绑定,LACP)</p></li>
|
||||||
|
<li><p><strong>PSU:</strong> 2x 800W 铂金</p></li>
|
||||||
|
</ul>
|
||||||
|
<h2 id="网络拓扑">3. 网络拓扑</h2>
|
||||||
|
<pre><code>核心交换机(100GbE 上行链路)
|
||||||
|
↓
|
||||||
|
机架 ToR 交换机(华为 CSS 配对,25GbE 到服务器)
|
||||||
|
↓
|
||||||
|
联想服务器(双归属,LACP 绑定)</code></pre>
|
||||||
|
<h2 id="成本估算近似">4. 成本估算(近似)</h2>
|
||||||
|
<p><strong>硬件(单机架 - 13 个存储 + 3 个控制/计算 + 1 个导入,完整 2.29 PB):</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>存储节点(13x):$195K</p></li>
|
||||||
|
<li><p>控制/计算节点(3x):$48K</p></li>
|
||||||
|
<li><p>导入节点(1x):$22K</p></li>
|
||||||
|
<li><p>网络交换机(2x):$30K</p></li>
|
||||||
|
<li><p>其他硬件和基础设施:$13K</p></li>
|
||||||
|
<li><p><strong>总计:$308K 资本支出</strong></p></li>
|
||||||
|
<li><p><strong>注意:</strong> 不包括 UPS(超出项目范围)</p></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>电源需求:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p><strong>总机架功率:</strong> 最大 9.5kW</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>13x 存储节点 @ 每个 400W = 5.2kW</p></li>
|
||||||
|
<li><p>3x 控制/计算节点 @ 每个 300W = 0.9kW</p></li>
|
||||||
|
<li><p>1x 导入节点 @ 500W = 0.5kW</p></li>
|
||||||
|
<li><p>2x 交换机 @ 每个 400W = 0.8kW</p></li>
|
||||||
|
<li><p>余量:~1.6kW 储备</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>PDU 需求:</strong> 2x 5kW PDU(A+B 供电)</p></li>
|
||||||
|
<li><p><strong>冷却需求:</strong> ~12kW 热(9.5kW × 1.3 PUE)</p></li>
|
||||||
|
</ul>
|
||||||
|
<h2 id="冗余和高可用性单机架13-个存储节点">5. 冗余和高可用性(单机架,13 个存储节点)</h2>
|
||||||
|
<ul>
|
||||||
|
<li><p><strong>存储:</strong> JBOD + 3 倍复制分布在 13 个联想 ThinkSystem SR650 V2 节点上</p>
|
||||||
|
<ul>
|
||||||
|
<li><p><strong>任何 10 个存储节点可同时故障而不会丢失数据(77% 冗余)</strong></p></li>
|
||||||
|
<li><p>312 个独立驱动器(每节点 24 个 × 13),软件管理</p></li>
|
||||||
|
<li><p>单个驱动器故障:自动重新平衡到其他节点</p></li>
|
||||||
|
<li><p>无 RAID 重建开销 - 软件处理恢复</p></li>
|
||||||
|
<li><p>驱动器恢复:~22TB 通过 25GbE = ~2 小时(vs RAID 重建需要数天)</p></li>
|
||||||
|
<li><p>数据条带化和复制用于并行 I/O</p></li>
|
||||||
|
<li><p>需要最少 4 个节点以维持数据可用性(3 倍复制)</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>控制/计算:</strong> 3 个多用途节点(主动-主动-主动)</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>所有 3 个节点运行:摄取工作器、API 服务器、负载均衡器、控制平面</p></li>
|
||||||
|
<li><p>3 个节点中的任何 2 个可以处理完整工作负载(N+1 冗余)</p></li>
|
||||||
|
<li><p>可以丢失 1 个控制/计算节点而不会中断服务</p></li>
|
||||||
|
<li><p>通过 DNS 轮询或浮动 VIP(Keepalived)进行负载均衡</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>网络:</strong> 华为 VLT/CSS 交换机,双归属服务器(2x25GbE 绑定)</p></li>
|
||||||
|
<li><p><strong>电源:</strong> 每个服务器双 PSU,冗余 PDU(A+B 供电)</p></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>扩展路径(如果未来需求增加):</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>第 1 年:13 个存储 + 3 个控制/计算 + 1 个导入 = 2.29 PB 可用(覆盖所有 110 亿个二维码)</p></li>
|
||||||
|
<li><p>未来扩展:机架中 7U 可用 = 最多 3 个更多 2U 存储节点</p></li>
|
||||||
|
<li><p>16 个存储节点:2.82 PB 可用(容量增加 23%)</p></li>
|
||||||
|
<li><p>超过 16 个节点:需要第二个机架或更高容量的驱动器</p></li>
|
||||||
|
</ul>
|
||||||
|
<h2 id="性能验证基于-hdd单机架">6. 性能验证(基于 HDD,单机架)</h2>
|
||||||
|
<p><strong>目标指标(13 个存储节点):</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>写入吞吐量:持续 6,960 个二维码/秒(1.39 GB/s)</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>3 倍复制:4.17 GB/s 网络写入流量</p></li>
|
||||||
|
<li><p>HDD 能力:19.5 GB/s 聚合写入(13 个节点 × 1.5 GB/s)✓ <strong>优秀</strong></p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p>峰值突发:21,000 个二维码/秒(4.2 GB/s)</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>3 倍复制:12.6 GB/s 网络写入流量</p></li>
|
||||||
|
<li><p>HDD 能力:19.5 GB/s 聚合写入(峰值)✓ <strong>优秀</strong>(55% 余量)</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p>读取延迟:< 50ms P99(带缓存)✓</p>
|
||||||
|
<ul>
|
||||||
|
<li>HDD 寻道:4-8ms,顺序:快速</li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p>存储 IOPS:125 万聚合(每节点 96K × 13,每驱动器 4K)</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>足以满足大型顺序二维码写入(每个 50-200 KB)</p></li>
|
||||||
|
<li><p>二维码是大块数据,不是小随机 I/O</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p>网络吞吐量:650 Gbps 聚合(每节点 50 Gbps × 13)</p></li>
|
||||||
|
<li><p>网络利用率:持续写入期间 < 8%,峰值突发期间 < 24%</p></li>
|
||||||
|
<li><p>存储容量:2.29 PB 可用(3 倍复制为 6.86 PB 原始)</p>
|
||||||
|
<ul>
|
||||||
|
<li><p><strong>覆盖所有 110 亿个二维码,最大 200KB 大小</strong></p></li>
|
||||||
|
<li><p>比 2.2 PB 最大需求高 4% 的缓冲区</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p>驱动器故障恢复:分布在所有健康节点上</p></li>
|
||||||
|
<li><p>冗余:可容忍最多 10 个存储节点故障(77% 冗余)</p></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>HDD 性能特征:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>顺序吞吐量:优秀(每驱动器 150-250 MB/s)</p></li>
|
||||||
|
<li><p>随机 IOPS:低于 NVMe(4K IOPS vs 100K+ IOPS)</p></li>
|
||||||
|
<li><p>二维码工作负载:主要是顺序大块写入(50-200 KB)</p></li>
|
||||||
|
<li><p><strong>结论:</strong> HDD 非常适合此工作负载(大顺序 I/O)</p></li>
|
||||||
|
</ul>
|
||||||
|
<h2 id="推荐配置摘要13-个存储节点完整容量">7. 推荐配置摘要(13 个存储节点,完整容量)</h2>
|
||||||
|
<p><strong>配置:13 个存储 + 3 个控制/计算 + 1 个导入(JBOD + 3 副本,单机架)</strong></p>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr class="header">
|
||||||
|
<th>指标</th>
|
||||||
|
<th>规格</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="odd">
|
||||||
|
<td><strong>存储服务器</strong></td>
|
||||||
|
<td>13x 联想 ThinkSystem SR650 V2(每个 2U)</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="even">
|
||||||
|
<td><strong>控制/计算</strong></td>
|
||||||
|
<td>3x 联想 ThinkSystem SR630 V2(每个 1U)</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="odd">
|
||||||
|
<td><strong>导入节点</strong></td>
|
||||||
|
<td>1x 联想 ThinkSystem SR650 V2(2U)</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="even">
|
||||||
|
<td><strong>交换机</strong></td>
|
||||||
|
<td>2x 华为 CE6800(25GbE)</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="odd">
|
||||||
|
<td><strong>每个存储节点的驱动器</strong></td>
|
||||||
|
<td>24x 22TB SAS(12Gbps,7.2K RPM)</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="even">
|
||||||
|
<td><strong>可用容量</strong></td>
|
||||||
|
<td><strong>2.29 PB</strong>(覆盖所有 110 亿个二维码,最大 200KB)</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="odd">
|
||||||
|
<td><strong>原始容量</strong></td>
|
||||||
|
<td>6.86 PB(3 倍复制)</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="even">
|
||||||
|
<td><strong>机架空间</strong></td>
|
||||||
|
<td>42U 中的 35U(7U 扩展可用)</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="odd">
|
||||||
|
<td><strong>聚合 IOPS</strong></td>
|
||||||
|
<td>125 万(每个存储节点 96K)</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="even">
|
||||||
|
<td><strong>聚合吞吐量</strong></td>
|
||||||
|
<td>39 GB/s 读取,19.5 GB/s 写入</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="odd">
|
||||||
|
<td><strong>电源</strong></td>
|
||||||
|
<td>12.7 kW(9.8 kW 实际,30% 余量)</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="even">
|
||||||
|
<td><strong>资本支出(中国,联想+华为)</strong></td>
|
||||||
|
<td>¥1,973,000(约 $274K USD)</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="odd">
|
||||||
|
<td><strong>存储冗余</strong></td>
|
||||||
|
<td>可丢失 10 个存储节点(77% 冗余)</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="even">
|
||||||
|
<td><strong>计算冗余</strong></td>
|
||||||
|
<td>N+1(3 个节点中的任何 2 个)</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="odd">
|
||||||
|
<td><strong>导入速度</strong></td>
|
||||||
|
<td>10-32 GB/s(物理介质)</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="even">
|
||||||
|
<td><strong>驱动器可靠性</strong></td>
|
||||||
|
<td>SAS 企业级</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="odd">
|
||||||
|
<td><strong>扩展</strong></td>
|
||||||
|
<td>7U 可用 = 最多 3 个更多存储节点</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p><strong>关键特性:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>✅ <strong>完整容量:</strong> 2.29 PB 覆盖所有 110 亿个二维码,最大 200KB 大小</p></li>
|
||||||
|
<li><p>✅ <strong>高冗余:</strong> 可容忍 10 个同时存储节点故障</p></li>
|
||||||
|
<li><p>✅ <strong>单机架:</strong> 所有设备在一个 42U 机架中(使用 35U)</p></li>
|
||||||
|
<li><p>✅ <strong>国产品牌:</strong> 比戴尔便宜 20%,优秀的本地支持</p></li>
|
||||||
|
<li><p>✅ <strong>物理导入:</strong> 通过 USB/NVMe/SATA/SAS 热插拔 10-32 GB/s</p></li>
|
||||||
|
<li><p>✅ <strong>行业标准:</strong> JBOD + 3 副本架构</p></li>
|
||||||
|
</ul>
|
||||||
|
<h2 id="中国私有数据中心成本估算人民币">10. 中国私有数据中心成本估算(人民币)</h2>
|
||||||
|
<p><strong>汇率:1 USD = 7.2 CNY(近似,2026 年 1 月)</strong></p>
|
||||||
|
<p><strong>中国摘要(13 个存储节点,完整容量,IT 机房):</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p><strong>推荐:</strong> 联想 ThinkSystem + 华为 CE6800</p></li>
|
||||||
|
<li><p><strong>资本支出:</strong> ¥1,973,000(约 $274K USD,基于公开定价)</p></li>
|
||||||
|
<li><p><strong>容量:</strong> 2.29 PB 可用(覆盖所有 110 亿个二维码,最大 200KB 大小)</p></li>
|
||||||
|
<li><p><strong>电源需求:</strong> 总计 12.7 kW(9.8 kW 实际 + 30% 余量)</p></li>
|
||||||
|
<li><p><strong>导入节点优势:</strong> 10-32 GB/s 物理介质导入(USB/NVMe/SATA/SAS)</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>消除互联网/WiFi 瓶颈</p></li>
|
||||||
|
<li><p>前面板热插拔便于操作员使用</p></li>
|
||||||
|
<li><p>31TB 暂存缓冲区用于离线处理</p></li>
|
||||||
|
</ul></li>
|
||||||
|
</ul>
|
||||||
|
<h3 id="中国定价---联想-thinksystem-华为配置">中国定价 - 联想 ThinkSystem + 华为配置</h3>
|
||||||
|
<p><strong>硬件(联想 + 华为 - 13 个存储节点,2.29 PB):</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>存储节点(13x 联想 ThinkSystem SR650 V2,24x 22TB SAS):¥1,235,000</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>基础服务器:每节点约 ¥35,000</p></li>
|
||||||
|
<li><p>24x 22TB SAS 驱动器 @ 每个 ¥3,000:每节点 ¥72,000</p></li>
|
||||||
|
<li><p>CPU、RAM、HBA 升级:每节点约 ¥23,000</p></li>
|
||||||
|
<li><p>每节点总计:¥95,000</p></li>
|
||||||
|
<li><p>公开定价参考:联想中国网站</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p>控制/计算节点(3x 联想 ThinkSystem SR630 V2):¥306,000</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>每个节点:¥102,000(约 $14,200)</p></li>
|
||||||
|
<li><p>多用途:摄取 + API + 负载均衡 + 控制</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p>导入节点(1x 联想 ThinkSystem SR650 V2,带热插拔):¥140,000</p>
|
||||||
|
<ul>
|
||||||
|
<li>前面板 USB-C、NVMe U.2、SATA/SAS 热插拔</li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p>网络交换机(2x 华为 CloudEngine 6800-48S-EI):¥180,000</p>
|
||||||
|
<ul>
|
||||||
|
<li>公开定价:每台交换机 ¥90,000</li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p>PDU、线缆、杂项:¥64,000</p></li>
|
||||||
|
<li><p><strong>硬件小计(联想 + 华为):¥1,925,000</strong></p></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>基础设施(如果 IT 机房不存在):</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>机架(42U,国产):¥8,000</p></li>
|
||||||
|
<li><p>冷却(1x 20kW 行内,如果 IT 机房 HVAC 不足):¥40,000</p></li>
|
||||||
|
<li><p><strong>基础设施小计:¥48,000</strong></p></li>
|
||||||
|
<li><p><strong>注意:</strong> 可能在现有 IT 机房中部分/完全可用(相应减少资本支出)</p></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>总资本支出(13 个存储节点,完整 2.29 PB):</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p><strong>联想 + 华为:¥1,973,000</strong>(约 $274K USD,推荐)</p></li>
|
||||||
|
<li><p><strong>注意:</strong> 不包括 UPS(超出项目范围)。价格基于联想中国公开定价和华为企业报价。</p></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>电源需求(13 个存储节点):</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p><strong>总功率:</strong> 12.7 kW</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>13x 存储节点 @ 每个 400W = 5.2 kW</p></li>
|
||||||
|
<li><p>3x 控制/计算节点 @ 每个 500W = 1.5 kW</p></li>
|
||||||
|
<li><p>1x 导入节点 @ 600W = 0.6 kW</p></li>
|
||||||
|
<li><p>2x 交换机 @ 每个 400W = 0.8 kW</p></li>
|
||||||
|
<li><p>开销和余量 = 4.6 kW</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p><strong>冷却需求:</strong> 16.5 kW 热(12.7 kW × 1.3 PUE)</p></li>
|
||||||
|
<li><p><strong>电路需求:</strong> 最少 2x 32A 三相 208V 电路</p></li>
|
||||||
|
</ul>
|
||||||
|
<h3 id="中国-hdd-采购">中国 HDD 采购</h3>
|
||||||
|
<p><strong>推荐:本地组装的企业级驱动器</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>Seagate Exos X22 22TB(苏州工厂):每个驱动器 ¥2,800-3,200</p></li>
|
||||||
|
<li><p>Western Digital Ultrastar DC HC570 22TB(上海/深圳):每个驱动器 ¥2,900-3,300</p></li>
|
||||||
|
<li><p>本地组装 = 无进口增值税(节省 13%)</p></li>
|
||||||
|
<li><p>提供增值税发票用于税务抵扣</p></li>
|
||||||
|
<li><p>与国际版本相同的保修</p></li>
|
||||||
|
<li><p>更快的更换(本地库存)</p></li>
|
||||||
|
</ul>
|
||||||
|
<h3 id="快速参考中国定价摘要13-个存储节点2.29-pb">快速参考:中国定价摘要(13 个存储节点,2.29 PB)</h3>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr class="header">
|
||||||
|
<th>项目</th>
|
||||||
|
<th>价值</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr class="odd">
|
||||||
|
<td><strong>资本支出</strong></td>
|
||||||
|
<td>¥1,973,000(约 $274K USD)</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="even">
|
||||||
|
<td><strong>容量</strong></td>
|
||||||
|
<td>2.29 PB 可用</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="odd">
|
||||||
|
<td><strong>电源</strong></td>
|
||||||
|
<td>总计 12.7 kW</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="even">
|
||||||
|
<td><strong>机架空间</strong></td>
|
||||||
|
<td>42U 中的 35U</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<p><strong>联想 ThinkSystem SR650 V2 + SR630 V2 + 华为 CE6800</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>覆盖所有 110 亿个二维码,最大 200KB 大小</p></li>
|
||||||
|
<li><p>导入节点:10-32 GB/s 物理介质导入(无网络瓶颈)</p></li>
|
||||||
|
<li><p>不包括 UPS(超出项目范围)</p></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>中国部署建议(IT 机房,13 个存储节点):</strong> 1. <strong>使用联想 ThinkSystem + 华为</strong> - 公开定价可用,比戴尔节省 20% 2. <strong>IT 机房需求(13 个存储节点,完整 2.29 PB):</strong> - 电源:需要 12.7 kW 容量(208V 三相,30A × 2 PDU) - 冷却:43,400 BTU/hr 散热(~3.6 吨,12.7 kW × 3,412 BTU/kW) - 地板承重:~1,100 kg 总重量(17 台服务器 + 交换机 + 机架) - 导入节点的操作员访问(前面板热插拔) - 机架空间:使用 42U 中的 35U(7U 用于未来扩展) 3. <strong>本地支持合同</strong> 必不可少(联想 + 华为在中国有优秀的支持,4 小时响应) 5. <strong>批量采购折扣</strong> - 大订单协商 10-15% 折扣 6. <strong>付款条件:</strong> Net 30-90 常见,一些供应商提供 6-12 个月融资 7. <strong>增值税发票(增值税发票):</strong> 税务抵扣必不可少,确保所有供应商提供 8. <strong>国产驱动器:</strong> 从中国工厂购买 Seagate/WD 以避免进口增值税 9. <strong>网络集成:</strong> 规划到现有企业网络的上行链路</p>
|
||||||
|
<p><strong>供应商联系(中国):</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p>联想:DCG(数据中心集团)- https://www.lenovo.com/cn/zh/data-center(联想数据中心)</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>ThinkSystem 服务器在网站上有公开定价</p></li>
|
||||||
|
<li><p>联系:400-100-6000(中国热线)</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p>华为:企业网络部门 - https://e.huawei.com(华为企业)</p>
|
||||||
|
<ul>
|
||||||
|
<li><p>CloudEngine 交换机需要企业报价</p></li>
|
||||||
|
<li><p>联系本地华为客户经理</p></li>
|
||||||
|
</ul></li>
|
||||||
|
<li><p>本地集成商:通常比直接购买便宜 5-10%(例如,神州数码 Digital China)</p></li>
|
||||||
|
</ul>
|
||||||
|
<h2 id="最终建议">11. 最终建议</h2>
|
||||||
|
<p><strong>推荐配置:联想 ThinkSystem + 华为在二线城市</strong></p>
|
||||||
|
<p><strong>硬件(13 个存储节点,完整 2.29 PB 容量):</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p><strong>13x 联想 ThinkSystem SR650 V2</strong> 存储节点(每个 24x 22TB SAS)= <strong>2.29 PB 可用</strong></p></li>
|
||||||
|
<li><p><strong>3x 联想 ThinkSystem SR630 V2</strong> 控制/计算节点(多用途)</p></li>
|
||||||
|
<li><p><strong>1x 联想 ThinkSystem SR650 V2</strong> 导入节点(前面板热插拔)</p></li>
|
||||||
|
<li><p><strong>2x 华为 CloudEngine 6800-48S-EI</strong> 交换机(48 端口 25GbE)</p></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>容量:</strong> 2.29 PB 可用 - 覆盖所有 110 亿个二维码,最大 200KB 大小</p>
|
||||||
|
<p><strong>成本:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p><strong>资本支出:</strong> ¥1,973,000(约 $274K USD)</p></li>
|
||||||
|
<li><p><strong>注意:</strong> 不包括 UPS(超出项目范围)。基于联想中国和华为公开定价。</p></li>
|
||||||
|
</ul>
|
||||||
|
<p><strong>电源需求:</strong></p>
|
||||||
|
<ul>
|
||||||
|
<li><p><strong>总计:</strong> 12.7 kW(9.8 kW 实际 + 30% 余量)</p></li>
|
||||||
|
<li><p><strong>冷却:</strong> 43,400 BTU/hr(~3.6 吨)</p></li>
|
||||||
|
</ul>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
481
doc/11b_cn.md
Normal file
@ -0,0 +1,481 @@
|
|||||||
|
项目 11b - 硬件规格 v0.1
|
||||||
|
|
||||||
|
## 1. 需求
|
||||||
|
|
||||||
|
年度数据量:110 亿个二维码,每个 50KB - 200KB。
|
||||||
|
- **总存储量:** 每年 550 TB - 2.2 PB
|
||||||
|
- **导入窗口:** 年度的 1/20 到 1/10(18-36 天)
|
||||||
|
- **写入吞吐量:** 持续 3,480 - 6,960 个二维码/秒
|
||||||
|
- **导入期间数据速率:** 持续 348 MB/s - 1.39 GB/s
|
||||||
|
- **峰值突发(3倍):** 最高 21,000 个二维码/秒,4 GB/s
|
||||||
|
|
||||||
|
**解决方案:13 个存储节点 + 3 个控制/计算节点 + 1 个导入节点(JBOD + 3 副本,单机架)**
|
||||||
|
- **可用容量:** 2.29 PB(覆盖所有 110 亿个二维码,最大 200KB 大小)
|
||||||
|
- **原始容量:** 6.86 PB(24 × 22TB × 13 个存储节点)
|
||||||
|
- **聚合 IOPS:** ~125 万(每节点 96K × 13,足以满足顺序二维码存储)
|
||||||
|
- **聚合吞吐量:** 39 GB/s 读取,19.5 GB/s 写入(超过 1.39 GB/s 需求)
|
||||||
|
- **冗余:** 3 倍复制(JBOD,软件管理,可容忍 10 个节点故障)
|
||||||
|
- **硬件(联想 + 华为,推荐):**
|
||||||
|
- 13x 联想 ThinkSystem SR650 V2 存储节点(每个 24x 22TB SAS)
|
||||||
|
- 3x 联想 ThinkSystem SR630 V2 控制/计算节点(多用途)
|
||||||
|
- 1x 联想 ThinkSystem SR650 V2 导入节点(前面板热插拔:USB/NVMe/SATA/SAS)
|
||||||
|
- 2x 华为 CloudEngine 6800 交换机(48 端口 25GbE)
|
||||||
|
- **导入能力:** 10-32 GB/s 物理介质导入(无互联网瓶颈)
|
||||||
|
- **成本(中国,联想+华为):** ¥1,973,000 资本支出(约 $274K USD)
|
||||||
|
- **注意:** 不包括 UPS(超出项目范围)。价格基于联想中国和华为公开定价。
|
||||||
|
- **适合:** 单个 42U 机架(使用 35U,7U 用于扩展)
|
||||||
|
|
||||||
|
## 2. 硬件配置
|
||||||
|
|
||||||
|
**供应商:联想(推荐用于中国部署)**
|
||||||
|
- **服务器:** 联想 ThinkSystem SR650 V2 和 SR630 V2
|
||||||
|
- 全球品牌,在中国市场表现优异
|
||||||
|
- 比戴尔在中国市场便宜 20-25%
|
||||||
|
- 相同的 Intel Xeon CPU,企业级质量
|
||||||
|
- 公开定价:每个配置节点约 ¥95,000-120,000
|
||||||
|
- **网络:** 华为 CloudEngine 6800
|
||||||
|
- 在中国市场处于行业领先地位
|
||||||
|
- 公开定价:每台 48 端口 25GbE 交换机约 ¥90,000
|
||||||
|
- 与中国网络(中国电信/联通/移动)集成更好
|
||||||
|
- **管理:** 联想 XClarity Controller(IPMI/Redfish 标准)
|
||||||
|
- **支持:** 联想中国 4 小时现场响应
|
||||||
|
- **驱动器:** Seagate Exos 或 WD Ultrastar(中国组装,无进口增值税)
|
||||||
|
|
||||||
|
### 2.1 机架布局(单个 42U 标准机架)
|
||||||
|
|
||||||
|
**单机架完整部署(13 个存储 + 3 个控制/计算 + 1 个导入节点)**
|
||||||
|
|
||||||
|
- **U1-U2:** 2x 华为 CloudEngine 6800-48S-EI 交换机(48 端口 25GbE,冗余,CSS)
|
||||||
|
- **U3-U4:** 2x 国产 PDU(20x C13,4x C19 插座,208V 三相,A+B 供电)
|
||||||
|
- **U5-U30:** 13x 存储节点(每个 2U,联想 ThinkSystem SR650 V2,24x 22TB SAS)
|
||||||
|
- 2.29 PB 可用(覆盖所有 110 亿个二维码,最大 200KB 大小)
|
||||||
|
- **U31-U33:** 3x 控制/计算节点(每个 1U,联想 ThinkSystem SR630 V2,多用途)
|
||||||
|
- 运行:摄取工作器、API 服务器、负载均衡器、控制平面
|
||||||
|
- 所有服务在所有 3 个节点上运行以实现冗余
|
||||||
|
- **U34-U35:** 1x 导入节点(2U,联想 ThinkSystem SR650 V2,前面板热插拔用于物理介质)
|
||||||
|
- USB 3.2 / NVMe / SATA / SAS 热插拔插槽
|
||||||
|
- 直接物理介质导入(无网络瓶颈)
|
||||||
|
- **U36-U42:** 保留用于未来扩展(7U 可用)
|
||||||
|
|
||||||
|
**使用的总机架空间:42U 中的 35U**
|
||||||
|
|
||||||
|
|
||||||
|
### 2.2 网络设计
|
||||||
|
|
||||||
|
**机架顶部交换机:**
|
||||||
|
- 型号:华为 CloudEngine 6800-48S-EI(推荐)
|
||||||
|
- 48x 25GbE SFP28 端口 + 6x 100GbE QSFP28 上行链路端口
|
||||||
|
- 2x 冗余 PSU(AC/DC)
|
||||||
|
- 2x 冗余风扇
|
||||||
|
- VRP OS(华为通用路由平台)
|
||||||
|
- 公开定价:中国每台交换机约 ¥90,000
|
||||||
|
- 在中国市场处于行业领先地位,比戴尔便宜 25%
|
||||||
|
- 配置:
|
||||||
|
- CSS(集群交换系统)配对以实现冗余
|
||||||
|
- 所有服务器连接的 LACP 绑定(每节点 2x25GbE)
|
||||||
|
- VLAN:管理(VLAN10)、存储(VLAN20)、API(VLAN30)
|
||||||
|
- 启用巨型帧(MTU 9000)
|
||||||
|
|
||||||
|
**上行链路:**
|
||||||
|
- 2x 100GbE 光纤到核心/分发交换机
|
||||||
|
- ECMP 路由用于负载分发
|
||||||
|
|
||||||
|
**线缆:**
|
||||||
|
- DAC(直连铜缆)Twinax 用于机架内(< 5m)
|
||||||
|
- OM4 MMF 或 SMF 用于机架间连接
|
||||||
|
|
||||||
|
### 2.3 电源设计
|
||||||
|
|
||||||
|
**电源需求(单机架 - 带导入节点:13 个存储 + 3 个计算 + 1 个导入):**
|
||||||
|
- 存储节点:13 个节点 × 500W = 6,500W(HDD 比 NVMe 耗电少得多)
|
||||||
|
- 每个 HDD:~10W 空闲,~12W 活动
|
||||||
|
- 每节点 24 个 HDD:~300W
|
||||||
|
- CPU + RAM + 风扇:~200W
|
||||||
|
- 控制/计算节点:3 个节点 × 600W = 1,800W
|
||||||
|
- 运行所有服务的多用途节点
|
||||||
|
- CPU 利用率更高但已整合
|
||||||
|
- 导入节点:1 个节点 × 700W = 700W
|
||||||
|
- 由于 NVMe 暂存驱动器 + 多个控制器而功耗更高
|
||||||
|
- 导入操作期间的峰值
|
||||||
|
- 网络交换机:2 个交换机 × 400W = 800W
|
||||||
|
- **总计:~9,800W = 9.8 kW**
|
||||||
|
- **含 30% 余量:单机架 12.7 kW**
|
||||||
|
|
||||||
|
**电源摘要:**
|
||||||
|
- 存储节点:6.5 kW(13 个节点 × 500W)
|
||||||
|
- 控制/计算节点:1.8 kW(3 个节点 × 600W)
|
||||||
|
- 导入节点:0.7 kW(1 个节点 × 700W)
|
||||||
|
- 网络交换机:0.8 kW(2 个交换机 × 400W)
|
||||||
|
- **总计:9.8 kW(含 30% 余量为 12.7 kW)**
|
||||||
|
|
||||||
|
**PDU 配置(单机架 - 13 个存储节点):**
|
||||||
|
- 2x 国产计量机架 PDU(冗余供电,A+B 电源)
|
||||||
|
- 输入:208V 三相,每个 PDU 30A
|
||||||
|
- 容量:208V × 30A × 1.732 = 每个 PDU 10.8 kVA(三相)
|
||||||
|
- 总计:机架 21.6 kVA(足以满足 12.7 kW 负载,有余量)
|
||||||
|
- 每个服务器:双 PSU 连接到单独的 PDU(A+B 供电)
|
||||||
|
|
||||||
|
|
||||||
|
**冷却(单机架 - 带导入节点,13 个存储节点):**
|
||||||
|
- 散热:12.7 kW × 3.41 = 43,307 BTU/hr(单机架)
|
||||||
|
- 需求:43,400 BTU/hr 的冷却容量(~3.6 吨)
|
||||||
|
- 选项:
|
||||||
|
- 使用现有 IT 机房 CRAC/HVAC(如果容量可用)
|
||||||
|
- 添加 1x 20kW 行内冷却单元(¥40,000-50,000)
|
||||||
|
- 标准精密空调单元(4-5 吨容量)
|
||||||
|
- 单机架无需特殊围护
|
||||||
|
|
||||||
|
### 2.4 存储节点规格
|
||||||
|
|
||||||
|
**存储容量规划:**
|
||||||
|
- 最大需求:每年 2.2 PB
|
||||||
|
- 复制策略影响原始容量需求:
|
||||||
|
- RAID 10 + 2 倍复制:4.4 PB 原始(复杂,双层冗余)
|
||||||
|
- JBOD + 3 倍复制:6.6 PB 原始(更简单,仅软件冗余)
|
||||||
|
|
||||||
|
**架构选择:JBOD vs RAID**
|
||||||
|
|
||||||
|
**JBOD + 3 副本(推荐,软件简单):**
|
||||||
|
- 无 RAID 控制器复杂性
|
||||||
|
- 软件处理所有冗余(Ceph、MinIO 等)
|
||||||
|
- 每个驱动器独立,故障恢复更容易
|
||||||
|
- 更好的性能可观测性
|
||||||
|
- 分布式存储的标准方法
|
||||||
|
- 更容易重新平衡和维护
|
||||||
|
|
||||||
|
**RAID 10 + 2 副本(传统方法):**
|
||||||
|
- 硬件 RAID 提供本地冗余
|
||||||
|
- 更低的网络复制流量
|
||||||
|
- 更快的本地读取(RAID 条带化)
|
||||||
|
- 更复杂的故障场景(RAID + 集群)
|
||||||
|
- 驱动器故障时的 RAID 重建开销
|
||||||
|
|
||||||
|
**存储节点:13 个节点,22TB SAS HDD + JBOD + 3 副本(覆盖所有 110 亿个二维码)**
|
||||||
|
|
||||||
|
**型号:联想 ThinkSystem SR650 V2(2U,24x 2.5" 驱动器插槽)**
|
||||||
|
**公开定价:中国每个配置节点约 ¥95,000-105,000**
|
||||||
|
|
||||||
|
**每个存储节点:**
|
||||||
|
- **CPU:** 2x Intel Xeon Silver 4316(20 核,2.3 GHz,30MB 缓存)
|
||||||
|
- 总计:每节点 40 核,80 线程
|
||||||
|
- 零件号:4XG7A42589
|
||||||
|
- **RAM:** 256 GB DDR4-3200 ECC RDIMM(8x 32GB)
|
||||||
|
- 零件号:4X77A08633 或同等产品
|
||||||
|
- **启动:** 2x 480GB SATA SSD(RAID 1,操作系统)
|
||||||
|
- 联想 2.5" 6Gb SATA SSD
|
||||||
|
- **存储:** 24x 22TB SAS HDD 12Gbps 7.2K RPM
|
||||||
|
- Seagate Exos X22(ST22000NM00 系列)或 WD Ultrastar
|
||||||
|
- 公开定价:每个驱动器约 ¥2,800-3,200(约 $390-445)
|
||||||
|
- JBOD 配置(无 RAID,所有驱动器独立)
|
||||||
|
- 每节点:528 TB 原始(24 × 22TB)
|
||||||
|
- 总集群:6.86 PB 原始(13 个节点 × 528 TB)
|
||||||
|
- 3 倍复制:2.29 PB 可用
|
||||||
|
- 性能:每驱动器 4K IOPS,每节点 ~96K IOPS,每节点 1.5 GB/s
|
||||||
|
- **网络:** 2x 联想 ThinkSystem Broadcom 57504 25GbE(4 端口,绑定)
|
||||||
|
- **HBA:** 联想 ThinkSystem 430-8i SAS/SATA 12Gb HBA(直通模式)
|
||||||
|
- **PSU:** 2x 1100W 铂金(冗余,208V)
|
||||||
|
- **机架空间:** 13 个节点 × 2U = 26U
|
||||||
|
|
||||||
|
### 2.5 导入节点规格(1 个节点,专用于物理介质导入)
|
||||||
|
|
||||||
|
**型号:联想 ThinkSystem SR650 V2(2U,前面板可访问热插拔)**
|
||||||
|
**公开定价:每个配置节点约 ¥140,000**
|
||||||
|
|
||||||
|
**每个节点:**
|
||||||
|
- **CPU:** 2x Intel Xeon Gold 6338(32 核,2.0 GHz,48MB 缓存)
|
||||||
|
- 总计:64 核,128 线程
|
||||||
|
- 高核数用于并行导入处理
|
||||||
|
- **RAM:** 256 GB DDR4-3200 ECC
|
||||||
|
- 用于暂存导入数据的大缓冲区
|
||||||
|
- **启动:** 2x 480GB SATA SSD(RAID 1,操作系统)
|
||||||
|
- **导入暂存存储:** 4x 7.68TB NVMe(JBOD)
|
||||||
|
- 总计:~31TB 暂存缓冲区
|
||||||
|
- 复制到存储集群之前的高速本地缓存
|
||||||
|
- **热插拔插槽(前面板):** 12 个插槽支持:
|
||||||
|
- **USB 3.2 Gen 2x2:** 4x 前面板 USB-C 端口(每个 2.5 GB/s)
|
||||||
|
- **NVMe U.2:** 4x 热插拔 NVMe 插槽(每个驱动器最高 8 GB/s)
|
||||||
|
- **SATA/SAS:** 4x 热插拔 3.5" 插槽(12 Gbps SAS)
|
||||||
|
- **控制器:**
|
||||||
|
- 联想 ThinkSystem 430-8i SAS/SATA HBA(直通模式)
|
||||||
|
- PCIe Gen4 NVMe 交换机
|
||||||
|
- USB 3.2 Gen 2x2 控制器
|
||||||
|
- **网络:** 2x 25GbE(绑定,LACP)
|
||||||
|
- **PSU:** 2x 1100W 铂金
|
||||||
|
|
||||||
|
**导入工作流程:**
|
||||||
|
1. 操作员将物理介质(USB、NVMe、SATA、SAS)插入前面板
|
||||||
|
2. 驱动器自动挂载或热检测
|
||||||
|
3. 导入软件从介质读取数据 → 暂存 NVMe(快速本地)
|
||||||
|
4. 并行处理/验证二维码(32-48 核)
|
||||||
|
5. 通过 25GbE(或 100GbE)将验证后的数据流式传输到存储集群
|
||||||
|
6. 导入完成后操作员移除介质(LED 指示灯)
|
||||||
|
|
||||||
|
**性能:**
|
||||||
|
- **USB 3.2:** 4 端口 × 2.5 GB/s = 10 GB/s 聚合
|
||||||
|
- **NVMe:** 4 驱动器 × 8 GB/s = 32 GB/s 聚合
|
||||||
|
- **SATA/SAS:** 4 驱动器 × 1.5 GB/s = 6 GB/s 聚合
|
||||||
|
- **网络:** 2x 25GbE = 6.25 GB/s 到存储集群
|
||||||
|
- 暂存 NVMe:31TB 缓冲区允许在网络传输之前进行离线处理
|
||||||
|
|
||||||
|
**优势:**
|
||||||
|
- **无互联网/WiFi 瓶颈:** 以全速直接物理介质导入
|
||||||
|
- **并行导入:** 同时接受最多 12 个驱动器
|
||||||
|
- **灵活介质:** 支持 USB、NVMe、SATA、SAS
|
||||||
|
- **高吞吐量:** 本地暂存消除了读取期间的网络瓶颈
|
||||||
|
- **操作员友好:** 前面板访问、LED 指示灯、热插拔安全
|
||||||
|
|
||||||
|
### 2.6 控制/计算节点规格(3 个节点,整合)
|
||||||
|
|
||||||
|
**型号:联想 ThinkSystem SR630 V2(1U,多用途)**
|
||||||
|
**公开定价:每个配置节点约 ¥102,000**
|
||||||
|
|
||||||
|
**每个节点(运行所有服务):**
|
||||||
|
- **CPU:** 2x Intel Xeon Gold 6338(32 核,2.0 GHz)
|
||||||
|
- 总计:每节点 64 核,128 线程
|
||||||
|
- 足以满足摄取 + API + 控制平面 + 负载均衡
|
||||||
|
- **RAM:** 256 GB DDR4-3200 ECC(8x 32GB)
|
||||||
|
- 分配:128GB 用于摄取,64GB 用于 API,64GB 用于系统/控制
|
||||||
|
- **启动:** 2x 480GB SATA SSD(RAID 1,操作系统)
|
||||||
|
- **本地存储:** 2x 3.84TB NVMe
|
||||||
|
- 摄取写入缓冲区 + API 读取缓存 + 监控数据
|
||||||
|
- **网络:** 2x 联想 ThinkSystem Broadcom 57504 25GbE(绑定,LACP)
|
||||||
|
- **PSU:** 2x 800W 铂金
|
||||||
|
|
||||||
|
## 3. 网络拓扑
|
||||||
|
```
|
||||||
|
核心交换机(100GbE 上行链路)
|
||||||
|
↓
|
||||||
|
机架 ToR 交换机(华为 CSS 配对,25GbE 到服务器)
|
||||||
|
↓
|
||||||
|
联想服务器(双归属,LACP 绑定)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. 成本估算(近似)
|
||||||
|
|
||||||
|
**硬件(单机架 - 13 个存储 + 3 个控制/计算 + 1 个导入,完整 2.29 PB):**
|
||||||
|
- 存储节点(13x):$195K
|
||||||
|
- 控制/计算节点(3x):$48K
|
||||||
|
- 导入节点(1x):$22K
|
||||||
|
- 网络交换机(2x):$30K
|
||||||
|
- 其他硬件和基础设施:$13K
|
||||||
|
- **总计:$308K 资本支出**
|
||||||
|
- **注意:** 不包括 UPS(超出项目范围)
|
||||||
|
|
||||||
|
|
||||||
|
**电源需求:**
|
||||||
|
- **总机架功率:** 最大 9.5kW
|
||||||
|
- 13x 存储节点 @ 每个 400W = 5.2kW
|
||||||
|
- 3x 控制/计算节点 @ 每个 300W = 0.9kW
|
||||||
|
- 1x 导入节点 @ 500W = 0.5kW
|
||||||
|
- 2x 交换机 @ 每个 400W = 0.8kW
|
||||||
|
- 余量:~1.6kW 储备
|
||||||
|
- **PDU 需求:** 2x 5kW PDU(A+B 供电)
|
||||||
|
- **冷却需求:** ~12kW 热(9.5kW × 1.3 PUE)
|
||||||
|
|
||||||
|
## 5. 冗余和高可用性(单机架,13 个存储节点)
|
||||||
|
|
||||||
|
- **存储:** JBOD + 3 倍复制分布在 13 个联想 ThinkSystem SR650 V2 节点上
|
||||||
|
- **任何 10 个存储节点可同时故障而不会丢失数据(77% 冗余)**
|
||||||
|
- 312 个独立驱动器(每节点 24 个 × 13),软件管理
|
||||||
|
- 单个驱动器故障:自动重新平衡到其他节点
|
||||||
|
- 无 RAID 重建开销 - 软件处理恢复
|
||||||
|
- 驱动器恢复:~22TB 通过 25GbE = ~2 小时(vs RAID 重建需要数天)
|
||||||
|
- 数据条带化和复制用于并行 I/O
|
||||||
|
- 需要最少 4 个节点以维持数据可用性(3 倍复制)
|
||||||
|
- **控制/计算:** 3 个多用途节点(主动-主动-主动)
|
||||||
|
- 所有 3 个节点运行:摄取工作器、API 服务器、负载均衡器、控制平面
|
||||||
|
- 3 个节点中的任何 2 个可以处理完整工作负载(N+1 冗余)
|
||||||
|
- 可以丢失 1 个控制/计算节点而不会中断服务
|
||||||
|
- 通过 DNS 轮询或浮动 VIP(Keepalived)进行负载均衡
|
||||||
|
- **网络:** 华为 VLT/CSS 交换机,双归属服务器(2x25GbE 绑定)
|
||||||
|
- **电源:** 每个服务器双 PSU,冗余 PDU(A+B 供电)
|
||||||
|
|
||||||
|
**扩展路径(如果未来需求增加):**
|
||||||
|
- 第 1 年:13 个存储 + 3 个控制/计算 + 1 个导入 = 2.29 PB 可用(覆盖所有 110 亿个二维码)
|
||||||
|
- 未来扩展:机架中 7U 可用 = 最多 3 个更多 2U 存储节点
|
||||||
|
- 16 个存储节点:2.82 PB 可用(容量增加 23%)
|
||||||
|
- 超过 16 个节点:需要第二个机架或更高容量的驱动器
|
||||||
|
|
||||||
|
## 6. 性能验证(基于 HDD,单机架)
|
||||||
|
|
||||||
|
**目标指标(13 个存储节点):**
|
||||||
|
- 写入吞吐量:持续 6,960 个二维码/秒(1.39 GB/s)
|
||||||
|
- 3 倍复制:4.17 GB/s 网络写入流量
|
||||||
|
- HDD 能力:19.5 GB/s 聚合写入(13 个节点 × 1.5 GB/s)✓ **优秀**
|
||||||
|
- 峰值突发:21,000 个二维码/秒(4.2 GB/s)
|
||||||
|
- 3 倍复制:12.6 GB/s 网络写入流量
|
||||||
|
- HDD 能力:19.5 GB/s 聚合写入(峰值)✓ **优秀**(55% 余量)
|
||||||
|
- 读取延迟:< 50ms P99(带缓存)✓
|
||||||
|
- HDD 寻道:4-8ms,顺序:快速
|
||||||
|
- 存储 IOPS:125 万聚合(每节点 96K × 13,每驱动器 4K)
|
||||||
|
- 足以满足大型顺序二维码写入(每个 50-200 KB)
|
||||||
|
- 二维码是大块数据,不是小随机 I/O
|
||||||
|
- 网络吞吐量:650 Gbps 聚合(每节点 50 Gbps × 13)
|
||||||
|
- 网络利用率:持续写入期间 < 8%,峰值突发期间 < 24%
|
||||||
|
- 存储容量:2.29 PB 可用(3 倍复制为 6.86 PB 原始)
|
||||||
|
- **覆盖所有 110 亿个二维码,最大 200KB 大小**
|
||||||
|
- 比 2.2 PB 最大需求高 4% 的缓冲区
|
||||||
|
- 驱动器故障恢复:分布在所有健康节点上
|
||||||
|
- 冗余:可容忍最多 10 个存储节点故障(77% 冗余)
|
||||||
|
|
||||||
|
**HDD 性能特征:**
|
||||||
|
- 顺序吞吐量:优秀(每驱动器 150-250 MB/s)
|
||||||
|
- 随机 IOPS:低于 NVMe(4K IOPS vs 100K+ IOPS)
|
||||||
|
- 二维码工作负载:主要是顺序大块写入(50-200 KB)
|
||||||
|
- **结论:** HDD 非常适合此工作负载(大顺序 I/O)
|
||||||
|
|
||||||
|
## 7. 推荐配置摘要(13 个存储节点,完整容量)
|
||||||
|
|
||||||
|
**配置:13 个存储 + 3 个控制/计算 + 1 个导入(JBOD + 3 副本,单机架)**
|
||||||
|
|
||||||
|
| 指标 | 规格 |
|
||||||
|
|--------|---------------|
|
||||||
|
| **存储服务器** | 13x 联想 ThinkSystem SR650 V2(每个 2U)|
|
||||||
|
| **控制/计算** | 3x 联想 ThinkSystem SR630 V2(每个 1U)|
|
||||||
|
| **导入节点** | 1x 联想 ThinkSystem SR650 V2(2U)|
|
||||||
|
| **交换机** | 2x 华为 CE6800(25GbE)|
|
||||||
|
| **每个存储节点的驱动器** | 24x 22TB SAS(12Gbps,7.2K RPM)|
|
||||||
|
| **可用容量** | **2.29 PB**(覆盖所有 110 亿个二维码,最大 200KB)|
|
||||||
|
| **原始容量** | 6.86 PB(3 倍复制)|
|
||||||
|
| **机架空间** | 42U 中的 35U(7U 扩展可用)|
|
||||||
|
| **聚合 IOPS** | 125 万(每个存储节点 96K)|
|
||||||
|
| **聚合吞吐量** | 39 GB/s 读取,19.5 GB/s 写入 |
|
||||||
|
| **电源** | 12.7 kW(9.8 kW 实际,30% 余量)|
|
||||||
|
| **资本支出(中国,联想+华为)** | ¥1,973,000(约 $274K USD)|
|
||||||
|
| **存储冗余** | 可丢失 10 个存储节点(77% 冗余)|
|
||||||
|
| **计算冗余** | N+1(3 个节点中的任何 2 个)|
|
||||||
|
| **导入速度** | 10-32 GB/s(物理介质)|
|
||||||
|
| **驱动器可靠性** | SAS 企业级 |
|
||||||
|
| **扩展** | 7U 可用 = 最多 3 个更多存储节点 |
|
||||||
|
|
||||||
|
**关键特性:**
|
||||||
|
- ✅ **完整容量:** 2.29 PB 覆盖所有 110 亿个二维码,最大 200KB 大小
|
||||||
|
- ✅ **高冗余:** 可容忍 10 个同时存储节点故障
|
||||||
|
- ✅ **单机架:** 所有设备在一个 42U 机架中(使用 35U)
|
||||||
|
- ✅ **国产品牌:** 比戴尔便宜 20%,优秀的本地支持
|
||||||
|
- ✅ **物理导入:** 通过 USB/NVMe/SATA/SAS 热插拔 10-32 GB/s
|
||||||
|
- ✅ **行业标准:** JBOD + 3 副本架构
|
||||||
|
|
||||||
|
## 10. 中国私有数据中心成本估算(人民币)
|
||||||
|
|
||||||
|
**汇率:1 USD = 7.2 CNY(近似,2026 年 1 月)**
|
||||||
|
|
||||||
|
**中国摘要(13 个存储节点,完整容量,IT 机房):**
|
||||||
|
- **推荐:** 联想 ThinkSystem + 华为 CE6800
|
||||||
|
- **资本支出:** ¥1,973,000(约 $274K USD,基于公开定价)
|
||||||
|
- **容量:** 2.29 PB 可用(覆盖所有 110 亿个二维码,最大 200KB 大小)
|
||||||
|
- **电源需求:** 总计 12.7 kW(9.8 kW 实际 + 30% 余量)
|
||||||
|
- **导入节点优势:** 10-32 GB/s 物理介质导入(USB/NVMe/SATA/SAS)
|
||||||
|
- 消除互联网/WiFi 瓶颈
|
||||||
|
- 前面板热插拔便于操作员使用
|
||||||
|
- 31TB 暂存缓冲区用于离线处理
|
||||||
|
|
||||||
|
### 中国定价 - 联想 ThinkSystem + 华为配置
|
||||||
|
|
||||||
|
**硬件(联想 + 华为 - 13 个存储节点,2.29 PB):**
|
||||||
|
- 存储节点(13x 联想 ThinkSystem SR650 V2,24x 22TB SAS):¥1,235,000
|
||||||
|
- 基础服务器:每节点约 ¥35,000
|
||||||
|
- 24x 22TB SAS 驱动器 @ 每个 ¥3,000:每节点 ¥72,000
|
||||||
|
- CPU、RAM、HBA 升级:每节点约 ¥23,000
|
||||||
|
- 每节点总计:¥95,000
|
||||||
|
- 公开定价参考:联想中国网站
|
||||||
|
- 控制/计算节点(3x 联想 ThinkSystem SR630 V2):¥306,000
|
||||||
|
- 每个节点:¥102,000(约 $14,200)
|
||||||
|
- 多用途:摄取 + API + 负载均衡 + 控制
|
||||||
|
- 导入节点(1x 联想 ThinkSystem SR650 V2,带热插拔):¥140,000
|
||||||
|
- 前面板 USB-C、NVMe U.2、SATA/SAS 热插拔
|
||||||
|
- 网络交换机(2x 华为 CloudEngine 6800-48S-EI):¥180,000
|
||||||
|
- 公开定价:每台交换机 ¥90,000
|
||||||
|
- PDU、线缆、杂项:¥64,000
|
||||||
|
- **硬件小计(联想 + 华为):¥1,925,000**
|
||||||
|
|
||||||
|
**基础设施(如果 IT 机房不存在):**
|
||||||
|
- 机架(42U,国产):¥8,000
|
||||||
|
- 冷却(1x 20kW 行内,如果 IT 机房 HVAC 不足):¥40,000
|
||||||
|
- **基础设施小计:¥48,000**
|
||||||
|
- **注意:** 可能在现有 IT 机房中部分/完全可用(相应减少资本支出)
|
||||||
|
|
||||||
|
**总资本支出(13 个存储节点,完整 2.29 PB):**
|
||||||
|
- **联想 + 华为:¥1,973,000**(约 $274K USD,推荐)
|
||||||
|
- **注意:** 不包括 UPS(超出项目范围)。价格基于联想中国公开定价和华为企业报价。
|
||||||
|
|
||||||
|
**电源需求(13 个存储节点):**
|
||||||
|
- **总功率:** 12.7 kW
|
||||||
|
- 13x 存储节点 @ 每个 400W = 5.2 kW
|
||||||
|
- 3x 控制/计算节点 @ 每个 500W = 1.5 kW
|
||||||
|
- 1x 导入节点 @ 600W = 0.6 kW
|
||||||
|
- 2x 交换机 @ 每个 400W = 0.8 kW
|
||||||
|
- 开销和余量 = 4.6 kW
|
||||||
|
- **冷却需求:** 16.5 kW 热(12.7 kW × 1.3 PUE)
|
||||||
|
- **电路需求:** 最少 2x 32A 三相 208V 电路
|
||||||
|
|
||||||
|
### 中国 HDD 采购
|
||||||
|
|
||||||
|
**推荐:本地组装的企业级驱动器**
|
||||||
|
- Seagate Exos X22 22TB(苏州工厂):每个驱动器 ¥2,800-3,200
|
||||||
|
- Western Digital Ultrastar DC HC570 22TB(上海/深圳):每个驱动器 ¥2,900-3,300
|
||||||
|
- 本地组装 = 无进口增值税(节省 13%)
|
||||||
|
- 提供增值税发票用于税务抵扣
|
||||||
|
- 与国际版本相同的保修
|
||||||
|
- 更快的更换(本地库存)
|
||||||
|
|
||||||
|
### 快速参考:中国定价摘要(13 个存储节点,2.29 PB)
|
||||||
|
|
||||||
|
| 项目 | 价值 |
|
||||||
|
|------|------|
|
||||||
|
| **资本支出** | ¥1,973,000(约 $274K USD)|
|
||||||
|
| **容量** | 2.29 PB 可用 |
|
||||||
|
| **电源** | 总计 12.7 kW |
|
||||||
|
| **机架空间** | 42U 中的 35U |
|
||||||
|
|
||||||
|
**联想 ThinkSystem SR650 V2 + SR630 V2 + 华为 CE6800**
|
||||||
|
- 覆盖所有 110 亿个二维码,最大 200KB 大小
|
||||||
|
- 导入节点:10-32 GB/s 物理介质导入(无网络瓶颈)
|
||||||
|
- 不包括 UPS(超出项目范围)
|
||||||
|
|
||||||
|
**中国部署建议(IT 机房,13 个存储节点):**
|
||||||
|
1. **使用联想 ThinkSystem + 华为** - 公开定价可用,比戴尔节省 20%
|
||||||
|
2. **IT 机房需求(13 个存储节点,完整 2.29 PB):**
|
||||||
|
- 电源:需要 12.7 kW 容量(208V 三相,30A × 2 PDU)
|
||||||
|
- 冷却:43,400 BTU/hr 散热(~3.6 吨,12.7 kW × 3,412 BTU/kW)
|
||||||
|
- 地板承重:~1,100 kg 总重量(17 台服务器 + 交换机 + 机架)
|
||||||
|
- 导入节点的操作员访问(前面板热插拔)
|
||||||
|
- 机架空间:使用 42U 中的 35U(7U 用于未来扩展)
|
||||||
|
3. **本地支持合同** 必不可少(联想 + 华为在中国有优秀的支持,4 小时响应)
|
||||||
|
5. **批量采购折扣** - 大订单协商 10-15% 折扣
|
||||||
|
6. **付款条件:** Net 30-90 常见,一些供应商提供 6-12 个月融资
|
||||||
|
7. **增值税发票(增值税发票):** 税务抵扣必不可少,确保所有供应商提供
|
||||||
|
8. **国产驱动器:** 从中国工厂购买 Seagate/WD 以避免进口增值税
|
||||||
|
9. **网络集成:** 规划到现有企业网络的上行链路
|
||||||
|
|
||||||
|
**供应商联系(中国):**
|
||||||
|
- 联想:DCG(数据中心集团)- https://www.lenovo.com/cn/zh/data-center(联想数据中心)
|
||||||
|
- ThinkSystem 服务器在网站上有公开定价
|
||||||
|
- 联系:400-100-6000(中国热线)
|
||||||
|
- 华为:企业网络部门 - https://e.huawei.com(华为企业)
|
||||||
|
- CloudEngine 交换机需要企业报价
|
||||||
|
- 联系本地华为客户经理
|
||||||
|
- 本地集成商:通常比直接购买便宜 5-10%(例如,神州数码 Digital China)
|
||||||
|
|
||||||
|
## 11. 最终建议
|
||||||
|
|
||||||
|
**推荐配置:联想 ThinkSystem + 华为在二线城市**
|
||||||
|
|
||||||
|
**硬件(13 个存储节点,完整 2.29 PB 容量):**
|
||||||
|
- **13x 联想 ThinkSystem SR650 V2** 存储节点(每个 24x 22TB SAS)= **2.29 PB 可用**
|
||||||
|
- **3x 联想 ThinkSystem SR630 V2** 控制/计算节点(多用途)
|
||||||
|
- **1x 联想 ThinkSystem SR650 V2** 导入节点(前面板热插拔)
|
||||||
|
- **2x 华为 CloudEngine 6800-48S-EI** 交换机(48 端口 25GbE)
|
||||||
|
|
||||||
|
**容量:** 2.29 PB 可用 - 覆盖所有 110 亿个二维码,最大 200KB 大小
|
||||||
|
|
||||||
|
**成本:**
|
||||||
|
- **资本支出:** ¥1,973,000(约 $274K USD)
|
||||||
|
- **注意:** 不包括 UPS(超出项目范围)。基于联想中国和华为公开定价。
|
||||||
|
|
||||||
|
**电源需求:**
|
||||||
|
- **总计:** 12.7 kW(9.8 kW 实际 + 30% 余量)
|
||||||
|
- **冷却:** 43,400 BTU/hr(~3.6 吨)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
1
emblem5/.gitignore
vendored
@ -5,3 +5,4 @@
|
|||||||
/.ipynb_checkpoints
|
/.ipynb_checkpoints
|
||||||
/reports
|
/reports
|
||||||
/dataset
|
/dataset
|
||||||
|
/wandb
|
||||||
|
|||||||
@ -28,6 +28,9 @@ scans_dir = os.path.join(data_dir, 'scans')
|
|||||||
|
|
||||||
os.makedirs(local_model_dir, exist_ok=True)
|
os.makedirs(local_model_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# Cache for loaded models to avoid reloading on every request
|
||||||
|
_model_cache = {}
|
||||||
|
|
||||||
def get_file_md5(fname):
|
def get_file_md5(fname):
|
||||||
return hashlib.md5(open(fname, 'rb').read()).hexdigest()
|
return hashlib.md5(open(fname, 'rb').read()).hexdigest()
|
||||||
|
|
||||||
@ -83,8 +86,12 @@ def find_best_frame(files):
|
|||||||
def do_qr_verify():
|
def do_qr_verify():
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
fd = flask.request.form
|
fd = flask.request.form
|
||||||
model_path, model_md5 = download_model(fd.get('model', default_model))
|
model_name = fd.get('model', default_model)
|
||||||
model, transforms = load_model(model_path)
|
model_path, model_md5 = download_model(model_name)
|
||||||
|
# Cache models by path to avoid reloading on every request
|
||||||
|
if model_path not in _model_cache:
|
||||||
|
_model_cache[model_path] = load_model(model_path)
|
||||||
|
model, transforms = _model_cache[model_path]
|
||||||
|
|
||||||
frame_image, clarities = find_best_frame(flask.request.files)
|
frame_image, clarities = find_best_frame(flask.request.files)
|
||||||
frame_qrcode, _ = extract_qr(frame_image)
|
frame_qrcode, _ = extract_qr(frame_image)
|
||||||
@ -110,6 +117,7 @@ def do_qr_verify():
|
|||||||
"probabilities": ', '.join([f'{v:.2%}' for k, v in probabilities]),
|
"probabilities": ', '.join([f'{v:.2%}' for k, v in probabilities]),
|
||||||
"clarities": clarities,
|
"clarities": clarities,
|
||||||
"model_md5": model_md5,
|
"model_md5": model_md5,
|
||||||
|
"model_name": model_name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -471,7 +471,7 @@ def main():
|
|||||||
parser.add_argument('--scheduler-min-lr', type=float, default=1e-6, help='Minimum learning rate for ReduceLROnPlateau scheduler (default: 1e-6)')
|
parser.add_argument('--scheduler-min-lr', type=float, default=1e-6, help='Minimum learning rate for ReduceLROnPlateau scheduler (default: 1e-6)')
|
||||||
parser.add_argument('--scan-ids', type=str, default=None, help='Filter scan IDs by range (e.g., "357193-358808" or "357193-358808,359000-359010")')
|
parser.add_argument('--scan-ids', type=str, default=None, help='Filter scan IDs by range (e.g., "357193-358808" or "357193-358808,359000-359010")')
|
||||||
parser.add_argument('--model', type=str, default=None, help='Path to model file to load for finetuning')
|
parser.add_argument('--model', type=str, default=None, help='Path to model file to load for finetuning')
|
||||||
parser.add_argument('--wandb-project', type=str, default='euphon/themblem', help='W&B project name (default: euphon/themblem)')
|
parser.add_argument('--wandb-project', type=str, default='themblem', help='W&B project name (default: themblem)')
|
||||||
parser.add_argument('--wandb-name', type=str, default=None, help='W&B run name (default: auto-generated)')
|
parser.add_argument('--wandb-name', type=str, default=None, help='W&B run name (default: auto-generated)')
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,7 @@ torch
|
|||||||
torchvision
|
torchvision
|
||||||
kornia
|
kornia
|
||||||
opencv-contrib-python
|
opencv-contrib-python
|
||||||
|
wandb
|
||||||
|
|
||||||
# RAG dependencies
|
# RAG dependencies
|
||||||
langchain>=0.1.0
|
langchain>=0.1.0
|
||||||
|
|||||||
2
web/public/camera-6.0/Makefile
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
deploy:
|
||||||
|
rsync -zrP * oci:/data/emblem-camera/
|
||||||
85
web/public/camera-6.0/css/loading.css
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
.lds-roller {
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
}
|
||||||
|
.lds-roller div {
|
||||||
|
animation: lds-roller 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
|
||||||
|
transform-origin: 40px 40px;
|
||||||
|
}
|
||||||
|
.lds-roller div:after {
|
||||||
|
content: " ";
|
||||||
|
display: block;
|
||||||
|
position: absolute;
|
||||||
|
width: 7px;
|
||||||
|
height: 7px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #666;
|
||||||
|
margin: -4px 0 0 -4px;
|
||||||
|
}
|
||||||
|
.lds-roller div:nth-child(1) {
|
||||||
|
animation-delay: -0.036s;
|
||||||
|
}
|
||||||
|
.lds-roller div:nth-child(1):after {
|
||||||
|
top: 63px;
|
||||||
|
left: 63px;
|
||||||
|
}
|
||||||
|
.lds-roller div:nth-child(2) {
|
||||||
|
animation-delay: -0.072s;
|
||||||
|
}
|
||||||
|
.lds-roller div:nth-child(2):after {
|
||||||
|
top: 68px;
|
||||||
|
left: 56px;
|
||||||
|
}
|
||||||
|
.lds-roller div:nth-child(3) {
|
||||||
|
animation-delay: -0.108s;
|
||||||
|
}
|
||||||
|
.lds-roller div:nth-child(3):after {
|
||||||
|
top: 71px;
|
||||||
|
left: 48px;
|
||||||
|
}
|
||||||
|
.lds-roller div:nth-child(4) {
|
||||||
|
animation-delay: -0.144s;
|
||||||
|
}
|
||||||
|
.lds-roller div:nth-child(4):after {
|
||||||
|
top: 72px;
|
||||||
|
left: 40px;
|
||||||
|
}
|
||||||
|
.lds-roller div:nth-child(5) {
|
||||||
|
animation-delay: -0.18s;
|
||||||
|
}
|
||||||
|
.lds-roller div:nth-child(5):after {
|
||||||
|
top: 71px;
|
||||||
|
left: 32px;
|
||||||
|
}
|
||||||
|
.lds-roller div:nth-child(6) {
|
||||||
|
animation-delay: -0.216s;
|
||||||
|
}
|
||||||
|
.lds-roller div:nth-child(6):after {
|
||||||
|
top: 68px;
|
||||||
|
left: 24px;
|
||||||
|
}
|
||||||
|
.lds-roller div:nth-child(7) {
|
||||||
|
animation-delay: -0.252s;
|
||||||
|
}
|
||||||
|
.lds-roller div:nth-child(7):after {
|
||||||
|
top: 63px;
|
||||||
|
left: 17px;
|
||||||
|
}
|
||||||
|
.lds-roller div:nth-child(8) {
|
||||||
|
animation-delay: -0.288s;
|
||||||
|
}
|
||||||
|
.lds-roller div:nth-child(8):after {
|
||||||
|
top: 56px;
|
||||||
|
left: 12px;
|
||||||
|
}
|
||||||
|
@keyframes lds-roller {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
464
web/public/camera-6.0/css/main.css
Normal file
@ -0,0 +1,464 @@
|
|||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family:Tahoma,Verdana,STHeiTi,simsun,sans-serif;
|
||||||
|
}
|
||||||
|
div.debug {
|
||||||
|
position: absolute;
|
||||||
|
left: 5px;
|
||||||
|
bottom: 5px;
|
||||||
|
width: 80%;
|
||||||
|
height: 50%;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 2px dashed rgba(50, 255, 50, 0.5);
|
||||||
|
background-color: rgba(0, 50, 0, 0.7);
|
||||||
|
padding: 1rem;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 12px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
.hidden {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
video.preview {
|
||||||
|
object-fit: cover;
|
||||||
|
position: fixed;
|
||||||
|
top: -1%;
|
||||||
|
left: -1%;
|
||||||
|
width: 102%;
|
||||||
|
height: 102%;
|
||||||
|
z-index: -1000;
|
||||||
|
border: 1px solid yellow;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
div.debug canvas {
|
||||||
|
display: inline-block;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
div.bottomfixed {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
bottom: 0;
|
||||||
|
height: 90px;
|
||||||
|
background-color: #171616;
|
||||||
|
text-align: center;
|
||||||
|
border-top: 1px solid #ef4823;
|
||||||
|
}
|
||||||
|
|
||||||
|
.play.button {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.1rem 1.2rem 0.1rem 1.4rem;
|
||||||
|
margin-right: 0.2rem;
|
||||||
|
border-radius: 6px;
|
||||||
|
background-color: #707070;
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.bottomfixed .action.highlight .play.button {
|
||||||
|
background-color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.bottomfixed .action img {
|
||||||
|
height: 14px;
|
||||||
|
width: 36px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.bottomfixed .action {
|
||||||
|
margin-top: 20px;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: #707070;
|
||||||
|
width: 49%;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.bottomfixed .action.highlight {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.camoverlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
clear: both;
|
||||||
|
font-size: 0;
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
top: 18vw;
|
||||||
|
}
|
||||||
|
div.camoverlay img.qrmarkers {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
margin: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.lower.text {
|
||||||
|
top: 4%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.progress {
|
||||||
|
width: 70%;
|
||||||
|
margin: 0.2rem 0 0 0;
|
||||||
|
height: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: #eee;
|
||||||
|
font-size: 18px;
|
||||||
|
color: black;
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
div.progress-text {
|
||||||
|
color: #333;
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
}
|
||||||
|
div.progress-bar {
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: #ef4823;
|
||||||
|
width: 0%;
|
||||||
|
height: 100%;
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
div.upper.overlay {
|
||||||
|
position: relative;
|
||||||
|
height: 100vmin;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.lower.overlay {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
div.debug .buttons {
|
||||||
|
margin: 0 0 0.3rem 0;
|
||||||
|
}
|
||||||
|
div.debug .buttons button {
|
||||||
|
height: 2rem;
|
||||||
|
margin: 0 5px 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#loading {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
background-color: white;
|
||||||
|
z-index: 10000;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas#output_img {
|
||||||
|
position: absolute;
|
||||||
|
right: 5px;
|
||||||
|
top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.bordered {
|
||||||
|
padding: 0.2rem;
|
||||||
|
border: 1px dashed yellow;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 0.2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.output {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip {
|
||||||
|
position: absolute;
|
||||||
|
width: 100vmin;
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
animation: bounce 1s ease-in-out infinite; /* Adjust the duration as needed */
|
||||||
|
top: -60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip .tooltiptext {
|
||||||
|
display: block;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 110px;
|
||||||
|
background-color: #ef4823;
|
||||||
|
color: #fff;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-radius: 10px;
|
||||||
|
z-index: 1;
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip:hover .tooltiptext {
|
||||||
|
}
|
||||||
|
.tooltip .tooltiptext::after {
|
||||||
|
content: " ";
|
||||||
|
position: absolute;
|
||||||
|
top: 100%; /* At the bottom of the tooltip */
|
||||||
|
left: 50%;
|
||||||
|
margin-left: -5px;
|
||||||
|
border-width: 5px;
|
||||||
|
border-style: solid;
|
||||||
|
border-color: #ef4823 transparent transparent transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bounce {
|
||||||
|
0%, 100% {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: translateY(10px); /* Adjust the bounce height as needed */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: fixed;
|
||||||
|
background-color: rgba(0, 0, 0, 0.9);
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal .panel {
|
||||||
|
position: relative;
|
||||||
|
width: 80vmin;
|
||||||
|
height: 80%;
|
||||||
|
margin: 10vmin auto;
|
||||||
|
background-image: linear-gradient(0deg, #8b8986 0%, #414141 36%, #414141 92%, #515151 100%);
|
||||||
|
border-radius: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal .actions {
|
||||||
|
position: absolute;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100px;
|
||||||
|
bottom: 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actions button {
|
||||||
|
display: inline-block;
|
||||||
|
width: 35%;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 0.4rem 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.service {
|
||||||
|
margin-right: 0.5rem;
|
||||||
|
border: 1px solid #676767;
|
||||||
|
background-color: #858585;
|
||||||
|
color: #a7a7a7;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.back {
|
||||||
|
border: 1px solid rgba(239, 72, 35, 0.7);
|
||||||
|
color: rgba(239, 72, 35, 0.7);
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.back img.return {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.serviceqr {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
padding-top: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.serviceqr .imgbox {
|
||||||
|
display: block;
|
||||||
|
margin: auto;
|
||||||
|
width: 60vmin;
|
||||||
|
height: 60vmin;
|
||||||
|
padding: 3vmin;
|
||||||
|
background-color: #eee;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.serviceqr .title {
|
||||||
|
position: absolute;
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
color: #eee;
|
||||||
|
margin: 30px auto;
|
||||||
|
text-align: center;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.serviceqr img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verifyfailed {
|
||||||
|
text-align: center;
|
||||||
|
color: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verifyfailed .title {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verifyfailed .hints {
|
||||||
|
margin-top: 80px;
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verifyfailed .hints div {
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verifyfailed .circle {
|
||||||
|
height: 20vmin;
|
||||||
|
width: 20vmin;
|
||||||
|
color: #666;
|
||||||
|
font-size: 15vmin;
|
||||||
|
margin: 10vmin auto 1rem;;
|
||||||
|
background-color: #ddd;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verifyspin {
|
||||||
|
position: absolute;
|
||||||
|
overflow: hidden;
|
||||||
|
z-index: 101;
|
||||||
|
width: 100vmin;
|
||||||
|
height: 100vmax;
|
||||||
|
background-color: rgba(0, 0, 0, 0.9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.verifyspin .spinner {
|
||||||
|
position: absolute; /* Position the image absolutely */
|
||||||
|
width: 100vmin;
|
||||||
|
height: 100vmin;
|
||||||
|
margin: 0 auto;
|
||||||
|
text-align: center;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.verifyspin img {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spin-and-shrink {
|
||||||
|
animation: spin-and-shrink 3s linear forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin-and-shrink {
|
||||||
|
0% {
|
||||||
|
transform: rotate(-90deg) scale(5.5)
|
||||||
|
}
|
||||||
|
10% {
|
||||||
|
transform: rotate(0deg) scale(2.0)
|
||||||
|
}
|
||||||
|
33% {
|
||||||
|
transform: rotate(90deg) scale(1.0);
|
||||||
|
}
|
||||||
|
66% {
|
||||||
|
transform: rotate(2000deg) scale(0.5);
|
||||||
|
}
|
||||||
|
99% {
|
||||||
|
transform: rotate(3600deg) scale(0.2);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(3600deg) scale(0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.spin-only {
|
||||||
|
animation: spin-only 0.3s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin-only {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg) scale(0.2);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg) scale(0.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.verifyspin .loading {
|
||||||
|
color: #ccc;
|
||||||
|
font-size: 1.4rem;
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
margin: 110vmin auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.scanguide {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#hint {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 10px auto;
|
||||||
|
padding: 12px 40px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
|
color: #eee;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.qrarc {
|
||||||
|
position: absolute;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 46vw;
|
||||||
|
height: 46vw;
|
||||||
|
margin: 35vw 27vw;
|
||||||
|
animation: qrarc-anime 1.2s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.qrarc img.arc {
|
||||||
|
position: absolute;
|
||||||
|
width: 15%;
|
||||||
|
height: 15%;
|
||||||
|
opacity: 0.9;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.qrarc img.arc.topright {
|
||||||
|
right: 0;
|
||||||
|
transform: rotate(90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
div.qrarc img.arc.bottomleft {
|
||||||
|
bottom: 0;
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
div.qrarc img.arc.bottomright {
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@keyframes qrarc-anime {
|
||||||
|
0% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
web/public/camera-6.0/images/arc.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
web/public/camera-6.0/images/black.png
Normal file
|
After Width: | Height: | Size: 637 B |
BIN
web/public/camera-6.0/images/camoverlay.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
web/public/camera-6.0/images/flash-button.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
web/public/camera-6.0/images/play-button.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
web/public/camera-6.0/images/qrmarkers.png
Normal file
|
After Width: | Height: | Size: 7.5 KiB |
BIN
web/public/camera-6.0/images/return.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
web/public/camera-6.0/images/right-arrow.png
Normal file
|
After Width: | Height: | Size: 389 B |
BIN
web/public/camera-6.0/images/spinner.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
121
web/public/camera-6.0/index.html
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="description" content="WebRTC code samples">
|
||||||
|
<meta name="viewport" content="width=device-width, user-scalable=yes, initial-scale=1, maximum-scale=1">
|
||||||
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
|
<base target="_blank">
|
||||||
|
<title>AI验真</title>
|
||||||
|
<link rel="stylesheet" href="css/main.css">
|
||||||
|
<link rel="stylesheet" href="css/loading.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="loading" id="loading">
|
||||||
|
<div>
|
||||||
|
<div class="lds-roller"><div></div><div></div><div></div><div></div><div></div><div></div><div></div><div></div></div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
AI摄像头加载中...
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="container">
|
||||||
|
<div>
|
||||||
|
<div class="camoverlay">
|
||||||
|
<div class="upper overlay">
|
||||||
|
<div>
|
||||||
|
<div class="qrarc">
|
||||||
|
<img class="topleft arc" src="images/arc.png" />
|
||||||
|
<img class="topright arc" src="images/arc.png" />
|
||||||
|
<img class="bottomleft arc" src="images/arc.png" />
|
||||||
|
<img class="bottomright arc" src="images/arc.png" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="hint wrapper">
|
||||||
|
<div id="hint" class="upper text" onclick="debug_countdown()">
|
||||||
|
对齐定位点
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<video class="preview" id="video" autoplay muted playsinline></video>
|
||||||
|
</div>
|
||||||
|
<div class="bottomfixed">
|
||||||
|
<div class="play action" onclick="show_modal('scanguide')">
|
||||||
|
<img
|
||||||
|
class="play"
|
||||||
|
src="images/play-button.png"
|
||||||
|
/>
|
||||||
|
验证演示
|
||||||
|
</div>
|
||||||
|
<div class="torch action" onclick="toggle_torch()">
|
||||||
|
<img
|
||||||
|
class="torch"
|
||||||
|
src="images/flash-button.png"
|
||||||
|
/>
|
||||||
|
开启补光
|
||||||
|
</div>
|
||||||
|
<div class="hidden tooltip"><span class="tooltiptext">遇到困难?</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="hidden debug" id="debug_div">
|
||||||
|
<div class="buttons">
|
||||||
|
<button onclick="location.reload()">reload</button>
|
||||||
|
<button onclick="set_zoom(4)">4x zoom</button>
|
||||||
|
<button onclick="set_zoom(2)">2x zoom</button>
|
||||||
|
<button onclick="wx_submit('debug')">WX OK</button>
|
||||||
|
<button onclick="torch_onoff()">Torch</button>
|
||||||
|
<button onclick="hide_debug()">Hide</button>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<canvas class="hidden" id="original" width="2000" height="2000">
|
||||||
|
</canvas>
|
||||||
|
<canvas class="hidden" id="grayscale" width="2000" height="2000">
|
||||||
|
</canvas>
|
||||||
|
</div>
|
||||||
|
<div id="logs" class="bordered">
|
||||||
|
</div>
|
||||||
|
<div class="bordered">
|
||||||
|
Caps: <span id="caps"></span>
|
||||||
|
</div>
|
||||||
|
<div class="bordered">
|
||||||
|
Constraints: <span id="cons"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="modal hidden">
|
||||||
|
<div class="panel">
|
||||||
|
<div class="scanguide hidden">
|
||||||
|
<div id="scanguide">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="serviceqr hidden">
|
||||||
|
<div class="imgbox">
|
||||||
|
<img id="service_img" src="https://emblem-resources.oss-accelerate.aliyuncs.com/service-qr2.png" alt=""/>
|
||||||
|
</div>
|
||||||
|
<div class="title">
|
||||||
|
长按识别二维码,添加人工客服
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="actions">
|
||||||
|
<button onclick="show_modal('serviceqr')" class="service">人工客服</button>
|
||||||
|
<button onclick="hide_modal()" class="back">
|
||||||
|
<img class="return" src="images/return.png" alt=""/>
|
||||||
|
采集</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="verifyspin hidden">
|
||||||
|
<div class="spinner">
|
||||||
|
<img src="images/spinner.png" class="spin-image spin-and-shrink">
|
||||||
|
</div>
|
||||||
|
<div class="loading">
|
||||||
|
Loading
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.3.2.js"></script>
|
||||||
|
<script type="text/javascript" src="js/lottie.min.js"></script>
|
||||||
|
<script type="text/javascript" src="js/main.js"></script>
|
||||||
|
<script type="text/javascript" src="js/qrtool.web.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
1
web/public/camera-6.0/js/lottie.min.js
vendored
Normal file
429
web/public/camera-6.0/js/main.js
Normal file
@ -0,0 +1,429 @@
|
|||||||
|
var debug_taps = 0;
|
||||||
|
var debug_logs = [];
|
||||||
|
var track;
|
||||||
|
var frame_width;
|
||||||
|
var frame_height;
|
||||||
|
var scanguide_anime;
|
||||||
|
var busy = false;
|
||||||
|
const max_inflight = 5;
|
||||||
|
var should_check_auto_torch = true;
|
||||||
|
var done_checking_auto_torch = false;
|
||||||
|
var camera_sensitivity = 1.0;
|
||||||
|
var camera_capabilities = null;
|
||||||
|
|
||||||
|
var Module = {
|
||||||
|
onRuntimeInitialized: start,
|
||||||
|
};
|
||||||
|
|
||||||
|
function get_query(key) {
|
||||||
|
const qs = window.location.search;
|
||||||
|
const params = new URLSearchParams(qs);
|
||||||
|
return params.get(key) || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function show_debug() {
|
||||||
|
const debug_div = document.getElementById("debug_div");
|
||||||
|
debug_div.classList.remove("hidden");
|
||||||
|
}
|
||||||
|
function hide_debug() {
|
||||||
|
const debug_div = document.getElementById("debug_div");
|
||||||
|
debug_div.classList.add("hidden");
|
||||||
|
}
|
||||||
|
function debug_countdown() {
|
||||||
|
debug_taps += 1;
|
||||||
|
if (debug_taps > 10) {
|
||||||
|
show_debug();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide_loading() {
|
||||||
|
const loading = document.getElementById("loading");
|
||||||
|
loading.classList.add("hidden");
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_stream(stream) {
|
||||||
|
const video = document.querySelector('video');
|
||||||
|
const videoTracks = stream.getVideoTracks();
|
||||||
|
debug_log(`total video tracks: ${videoTracks.length} Using video device: ${videoTracks[0].label}`);
|
||||||
|
window.stream = stream; // make variable available to browser console
|
||||||
|
video.srcObject = stream;
|
||||||
|
video.play();
|
||||||
|
track = videoTracks[0];
|
||||||
|
camera_capabilities = track.getCapabilities();
|
||||||
|
const caps = document.getElementById("caps");
|
||||||
|
caps.innerHTML = JSON.stringify(camera_capabilities);
|
||||||
|
const settings = track.getSettings();
|
||||||
|
console.log(settings);
|
||||||
|
const canvas = document.getElementById("original");
|
||||||
|
frame_width = settings.width;
|
||||||
|
frame_height = settings.height;
|
||||||
|
video.width = frame_width;
|
||||||
|
video.height = frame_height;
|
||||||
|
canvas.width = frame_width;
|
||||||
|
canvas.height = frame_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleError(error) {
|
||||||
|
debug_log(error);
|
||||||
|
console.log(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
function debug_log(msg) {
|
||||||
|
debug_logs.push(Date.now() / 1000 + ": " + msg);
|
||||||
|
const nentries = 20;
|
||||||
|
while (debug_logs.length > nentries) {
|
||||||
|
debug_logs.shift();
|
||||||
|
}
|
||||||
|
var output = "";
|
||||||
|
for (var l of debug_logs) {
|
||||||
|
output = `<div>${l}</div>` + output;
|
||||||
|
}
|
||||||
|
console.log(msg);
|
||||||
|
const di = document.getElementById("logs");
|
||||||
|
di.innerHTML = output;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function start_camera(e) {
|
||||||
|
try {
|
||||||
|
const constraints = window.constraints = {
|
||||||
|
audio: false,
|
||||||
|
video: {
|
||||||
|
facingMode: "environment",
|
||||||
|
focusDistance: 0.12,
|
||||||
|
focusMode: "manual",
|
||||||
|
width: { ideal: 2000 },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const supportedConstraints = navigator.mediaDevices.getSupportedConstraints();
|
||||||
|
const cons = document.getElementById("cons");
|
||||||
|
cons.innerHTML = JSON.stringify(supportedConstraints);
|
||||||
|
debug_log(navigator.userAgent);
|
||||||
|
|
||||||
|
const stream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||||
|
handle_stream(stream);
|
||||||
|
const zoom = get_query("zoom");
|
||||||
|
if (zoom) {
|
||||||
|
set_zoom(zoom);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
handleError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function is_emblem_qr_pattern(p)
|
||||||
|
{
|
||||||
|
if (p.search(/code=[0-9a-zA-Z]+/) >= 0) return true;
|
||||||
|
if (p.search(/id=[0-9a-zA-Z]+/) >= 0) return true;
|
||||||
|
if (p.search(/c=[0-9a-zA-Z]+/) >= 0) return true;
|
||||||
|
if (p.search(/https:\/\/xy.ltd\/v\/[0-9a-zA-Z]+/) >= 0) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_hint_text(r) {
|
||||||
|
var qr_is_valid = false;
|
||||||
|
if (r.qrcode && r.qrcode.length > 0) {
|
||||||
|
qr_is_valid = is_emblem_qr_pattern(r.qrcode);
|
||||||
|
if (!qr_is_valid) {
|
||||||
|
return "无效编码";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (qr_is_valid) {
|
||||||
|
var err = r.err || "";
|
||||||
|
if (err.includes("margin too small")) {
|
||||||
|
return "对齐定位点";
|
||||||
|
} else if (err.includes("qr too small")) {
|
||||||
|
return "移近一点";
|
||||||
|
} else if (err.includes("too blurry")) {
|
||||||
|
return "保持稳定";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "对齐定位点";
|
||||||
|
}
|
||||||
|
|
||||||
|
function handle_frame() {
|
||||||
|
try {
|
||||||
|
do_handle_frame();
|
||||||
|
} catch (e) {
|
||||||
|
debug_log("handle frame exception: " + e);
|
||||||
|
}
|
||||||
|
setTimeout(handle_frame, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
image_data_urls = [];
|
||||||
|
|
||||||
|
function do_handle_frame() {
|
||||||
|
if (busy) return;
|
||||||
|
const canvas = document.getElementById("original");
|
||||||
|
const video = document.getElementById("video");
|
||||||
|
|
||||||
|
canvas.width = video.width;
|
||||||
|
canvas.height = video.height;
|
||||||
|
|
||||||
|
canvas.style.width = video.width / 4 + "px";
|
||||||
|
canvas.style.height = video.height / 4 + "px";
|
||||||
|
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
ctx.drawImage(video, 0, 0);
|
||||||
|
const id = ctx.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
|
console.log(Module);
|
||||||
|
var buf = Module._malloc(id.data.length * id.data.BYTES_PER_ELEMENT);
|
||||||
|
Module.HEAPU8.set(id.data, buf);
|
||||||
|
var r = Module.ccall('qrtool_angle', 'string', ['number', 'number', 'number', 'number', 'number'], [buf, id.width, id.height, 0, camera_sensitivity]);
|
||||||
|
Module._free(buf);
|
||||||
|
debug_log(r);
|
||||||
|
const res = JSON.parse(r);
|
||||||
|
const is_valid_pattern = res.qrcode && res.qrcode.length && is_emblem_qr_pattern(res.qrcode);
|
||||||
|
if (res.qrcode && should_check_auto_torch) {
|
||||||
|
start_check_auto_torch(res.qrcode);
|
||||||
|
}
|
||||||
|
if (done_checking_auto_torch && is_valid_pattern && res.ok) {
|
||||||
|
var data_url = canvas.toDataURL("image/jpeg", 1.0);
|
||||||
|
image_data_urls.push(data_url);
|
||||||
|
if (image_data_urls.length >= 3) {
|
||||||
|
submit_image(res.qrcode, res.angle, image_data_urls);
|
||||||
|
image_data_urls = [];
|
||||||
|
} else {
|
||||||
|
pending_hint = make_hint_text(res);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pending_hint = make_hint_text(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function start_check_auto_torch(qrcode)
|
||||||
|
{
|
||||||
|
should_check_auto_torch = false;
|
||||||
|
try {
|
||||||
|
var r = await fetch("https://themblem.com/api/v1/check-auto-torch/?qrcode=" + encodeURIComponent(qrcode), {
|
||||||
|
method: "GET",
|
||||||
|
});
|
||||||
|
var d = await r.json();
|
||||||
|
debug_log(JSON.stringify(d));
|
||||||
|
if (d.enable_auto_torch && !torch) {
|
||||||
|
toggle_torch();
|
||||||
|
}
|
||||||
|
camera_sensitivity = d.camera_sensitivity || 1.0;
|
||||||
|
} catch (e) {
|
||||||
|
debug_log("start_check_auto_torch exception: " + e);
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
done_checking_auto_torch = true;
|
||||||
|
}, 200);
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_zoom(zoom) {
|
||||||
|
if (camera_capabilities.zoom) {
|
||||||
|
debug_log("set zoom by applying constraints");
|
||||||
|
track.applyConstraints({advanced: [ {zoom} ]});
|
||||||
|
} else {
|
||||||
|
debug_log("set zoom by scaling video element");
|
||||||
|
add_style_by_query("video.preview", "transform", `scale(${zoom})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let torch = false;
|
||||||
|
function toggle_torch() {
|
||||||
|
torch = !torch;
|
||||||
|
track.applyConstraints({advanced: [ {torch: torch} ]});
|
||||||
|
if (torch) {
|
||||||
|
add_class_by_query(".bottomfixed .torch.action", "highlight");
|
||||||
|
} else {
|
||||||
|
remove_class_by_query(".bottomfixed .torch.action", "highlight");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function show_spinner() {
|
||||||
|
remove_class_by_query('.verifyspin', 'hidden');
|
||||||
|
remove_class_by_query('.spin-image', 'spin-only');
|
||||||
|
remove_class_by_query('.spin-image', 'spin-and-shrink');
|
||||||
|
setTimeout(() => {
|
||||||
|
add_class_by_query('.spin-image', 'spin-and-shrink');
|
||||||
|
setTimeout(() => {
|
||||||
|
remove_class_by_query('.spin-image', 'spin-and-shrink');
|
||||||
|
add_class_by_query('.spin-image', 'spin-only');
|
||||||
|
}, 3000);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide_spinner() {
|
||||||
|
add_class_by_query('.verifyspin', 'hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submit_image(qrcode, angle, image_data_urls) {
|
||||||
|
busy = true;
|
||||||
|
var begin = Date.now();
|
||||||
|
show_spinner();
|
||||||
|
debug_log(`submit: qrcode: ${qrcode} angle: ${angle}`);
|
||||||
|
try {
|
||||||
|
// TODO: pass these parameters from query string
|
||||||
|
var emblem_id = get_query("emblem_id");
|
||||||
|
var nick_name = get_query("nick_name");
|
||||||
|
var realip = get_query("realip");
|
||||||
|
var phonemodel = get_query("phonemodel");
|
||||||
|
var data = {
|
||||||
|
emblem_id,
|
||||||
|
nick_name,
|
||||||
|
realip,
|
||||||
|
qrcode,
|
||||||
|
angle,
|
||||||
|
phonemodel,
|
||||||
|
image_data_urls,
|
||||||
|
log: debug_logs.join("\n"),
|
||||||
|
};
|
||||||
|
var r = await fetch("https://themblem.com/api/v1/qr-verify/", {
|
||||||
|
method: "POST",
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
});
|
||||||
|
var d = await r.json();
|
||||||
|
debug_log(JSON.stringify(d));
|
||||||
|
if (wx) {
|
||||||
|
var delay = 3000 - (Date.now() - begin);
|
||||||
|
setTimeout(() => {
|
||||||
|
var wx_redirect_to = get_query("wx_redirect_to");
|
||||||
|
var query_params;
|
||||||
|
|
||||||
|
if (d.serial_code) {
|
||||||
|
// Success: pass ok=1 with qr_code and serial_code
|
||||||
|
query_params = `?ok=1&qr_code=${encodeURIComponent(qrcode)}&serial_code=${encodeURIComponent(d.serial_code)}`;
|
||||||
|
} else {
|
||||||
|
// Failure: pass ok=0 only
|
||||||
|
query_params = `?ok=0`;
|
||||||
|
}
|
||||||
|
|
||||||
|
wx.miniProgram.redirectTo({
|
||||||
|
url: wx_redirect_to + query_params,
|
||||||
|
});
|
||||||
|
}, delay > 0 ? delay : 0);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
debug_log(`submission error: ${e}`);
|
||||||
|
if (wx) {
|
||||||
|
var delay = 3000 - (Date.now() - begin);
|
||||||
|
setTimeout(() => {
|
||||||
|
var wx_redirect_to = get_query("wx_redirect_to");
|
||||||
|
var query_params = `?ok=0`;
|
||||||
|
|
||||||
|
wx.miniProgram.redirectTo({
|
||||||
|
url: wx_redirect_to + query_params,
|
||||||
|
});
|
||||||
|
}, delay > 0 ? delay : 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var pending_hint = null;
|
||||||
|
|
||||||
|
function update_hint() {
|
||||||
|
if (pending_hint) {
|
||||||
|
const hint_dev = document.getElementById("hint");
|
||||||
|
hint_dev.innerHTML = pending_hint;
|
||||||
|
pending_hint = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function start() {
|
||||||
|
console.log("start");
|
||||||
|
setTimeout(handle_frame, 100);
|
||||||
|
hide_loading();
|
||||||
|
if (get_query("debug")) {
|
||||||
|
show_debug();
|
||||||
|
}
|
||||||
|
setInterval(update_hint, 1000);
|
||||||
|
setTimeout(() => {
|
||||||
|
show_tooltip();
|
||||||
|
}, 15000);
|
||||||
|
}
|
||||||
|
|
||||||
|
function show_tooltip() {
|
||||||
|
remove_class_by_query(".tooltip", "hidden");
|
||||||
|
add_class_by_query(".bottomfixed .action", "highlight");
|
||||||
|
}
|
||||||
|
|
||||||
|
function hide_tooltip() {
|
||||||
|
add_class_by_query(".tooltip", "hidden");
|
||||||
|
remove_class_by_query(".bottomfixed .action", "highlight");
|
||||||
|
}
|
||||||
|
|
||||||
|
async function init_scanguide() {
|
||||||
|
var r = await fetch('https://emblem-resources.oss-cn-guangzhou.aliyuncs.com/scan-guide-1080x1920-3.json');
|
||||||
|
var d = await r.json();
|
||||||
|
console.log("start scanguide", d);
|
||||||
|
const elem = document.getElementById("scanguide");
|
||||||
|
scanguide_anime = bodymovin.loadAnimation({
|
||||||
|
container: elem,
|
||||||
|
animationData: d,
|
||||||
|
// path: 'data.json',
|
||||||
|
renderer: 'svg',
|
||||||
|
loop: true,
|
||||||
|
autoplay: true,
|
||||||
|
name: "Scan guide",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function add_style_by_query(query, property, style) {
|
||||||
|
var list = document.querySelectorAll(query);
|
||||||
|
for (var i = 0; i < list.length; ++i) {
|
||||||
|
list[i].style[property] = style;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function add_class_by_query(query, to_add) {
|
||||||
|
var list = document.querySelectorAll(query);
|
||||||
|
for (var i = 0; i < list.length; ++i) {
|
||||||
|
list[i].classList.add(to_add);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove_class_by_query(query, to_remove) {
|
||||||
|
var list = document.querySelectorAll(query);
|
||||||
|
for (var i = 0; i < list.length; ++i) {
|
||||||
|
list[i].classList.remove(to_remove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var modals = ['serviceqr', 'scanguide', 'verifyspin'];
|
||||||
|
|
||||||
|
function hide_modal() {
|
||||||
|
busy = false;
|
||||||
|
scanguide_anime.stop();
|
||||||
|
add_class_by_query(".modal", "hidden");
|
||||||
|
hide_tooltip();
|
||||||
|
}
|
||||||
|
|
||||||
|
function show_modal(which) {
|
||||||
|
busy = true;
|
||||||
|
hide_tooltip();
|
||||||
|
hide_spinner();
|
||||||
|
add_class_by_query(".verifyspin", "hidden");
|
||||||
|
remove_class_by_query(".modal", "hidden");
|
||||||
|
remove_class_by_query('.actions', 'hidden');
|
||||||
|
for (var m of modals) {
|
||||||
|
add_class_by_query('.' + m, 'hidden');
|
||||||
|
}
|
||||||
|
if (which == 'scanguide') {
|
||||||
|
scanguide_anime.goToAndPlay(0);
|
||||||
|
remove_class_by_query('.scanguide', 'hidden');
|
||||||
|
}
|
||||||
|
if (which == 'serviceqr') {
|
||||||
|
remove_class_by_query('.serviceqr', 'hidden');
|
||||||
|
add_class_by_query('.actions', 'hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_service_qr_img() {
|
||||||
|
var tid = get_query('tenant');
|
||||||
|
var url = '/api/v1/service-qr/?tenant=' + tid;
|
||||||
|
const elem = document.getElementById("service_img");
|
||||||
|
elem.src = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
start_camera();
|
||||||
|
init_scanguide();
|
||||||
|
set_service_qr_img();
|
||||||
|
})();
|
||||||