# VelaMQ RS

VelaMQ RS 是一个基于 Rust workspace 的 MQTT Broker 项目，包含：

- MQTT 服务端运行时
- 集群节点间订阅同步与远程投递
- 管理 API 与 Web 控制台
- 认证、ACL、规则引擎、数据源、命令、监控能力

仓库根目录是 workspace，同时负责在 `cargo build` 时把 `web/` 前端构建到 `static/`。

## 目录结构

- `apps/velamqd`: VelaMQ 启动入口，生成 `velamqd`
- `apps/fluxmqd`: FluxMQ 启动入口，生成 `fluxmqd`
- `crates/broker`: broker 核心逻辑
- `crates/mqtt`: MQTT 协议处理与监听器运行时
- `crates/cluster`: 节点发现、订阅同步、远程发布 fanout
- `crates/api`: 管理 API
- `crates/topic`: 主题 trie、保留消息索引
- `crates/storage`: 元数据、会话、retain、sync event 存储
- `crates/rule`: 规则引擎
- `crates/auth`: 认证
- `crates/acl`: 访问控制
- `crates/datasource`: 外部数据源接入
- `crates/command`: 命令发布能力
- `web`: 管理前端
- `monitoring`: Grafana / Prometheus 监控资产
- `tools/mqtt-bench`: MQTT 压测与复现脚本

## 开发环境

建议准备以下依赖：

- Rust stable toolchain
- Node.js + npm
- `cmake`
- Docker + `cross`（做 Linux / Windows 多平台打包时需要）

说明：

- 根目录 `build.rs` 会在构建时自动执行 `npm ci` / `npm run build`，把前端产物输出到 `static/`
- 如果只想做 Rust 侧迭代，可以设置 `VELAMQ_SKIP_WEB_BUILD=1`
- `apps/velamqd` 默认开启 `oracle-oci` feature；如果本机没有 OCI 环境，可以用 `--no-default-features`

## 快速启动

使用仓库内 `config.toml`：

```bash
cd /Users/lulu/Work/velamq-rs
VELAMQ_CONFIG_FILE=config.toml cargo run -p velamqd
```

FluxMQ 品牌入口：

```bash
VELAMQ_CONFIG_FILE=config.toml cargo run -p fluxmqd
```

只做编译：

```bash
cd /Users/lulu/Work/velamq-rs
cargo build -p velamqd
```

跳过前端构建：

```bash
cd /Users/lulu/Work/velamq-rs
VELAMQ_SKIP_WEB_BUILD=1 cargo build -p velamqd
```

如果不想启用 Oracle 相关 feature：

```bash
cd /Users/lulu/Work/velamq-rs
VELAMQ_SKIP_WEB_BUILD=1 cargo build -p velamqd --no-default-features
```

启动后：

- MQTT 监听端点必须通过控制台“端点”页面管理，并随管理数据持久化
- 管理 API / 控制台默认参考 `config.toml` 中的 `[api]`

## 跨平台打包

仓库内提供了两个品牌打包入口，都会先构建 `web/` 到 `static/`，再把对应二进制和运行所需文件打成每个平台一个 zip。

```bash
cd /Users/lulu/Work/velamq-rs
bash deploy/package-velamq.sh
```

FluxMQ 品牌包：

```bash
bash deploy/package-fluxmq.sh
```

默认行为：

- macOS：打 `macos-x86_64`、`macos-aarch64`、`linux-x86_64`、`linux-aarch64`、`windows-x86_64`
- Linux：打 `linux-x86_64`、`linux-aarch64`、`windows-x86_64`
- 默认使用 `--no-default-features`，关闭 `oracle-oci`，让发布包不依赖 Oracle Instant Client
- release 包默认使用 `opt-level=3`、thin LTO、单 codegen unit、strip symbols、`panic=abort`

常见用法：

