mocra 0.4.1

A distributed, event-driven crawling and data collection framework
# 部署指南

本指南介绍如何在生产中运行 mocra:零基础设施的单节点、内嵌 Raft 集群、跨节点数据面队列,以及监控。

> **English version:** [docs/deployment.md]../deployment.md

mocra 沿两条相互独立的轴扩展——**控制面**(协调)与**数据面**(消息队列)。请从下面选择一种拓扑;
各部分如何组合见[系统架构](architecture.md)。

## 单节点(零基础设施)

默认方式。无数据库、无外部服务、无消息代理——门面构建一个内存、单进程的引擎。

### 适用场景

- 开发与测试
- 一次性抓取与独立脚本
- 单进程即可容纳的低流量采集

### 配置

依赖 mocra 并运行一个 spider——这就是全部部署:

```toml
[dependencies]
mocra = "0.4"
```

```rust
use mocra::prelude::*;

#[tokio::main]
async fn main() -> Result<()> {
    Mocra::builder()
        .spider(MySpider, on_item(|item: Item| async move {
            // 落库 / 打印 / 转发
        }))
        .run()          // 不用 .from_toml、不用 .cluster → 内存单节点
        .await
}
```

```bash
cargo run --release
```

### 运行效果

- 队列是进程内的 Tokio mpsc 通道。
- 协调在进程内(无分布式锁)。
- 未提供 `.from_toml` 时,构建器会自动为每个 spider 播种,并在**空闲时停止**——非常适合一次性运行。
  持续产生任务的 spider 会持续运行。

无需 Postgres,也无需外部服务。账号 × 平台 × 模块的数据库模型通过 `store` 特性与底层 `ModuleTrait`
路径按需启用——`Spider` 门面并不需要它。

## 内嵌集群(`cluster-embedded`

运行一个**自组网的 Raft 集群**,其控制面是内嵌的 **redb + Raft**——选主、带栅栏(fencing)的分布式锁、
成员管理与分区归属,**无需外部协调器**。

```toml
mocra = { version = "0.4", features = ["cluster-embedded"] }
```

### 注册节点

首个节点**自举**(bootstrap)一个新集群;其余每个节点通过任意已知节点的地址(一个「种子」)**加入**
(join)。把任意节点注册到任意已在网中的节点:

```rust
// 首个核心节点——自举一个新集群(无种子)
Mocra::builder()
    .spider(MySpider, on_item(|_: Item| async move { /* ... */ }))
    .cluster(ClusterConfig::bootstrap(1, "127.0.0.1:7001", "./data/n1"))
    .run().await?;

// 其余节点通过种子地址加入
Mocra::builder()
    .spider(MySpider, on_item(|_: Item| async move { /* ... */ }))
    .cluster(ClusterConfig::join(2, "127.0.0.1:7002", "./data/n2", "127.0.0.1:7001"))
    .run().await?;
```

`ClusterConfig::bootstrap(node_id, http_addr, data_dir)` 启动一个新集群;`ClusterConfig::join` 额外
带上任意已有节点的种子地址。`data_dir` 是 redb 状态机 + Raft 日志目录,因此要给每个节点各自的路径。

运行内置示例即可拉起三个节点(三个终端——首个自举,其余通过它加入):

```bash
# 首个核心节点(无种子 → 自举)
cargo run --example cluster_quickstart --features cluster-embedded -- 1 127.0.0.1:7001
# 其余节点(第三个参数 = 种子 = 首节点地址 → join)
cargo run --example cluster_quickstart --features cluster-embedded -- 2 127.0.0.1:7002 127.0.0.1:7001
cargo run --example cluster_quickstart --features cluster-embedded -- 3 127.0.0.1:7003 127.0.0.1:7001
```

### 容器化部署

若要跨节点复用同一镜像,用 `ClusterConfig::from_env()`,仅通过环境变量区分:

| 变量 | 含义 |
|---|---|
| `MOCRA_NODE_ID` | 节点唯一 id(u64,必填) |
| `MOCRA_HTTP_ADDR` | 本节点对外地址,如 `10.0.0.4:7001`(必填) |
| `MOCRA_DATA_DIR` | redb + Raft 目录(默认 `./mocra-data/node-{id}`|
| `MOCRA_SEEDS` | 逗号分隔的种子地址;为空 = 自举,非空 = 加入 |

```rust
Mocra::builder()
    .spider(MySpider, on_item(|_: Item| async move { /* ... */ }))
    .cluster(ClusterConfig::from_env()?)
    .run().await?;
```

### 数据面

内嵌集群只是控制面。**队列后端独立选择**:

- **内存**(默认)——进程内队列不跨节点,播种的工作留在产生它的节点上。
- **Kafka / NATS JetStream**——任务扇出到各节点,按 `hash(account)` 路由以实现消费者
  亲和(同一账号 → 同一消费者)。启用 `queue-kafka``queue-nats`,并在 TOML 中配置该后端
  (见[配置参考]configuration.md)。

## 跨节点数据面(Kafka / NATS)

内嵌集群是控制面,进程内队列永远不会离开产生该工作的进程。要把任务扇出到各节点,请通过
`.from_toml(cfg)` 加载 TOML 配置,把**数据面队列**指向共享消息代理——Kafka(`queue-kafka`)或
NATS JetStream(`queue-nats`)。任务按 `hash(account)` 路由以实现消费者亲和(同一账号 → 同一消费者):

```toml
# config.toml —— 用 Kafka 作为数据面队列
[channel_config.kafka]
brokers = "kafka-host:9092"

# 或使用 NATS JetStream
# [channel_config.nats]
# url = "nats://nats-host:4222"
```

```rust
Mocra::builder()
    .spider(MySpider, on_item(|_: Item| async move { /* ... */ }))
    .from_toml("config.toml")            // 数据面队列(Kafka / NATS)
    .cluster(ClusterConfig::from_env()?) // 控制面(选主、锁)
    .run().await?;
```

```bash
# 每个节点用同一二进制、同一 config.toml
cargo run --release --features "cluster-embedded queue-kafka"
```

完整的 `[channel_config]` 参考见[配置参考](configuration.md)。

## 拓扑选择

| | 单节点 | 内嵌集群 |
|---|---|---|
| 特性开关 || `cluster-embedded` |
| 外部基础设施 | **** | **** |
| 协调 | 进程内 | redb + Raft |
| 启用方式 | 门面默认 | `.cluster(ClusterConfig::…)` |
| 数据面队列 | 内存 | 内存 / Kafka / NATS |
| 最适合 | 开发、一次性、低流量 | 自包含集群、跨节点扩展 |

## 监控

### Dashboard、指标与 Web UI

启用 `dashboard` 特性并调用 `.dashboard(port)`。引擎会托管一套只读可观测 HTTP API **以及**一个内置的
单文件 Web UI——浏览器打开该端口即见 **指标 / 日志 / 任务 / 性能**,无需任何前端构建:

```toml
mocra = { version = "0.4", features = ["dashboard"] }
```

```rust
Mocra::builder()
    .spider(MySpider, on_item(|_: Item| async move { /* ... */ }))
    .dashboard(12800)   // GET / → Web UI;  /metrics、  /health、  /observability/*
    .run().await?;
```

```bash
cargo run --example dashboard --features dashboard   # 然后打开 http://127.0.0.1:12800
```

端点:

| 路由 | 鉴权 | 说明 |
|---|---|---|
| `GET /` || 内置 dashboard Web UI |
| `GET /metrics` | 无(限速) | Prometheus 指标(`mocra_*` 指标族) |
| `GET /health` || 健康 / 存活探针 |
| `GET /observability/engine` || 引擎 + 各队列快照 |
| `GET /observability/cluster` || Raft 集群状态(单机时为 `null`|
| `GET /observability/system` || 主机 CPU / 内存 / swap |
| `GET /observability/logs?limit=N` || 近期结构化日志 |
| `POST /control/pause` \| `resume` | API key | 暂停 / 恢复引擎 |
| `POST /start_work` | API key | 手动注入任务 |

只读端点已开启 CORS 且免 API key(独立前端可跨域消费);写操作端点仍需鉴权。详见
[API 参考](api-reference.md)。

### Prometheus + Grafana

仓库自带一套监控栈。`docker-compose.monitoring.yml` 运行 Prometheus 与 Grafana;Prometheus 采集你引擎的
`/metrics`(采集目标配置在 `monitoring/prometheus/prometheus.yml`,容器内通过 `host.docker.internal`
访问宿主机):

```bash
docker compose -f docker-compose.monitoring.yml up -d

# Prometheus: http://localhost:9090
# Grafana:    http://localhost:3000   (admin / admin)
# 指标:       http://localhost:12800/metrics   (即你的 .dashboard(port))
```

预置的 Grafana 数据源与仪表盘位于 `monitoring/grafana/provisioning`。

### 日志

mocra 使用 `log` / `tracing` 生态。通过 `RUST_LOG` 配置:

```bash
RUST_LOG=mocra=info cargo run          # 或 mocra=debug 获取更多细节
```

启用 `.dashboard(...)` 时,近期日志还会被采集进内存环形缓冲,并在 `GET /observability/logs` 提供。

## 生产环境检查清单

- [ ] 选择一种拓扑:单节点或内嵌集群(`cluster-embedded`)。
- [ ] 若为集群,给每个节点分配唯一的 `node_id` 和各自的 `data_dir`;其余节点通过已知地址加入
      (容器中用 `ClusterConfig::from_env()`)。
- [ ] 选择数据面队列:内存、Kafka(`queue-kafka`)或 NATS JetStream(`queue-nats`)。
- [ ] 启用 `dashboard` 并暴露 `/metrics`;用 Prometheus 采集、用 Grafana 绘图。
- [ ] 将 `RUST_LOG` 设为合适的级别。
- [ ] 若在生产中注入任务或暂停 / 恢复,用 API key 保护写操作端点(`[api] api_key`)。
- [ ] 在 TOML 中调优重试 / 错误阈值与限速(`[crawler]``[download_config]`)——见
      [配置参考]configuration.md- [ ] 生产构建保持开启 `mimalloc` 特性(它是默认特性)。

## 相关指南

- [系统架构]architecture.md——控制面 vs 数据面、工作区布局
- [配置参考]configuration.md——完整 TOML 参考(`[cache]``[channel_config]``[api]`- [API 参考]api-reference.md——HTTP 控制面与指标
- [快速上手]getting-started.md——安装与第一个 spider