```bash
# 只打本机
TARGETS="$(rustc -vV | sed -n 's/^host: //p')" bash deploy/package-velamq.sh

# 指定输出版本号
VERSION=0.1.0 bash deploy/package-velamq.sh

# 只打本机，并保留 Oracle OCI
ENABLE_ORACLE_OCI=1 TARGETS="$(rustc -vV | sed -n 's/^host: //p')" bash deploy/package-velamq.sh

# 只打本机，并针对当前 CPU 做 codegen 优化
VELAMQ_RELEASE_NATIVE_CPU=1 TARGETS="$(rustc -vV | sed -n 's/^host: //p')" bash deploy/package-velamq.sh
```

从 macOS 交叉编译 Linux / Windows 时，需要 `cross` 和 Docker。建议安装开发版 `cross`：

```bash
cargo uninstall cross 2>/dev/null
cargo install cross --git https://github.com/cross-rs/cross
```

VelaMQ 产物位于 `dist/velamqd-{version}-{platform}.zip`；FluxMQ 产物位于 `dist/fluxmqd-{version}-{platform}.zip`。每个 zip 内包含：

- `bin/velamqd` / `bin/velamqd.exe`，或 FluxMQ 包里的 `bin/fluxmqd` / `bin/fluxmqd.exe`
- `static/`
- `config.toml`
- `start.sh` 或 `start.bat`
- `deploy/README.md`

## 配置说明

配置加载顺序：

1. 如果设置了 `VELAMQ_CONFIG_FILE`，优先从 TOML 文件加载
2. 否则从环境变量加载

当前仓库自带了一个示例配置文件：

- `config.toml`

几个关键约束：

- Broker storage 固定使用本地 RocksDB；集群复制通过 `[cluster]` + `[cluster.raft]` 配置完成
- MQTT 监听端口不在启动配置里定义，而是由管理端点配置持久化
- `api.enabled = true` 时控制台 JWT 登录会被强制开启
- Kafka 规则动作批量写入和调优参考 `docs/rule-kafka-batching-runbook.md`

### RocksDB + Raft 存储

VelaMQ 启动后直接使用本地 RocksDB 状态机。关键配置：

```toml
[cluster]
node_id = "node-a"
bind_addr = "0.0.0.0:50051"
advertise_addr = "http://127.0.0.1:50051"
seeds = []

[cluster.tls]
enabled = false
ca_cert = "certs/cluster-ca.pem"
cert = "certs/node-a.pem"
key = "certs/node-a-key.pem"
# server_name = "velamq-cluster"

[cluster.raft]
role = "voter"
read_consistency = "voter_any"
only_vote_store = false
heartbeat_interval_ms = 500
election_timeout_min_ms = 1500
election_timeout_max_ms = 3000
snapshot_every_entries = 10000

[rocksdb]
data_dir = "./data/rocksdb-node-a"
backup_dir = "./data/rocksdb-backups"
backup_retention = 7
```

三节点部署时，每个节点必须使用不同的 `cluster.node_id`、`cluster.bind_addr`、
`cluster.advertise_addr` 和 `rocksdb.data_dir`。`cluster.seeds = []` 表示单节点自举，
节点会自己成为 leader；只要配置了 `cluster.seeds`，该节点就按集群模式启动，选举和提交
quorum 至少按 `1 + seeds.len()` 计算，不会在 seed 不可达时退化成单节点 leader。
生产三节点建议每个节点都配置其他 voter 的 gRPC 地址，避免单个旧副本单独启动后对外写入。
`cluster.raft.role = "voter"` 的节点保存 RocksDB 副本并参与 Raft；`role = "client"`
的节点不保存本地 RocksDB 副本，互联后自动发现 voter 并代理读写。
如果某个 voter 只希望作为存储副本和投票节点，不初始化 MQTT 端点、数据源、认证、ACL、
规则和命令等运行态配置，可以设置 `cluster.raft.only_vote_store = true`。
`cluster.tls.enabled = true` 时节点间 gRPC 使用 mTLS，所有 voter/client 需要使用同一 CA
签发的节点证书；已有 `http://host:port` 和 `host:port` 地址会在 mTLS 开启后自动按
`https://host:port` 拨号。

管理控制台 Dashboard 会展示本地 RocksDB/Raft 状态、集群 Storage Raft 角色、快照和备份
运行计数。运维 API：

- `GET /api/data-management/storage/status`
- `POST /api/data-management/storage/backup`
- `POST /api/data-management/storage/snapshot`
- `GET /api/cluster/storage-raft`

Prometheus `/metrics` 也会导出 `velamq_storage_raft_*` 与 `velamq_storage_backup_*` 指标，
覆盖 Raft 角色、term、log/applied index、转发失败、快照年龄和本地备份状态。

离线恢复本地 checkpoint 备份：

```bash
velamqd restore-rocksdb --backup ./data/rocksdb-backups/backup-... --data-dir ./data/rocksdb-node-a
```

目标 `data_dir` 非空时可加 `--force`，命令会先把现有目录移动为 `*.pre-restore-*`。

## 集群说明

集群相关逻辑主要在：

- `crates/cluster`
- `crates/broker/src/impl_subscriptions.rs`
- `crates/topic/src/topic_trie.rs`

当前 direct subscription 的同步语义是：

- 同一节点、同一 direct filter，只在 `0 -> 1` 时向其他节点广播一次 `Upsert`
- 只在 `1 -> 0` 时向其他节点广播一次 `Remove`
- 其他节点只维护“该节点是否对该 filter 感兴趣”的代表订阅，不复制每个 client 的完整 direct 行

这块现在已经改成基于原子 bucket 增删结果判断 first / last，避免并发订阅或退订时漏同步。

## 测试

常规定向测试：

```bash
cd /Users/lulu/Work/velamq-rs
cargo test -p topic topic_trie -- --nocapture
cargo test -p broker impl_subscriptions -- --nocapture
```

## 压测与并发验证

### 1. TopicTrie 原子边界压测

验证同一 filter 并发插入 / 删除时，是否只出现一次 first / last：

```bash
cd /Users/lulu/Work/velamq-rs
VELAMQ_TRIE_STRESS_THREADS=24 \
VELAMQ_TRIE_STRESS_ROUNDS=20000 \
cargo test -p topic stress_local_direct_boundary_detection -- --ignored --nocapture
```

### 2. Broker 订阅同步语义压测

验证 broker 层 `register/unregister` 是否仍然只在 first / last 边界生成 cluster sync：

```bash
cd /Users/lulu/Work/velamq-rs
VELAMQ_BROKER_STRESS_WORKERS=12 \
VELAMQ_BROKER_STRESS_ROUNDS=1000 \
cargo test -p broker stress_direct_subscription_sync_boundaries -- --ignored --nocapture
```

### 3. 两节点真实 cluster end-to-end 压测

验证真实 gRPC 订阅同步后，远端节点是否能及时看到 direct route 的出现与消失：

```bash
cd /Users/lulu/Work/velamq-rs
cargo test -p broker stress_cluster_remote_direct_sync_end_to_end -- --ignored --nocapture
```

可调参数：

- `VELAMQ_CLUSTER_STRESS_WORKERS`
- `VELAMQ_CLUSTER_STRESS_ROUNDS`

## MQTT 压测工具

仓库内自带了一个 Node.js 脚本工具：

- `tools/mqtt-bench`

文档见：

- `tools/mqtt-bench/README.md`

它适合做：

- 本地并发连接 / 发布压测
- 收包闭环校验
- 重连后重复投递问题复现

## 监控

监控资产在：

- `monitoring/grafana/velamq-overview.json`
- `monitoring/prometheus/velamq-alerts.yml`

说明见：

- `monitoring/README.md`

## 前端开发

单独开发前端时：

```bash
cd /Users/lulu/Work/velamq-rs/web
npm ci
npm run dev
```

正式构建：

```bash
cd /Users/lulu/Work/velamq-rs/web
npm run build
```

## 备注

- 根目录 `cargo build` 会触发前端构建
- `build.rs` 会先构建到临时目录，再增量覆盖 `static/`，避免运行中 rebuild 删除浏览器仍在请求的旧 hash 资源
- 若需要补充部署文档、API 文档或集群运维手册，建议在此 README 基础上继续拆分 `docs/`
