目录结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
crpc/
├── migration/ # 数据库SQL迁移文件
│ ├── init_database.sql # 数据库初始化(表结构+索引)
│ └── insert_rpc_nodes.sql # RPC节点配置数据
│
├── config/ # 配置文件
│ ├── config.toml # 主配置文件
│ └── nginx.conf # Nginx负载均衡配置
│
├── program/ # Go源码
│ ├── main.go # 程序入口
│ ├── go.mod # Go模块定义
│ ├── go.sum # 依赖锁定文件
│ ├── api/ # API接口层
│ ├── cleanup/ # 日志自动清理
│ ├── config/ # 配置管理
│ ├── database/ # 数据库操作
│ ├── health/ # 健康检查
│ ├── logger/ # 日志管理
│ ├── models/ # 数据模型
│ └── proxy/ # RPC代理和故障切换
│
├── scripts/ # 脚本文件
│ ├── init_db.sh # 数据库初始化
│ ├── start_multi_instance.sh # 启动多实例(开发)
│ ├── deploy_to_remote.sh # 单实例远程部署
│ ├── deploy_multi_instance.sh # 多实例远程部署
│ ├── benchmark.sh # 性能基准测试
│ ├── monitor.sh # 实时监控面板
│ ├── test_api.sh # API测试
│ ├── test_batch_requests.sh # 批量请求测试
│ └── test_rpc_realworld.sh # 真实交易测试
│
├── bin/ # 编译后的二进制文件
│ └── crpc # 可执行程序
│
├── log/ # 日志文件目录
│
├── Makefile # 构建脚本
└── README.org # 项目文档(本文件)
|
项目简介
CRPC 是一个高可用的多链 RPC 代理服务,解决区块链应用中 RPC 节点不稳定的问题。
核心问题
前端直连RPC节点存在的问题:
- ❌ 节点故障导致业务中断
- ❌ 节点地址硬编码在前端
- ❌ 节点变更需要重新发布前端
- ❌ 无法统一管理和监控
- ❌ 无法实现负载均衡和故障切换
解决方案
通过后端RPC代理服务 + 多实例高可用架构,提供:
- ⚡ *自动故障切换*:节点故障自动切换到备用节点
- 🔄 *负载均衡*:支持多种负载均衡策略
- 💪 *高可用部署*:支持多实例部署,单实例故障不影响服务
- 🏥 *健康检查*:定期检测节点健康状态
- 📊 *监控统计*:完整的请求和节点统计
- 🔧 *易于管理*:RESTful API 管理节点配置
技术栈
- *后端*:Go 1.21 + Gin
- *数据库*:PostgreSQL 15
- *负载均衡*:Nginx
- *配置管理*:TOML
快速开始
环境要求
软件依赖
- Go 1.21+
- PostgreSQL 15+
- Nginx(生产环境)
硬件建议
- CPU: 2核+
- 内存: 4GB+
- 磁盘: 20GB+
本地开发部署
1. 准备数据库
1
2
3
4
5
6
7
8
9
10
|
# 确保PostgreSQL已安装并运行
sudo systemctl start postgresql
# 创建数据库和用户
sudo -u postgres psql << EOF
CREATE DATABASE crpc;
CREATE USER crpc WITH PASSWORD 'crpc_password';
GRANT ALL PRIVILEGES ON DATABASE crpc TO crpc;
\q
EOF
|
2. 初始化数据库
1
2
3
4
5
|
# 进入项目目录
cd /home/smokey/Desktop/crpc
# 运行数据库迁移
make migrate
|
3. 配置RPC节点
系统提供了预配置的RPC节点,包括:
- ✅ 19条主流区块链(Ethereum、BSC、Arbitrum、Polygon、Solana等)
- ✅ 50+ 公共RPC节点
- ✅ 自动优先级分级
1
2
|
# 插入预配置的节点
make insert-nodes
|
*优先级策略*:
| 提供商类型 |
Priority |
Weight |
说明 |
| Chainstack(付费) |
1 |
20 |
速度快、稳定性高 |
| Infura(免费) |
2 |
15 |
稳定但有限流 |
| 公共节点 |
3 |
10 |
免费但可能不稳定 |
*说明*:
priority 值越小优先级越高
weight 用于同优先级节点的权重随机选择
- 系统会优先选择
priority 最小的健康节点
- 如果高优先级节点全部故障,会自动降级使用低优先级节点
*节点选择逻辑*:
1
2
3
4
|
1. 筛选健康节点(is_healthy = true)
2. 按 priority 排序,选择最小值的节点组
3. 在同优先级组内,根据 weight 进行加权随机选择
4. 考虑平均响应时间(avg_response_time)进行微调
|
4. 配置文件
编辑 config/config.toml,确认数据库连接信息:
1
2
3
4
5
6
|
[database]
host = "localhost"
port = 5432
user = "crpc"
password = "crpc_password"
dbname = "crpc"
|
5. 安装依赖并运行
1
2
3
4
5
6
7
8
9
10
11
|
# 安装Go依赖
make deps
# 编译程序
make build
# 运行(单实例)
make run
# 或者直接运行
cd program && go run main.go
|
5. 验证安装
1
2
3
4
5
|
# 检查服务健康状态
curl http://localhost:8080/health
# 预期输出:
# {"status":"ok","instance_id":"crpc-1"}
|
多实例部署
使用脚本启动多实例
启动方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
cd scripts
# 方式1:Unix Socket模式(推荐,性能最佳)
./start_multi_instance.sh 3 socket
# 方式2:HTTP模式(跨服务器部署)
./start_multi_instance.sh 3 http
# 方式3:双监听模式(同时支持Socket和HTTP)
./start_multi_instance.sh 3 both
# 查看日志
tail -f ../log/crpc-*.log
# 查看Socket文件(socket模式)
ls -lh /tmp/crpc-*.sock
|
性能对比
| 监听方式 |
延迟 |
吞吐量 |
使用场景 |
| Unix Socket |
~5-10μs |
最高 |
同一服务器(推荐)✅ |
| TCP (localhost) |
~50-100μs |
高 |
本地或跨服务器 |
| TCP (网络) |
>1ms |
中 |
跨服务器部署 |
**性能提升**:Unix Socket 比 TCP 快 **5-10倍**!
配置Nginx负载均衡
1
2
3
4
5
6
7
8
9
10
11
12
|
# 安装Nginx
sudo apt install nginx
# 复制配置文件
sudo cp config/nginx.conf /etc/nginx/nginx.conf
# 测试配置
sudo nginx -t
# 启动Nginx
sudo systemctl start nginx
sudo systemctl enable nginx
|
验证负载均衡
1
2
3
4
5
6
7
|
# 多次请求,观察instance_id变化
for i in {1..10}; do
curl -s http://localhost/health | jq .instance_id
sleep 1
done
# 应该看到 crpc-1, crpc-2, crpc-3 轮流出现
|
使用示例
RPC请求
以太坊主网
1
2
3
4
5
6
7
8
9
10
11
|
# 获取最新区块号
curl -X POST http://localhost/ethereum \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
# 响应示例:
# {
# "jsonrpc": "2.0",
# "result": "0x123456",
# "id": 1
# }
|
BSC
1
2
3
4
5
6
7
8
9
|
# 获取账户余额
curl -X POST http://localhost/bsc \
-H "Content-Type: application/json" \
-d '{
"jsonrpc":"2.0",
"method":"eth_getBalance",
"params":["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", "latest"],
"id":1
}'
|
节点管理
获取所有链
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
curl http://localhost/api/chains | jq
# 响应示例:
# {
# "code": 0,
# "msg": "success",
# "data": [
# {
# "chain_id": "ethereum",
# "chain_name": "Ethereum Mainnet",
# "description": "以太坊主网"
# }
# ]
# }
|
获取节点列表
1
2
3
4
5
|
# 获取以太坊的所有节点
curl http://localhost/api/nodes?chain_id=ethereum | jq
# 获取所有链的节点
curl http://localhost/api/nodes | jq
|
添加新节点
1
2
3
4
5
6
7
8
9
10
|
curl -X POST http://localhost/api/nodes \
-H "Content-Type: application/json" \
-d '{
"chain_id": "ethereum",
"node_url": "https://your-custom-rpc.com",
"provider": "Your Provider",
"priority": 10,
"weight": 5,
"is_active": true
}'
|
更新节点
1
2
3
4
5
6
7
8
9
|
# 禁用某个节点
curl -X PUT http://localhost/api/nodes/1 \
-H "Content-Type: application/json" \
-d '{"is_active": false}'
# 更新节点优先级
curl -X PUT http://localhost/api/nodes/1 \
-H "Content-Type: application/json" \
-d '{"priority": 5, "weight": 20}'
|
删除节点
1
|
curl -X DELETE http://localhost/api/nodes/1
|
获取节点统计
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
curl http://localhost/api/stats/nodes | jq
# 响应示例:
# {
# "code": 0,
# "msg": "success",
# "data": {
# "ethereum": {
# "total": 3,
# "healthy": 2
# },
# "bsc": {
# "total": 2,
# "healthy": 2
# }
# }
# }
|
前端集成示例
JavaScript/TypeScript
1
2
3
4
5
6
7
8
9
|
// 旧代码(直接连接RPC节点)
const provider = new ethers.JsonRpcProvider('https://eth.llamarpc.com');
// 新代码(使用CRPC代理,按链ID挂在根路径)
const provider = new ethers.JsonRpcProvider('http://localhost/ethereum');
// 就这么简单!无需其他修改
const blockNumber = await provider.getBlockNumber();
console.log('当前区块:', blockNumber);
|
Python
1
2
3
4
5
6
7
8
|
from web3 import Web3
# 使用CRPC代理
w3 = Web3(Web3.HTTPProvider('http://localhost/ethereum'))
# 无需其他修改
block_number = w3.eth.block_number
print(f'当前区块: {block_number}')
|
React 示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
import { ethers } from 'ethers';
// 配置文件
const RPC_ENDPOINTS = {
ethereum: 'http://localhost/ethereum',
bsc: 'http://localhost/bsc',
polygon: 'http://localhost/polygon',
};
// 创建Provider
function useProvider(chainId) {
const [provider, setProvider] = useState(null);
useEffect(() => {
const endpoint = RPC_ENDPOINTS[chainId];
if (endpoint) {
setProvider(new ethers.JsonRpcProvider(endpoint));
}
}, [chainId]);
return provider;
}
|
双监听模式(HTTP + Unix Socket)
CRPC支持**HTTP (TCP)** 和 Unix Socket 两种监听方式,可单独使用或同时启用。
为什么需要Unix Socket?
性能优势
当Nginx和CRPC在**同一台服务器**时,Unix Socket比TCP快得多:
| 对比项 |
TCP (localhost) |
Unix Socket |
提升 |
| 延迟 |
~50-100μs |
~5-10μs |
5-10倍 ⚡ |
| 吞吐量 |
高 |
极高 |
约30-40% ⬆️ |
| CPU占用 |
中 |
低 |
减少20-30% |
| 内核切换 |
需要TCP/IP栈 |
直接内核通信 |
无协议开销 |
原理
1
2
3
4
5
6
7
|
HTTP (TCP):
应用 → Socket → TCP层 → IP层 → 网卡驱动 → 回环 → 网卡驱动 → IP层 → TCP层 → Socket → 应用
↑ 需要完整的网络协议栈处理
Unix Socket:
应用 → VFS → Socket缓冲区 → VFS → 应用
↑ 直接内核通信,无需协议栈
|
使用场景
| 场景 |
推荐模式 |
原因 |
| Nginx和CRPC同一台服务器 |
Unix Socket |
性能最佳 ✅ |
| CRPC跨多台服务器部署 |
HTTP (TCP) |
Socket无法跨网络 |
| 混合部署(本地+远程) |
双监听模式 |
灵活性最高 |
| 开发调试 |
HTTP |
便于直接访问 |
配置示例
场景1:纯Socket模式(推荐,生产环境)
配置文件 (config/config.toml):
1
2
3
4
5
6
7
8
|
[server]
port = 8080
instance_id = "crpc-1"
enable_http = false # 关闭HTTP
enable_socket = true # 启用Socket
# ⚠️ 注意:生产环境不要把socket放在/tmp下,避免Nginx的PrivateTmp=true导致不可见
# 开发环境示例:socket_path = "/tmp/crpc-1.sock"
socket_path = "/srv/crpc/crpc-1.sock" # 生产环境推荐路径(与服务目录一致)
|
Nginx配置 (config/nginx.conf):
1
2
3
4
5
6
7
8
9
|
upstream crpc_backend {
least_conn;
# 生产环境推荐:与上面的socket_path保持一致
server unix:/srv/crpc/crpc-1.sock max_fails=3 fail_timeout=30s;
# 如需多实例,可配置 crpc-2.sock / crpc-3.sock
# server unix:/srv/crpc/crpc-2.sock max_fails=3 fail_timeout=30s;
# server unix:/srv/crpc/crpc-3.sock max_fails=3 fail_timeout=30s;
keepalive 64;
}
|
*启动*:
1
|
./scripts/start_multi_instance.sh 3 socket
|
场景2:纯HTTP模式
*配置文件*:
1
2
3
4
5
|
[server]
port = 8080
instance_id = "crpc-1"
enable_http = true # 启用HTTP
enable_socket = false # 关闭Socket
|
*Nginx配置*:
1
2
3
4
5
6
7
|
upstream crpc_backend {
least_conn;
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8081 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8082 max_fails=3 fail_timeout=30s;
keepalive 32;
}
|
*启动*:
1
|
./scripts/start_multi_instance.sh 3 http
|
场景3:双监听模式(最灵活)
*配置文件*:
1
2
3
4
5
6
|
[server]
port = 8080
instance_id = "crpc-1"
enable_http = true # 同时启用HTTP
enable_socket = true # 同时启用Socket
socket_path = "/tmp/crpc-1.sock"
|
*好处*:
- ✅ Nginx通过Socket访问(性能最佳)
- ✅ 可以直接HTTP调试:
curl http://localhost:8080/health
- ✅ 支持混合upstream(本地Socket + 远程HTTP)
*Nginx混合配置*:
1
2
3
4
5
6
7
8
9
10
11
12
|
upstream crpc_backend {
least_conn;
# 本地实例(Unix Socket,优先级高)
server unix:/tmp/crpc-1.sock max_fails=3 fail_timeout=30s weight=2;
server unix:/tmp/crpc-2.sock max_fails=3 fail_timeout=30s weight=2;
# 远程实例(HTTP,优先级低)
server 192.168.1.100:8080 max_fails=3 fail_timeout=30s weight=1;
keepalive 64;
}
|
*启动*:
1
|
./scripts/start_multi_instance.sh 3 both
|
验证和测试
验证Socket监听
1
2
3
4
5
6
7
8
9
10
11
|
# 查看socket文件
ls -lh /tmp/crpc-*.sock
# 输出:srw-rw-rw- 1 user group 0 Nov 17 10:00 /tmp/crpc-1.sock
# 测试socket连接(使用curl)
curl --unix-socket /tmp/crpc-1.sock http://localhost/health
# 输出:{"status":"ok","instance_id":"crpc-1"}
# 查看进程监听
ss -xl | grep crpc
# 输出:u_str LISTEN 0 128 /tmp/crpc-1.sock
|
性能测试
1
2
3
4
5
6
7
|
# 测试TCP性能
ab -n 10000 -c 100 http://localhost:8080/health
# 测试Socket性能(通过Nginx)
ab -n 10000 -c 100 http://localhost/health
# 对比QPS(Socket通常高20-30%)
|
故障排查
问题1:Socket文件不存在
*症状*:Nginx报错 connect() to unix:/tmp/crpc-1.sock failed (2: No such file or directory)
*解决*:
1
2
3
4
5
6
7
8
|
# 1. 检查CRPC是否启动
ps aux | grep crpc
# 2. 检查配置
grep enable_socket config/config.toml
# 3. 查看日志
tail -f log/crpc-1.log | grep -i socket
|
问题2:权限被拒绝
*症状*:Nginx报错 connect() to unix:/tmp/crpc-1.sock failed (13: Permission denied)
*解决*:
1
2
3
4
5
6
7
8
|
# 检查socket权限
ls -l /tmp/crpc-*.sock
# 应该是:srw-rw-rw-
# 如果不是,手动设置
chmod 666 /tmp/crpc-*.sock
# 或者修改代码中的chmod调用
|
问题3:旧socket文件占用
*症状*:启动失败,提示 address already in use
*解决*:
1
2
3
4
5
|
# 清理旧socket文件
rm -f /tmp/crpc-*.sock
# 重启服务
./scripts/start_multi_instance.sh 3 socket
|
最佳实践
- **生产环境推荐Socket**:Nginx和CRPC同服务器时,优先使用Unix Socket
- **Socket路径选择**:使用
/tmp/ 或 /var/run/,确保权限正确
- **定期清理**:重启时先清理旧socket文件
- **监控文件数**:
ulimit -n 确保足够的文件描述符
- **Keepalive调优**:Socket模式可以使用更大的keepalive值(64+)
- **备份方案**:同时启用HTTP,便于应急和调试
系统架构
整体架构图
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
┌─────────────────────────────────────────────┐
│ 前端应用 │
│ 只需要知道一个固定的代理域名 │
│ https://crpc.coinexus.life/{chain} │
└─────────────────┬───────────────────────────┘
│
↓
┌─────────────────────────────────────────────┐
│ Nginx 负载均衡器 │
│ - 使用 least_conn 策略 │
│ - 自动检测后端健康状态 │
│ - 失败自动重试其他实例 │
└──────────┬──────────────┬───────────────────┘
│ │
┌──────┴──────┬───────┴──────┬
↓ ↓ ↓
┌────────┐ ┌────────┐ ┌────────┐
│ CRPC-1 │ │ CRPC-2 │ │ CRPC-3 │
│ 实例1 │ │ 实例2 │ │ 实例3 │
│ 8080 │ │ 8081 │ │ 8082 │
└───┬────┘ └───┬────┘ └───┬────┘
│ │ │
└────────────┴──────────────┘
│
↓ (共享同一个数据库)
┌──────────────┐
│ PostgreSQL │
│ - 节点配置 │
│ - 健康状态 │
│ - 统计数据 │
└──────┬───────┘
│
↓ (转发到健康节点)
┌───────────────────────────┐
│ 多个RPC节点池 │
│ Ethereum: 3个节点 │
│ BSC: 2个节点 │
│ Polygon: 3个节点 │
└───────────────────────────┘
|
多实例实现原理
1. 无状态服务设计
所有CRPC实例都是 完全无状态 的:
- ✅ 不保存任何会话信息
- ✅ 不缓存任何节点数据(从数据库实时读取)
- ✅ 可以随时启动/停止任何实例
- ✅ 可以动态增加/减少实例数量
1
2
3
4
5
6
7
8
9
10
11
12
|
// 每次请求都从数据库获取最新的节点列表
func (p *Proxy) ForwardRequest(ctx context.Context, chainID string, rpcReq *models.RPCRequest) (*models.RPCResponse, error) {
// 从数据库实时获取健康节点
nodes, err := p.db.GetHealthyNodes(chainID)
if err != nil {
return nil, err
}
// 选择节点并转发请求
node := p.selectNode(nodes)
return p.sendRequest(ctx, node, rpcReq)
}
|
2. 共享数据库存储
所有状态和配置都存储在 PostgreSQL 中:
- *链配置*:chains 表
- *RPC节点配置*:rpc_nodes 表(URL、优先级、权重等)
- *节点健康状态*:is_healthy、failure_count、avg_response_time
- *健康检查日志*:health_check_logs 表
- *请求日志*:request_logs 表(可选)
3. Nginx 负载均衡
使用 least_conn 策略分发请求:
1
2
3
4
5
6
7
8
9
|
upstream crpc_backend {
least_conn; # 选择连接数最少的实例
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8081 max_fails=3 fail_timeout=30s;
server 127.0.0.1:8082 max_fails=3 fail_timeout=30s;
keepalive 32; # 保持长连接
}
|
*负载均衡策略*:
least_conn:选择当前连接数最少的实例(推荐)
round_robin:轮询(默认)
ip_hash:同一客户端IP总是转发到同一实例
weight:加权负载均衡
4. 分布式健康检查
每个实例独立运行健康检查,通过数据库更新节点状态。虽然多个实例都会检查,但这是有意为之:
- ✅ 提高可靠性(一个实例宕机不影响检查)
- ✅ 数据库写入量不大(每个节点10秒一次)
- ✅ 可以看到不同实例视角的健康状态
- ✅ 检查日志记录了
checked_by 字段区分来源
5. 自动故障切换
*实例级别故障切换*:
- 任意实例宕机,Nginx自动将流量转移到其他实例
- 连续3次失败标记为不可用30秒
- 自动恢复可用实例
*节点级别故障切换*:
- 只选择健康节点进行转发
- 请求失败自动重试其他节点
- 连续失败3次标记节点为不健康
工作流程
- 前端发送RPC请求到Nginx
- Nginx根据负载策略选择一个CRPC实例
- CRPC实例从数据库查询该链的健康节点
- 根据优先级和权重选择节点发送请求
- 请求失败自动重试其他节点
- 返回结果给前端
- 后台健康检查定期更新节点状态
为什么选择这个架构?
| 特性 |
我们的选择 |
原因 |
| 数据存储 |
PostgreSQL |
强一致性、完整ACID、SQL查询强大 |
| 负载均衡 |
Nginx |
性能优秀、配置简单、生态成熟 |
| 服务设计 |
无状态 |
易扩展、易维护、可随时重启 |
| 健康检查 |
分布式(不加锁) |
提高可靠性、避免单点故障 |
RPC节点配置
支持的区块链
系统预配置了以下主流区块链网络:
EVM链
-
以太坊系列
ethereum - Ethereum Mainnet
sepolia - Ethereum Sepolia 测试网
goerli - Ethereum Goerli 测试网
-
Layer 2
arbitrum - Arbitrum One L2主网
arbitrum-sepolia - Arbitrum Sepolia 测试网
optimism - Optimism L2主网
base - Coinbase Base L2
zksync - zkSync Era主网
-
BSC
bsc - BNB Smart Chain 主网
bsc-testnet - BSC 测试网
-
Polygon
polygon - Polygon 主网
polygon-mumbai - Polygon Mumbai 测试网
polygon-zkevm - Polygon zkEVM
-
其他EVM链
avalanche - Avalanche C-Chain
fantom - Fantom Opera
cronos - Cronos 主网
非EVM链
-
Solana
solana - Solana 主网
solana-testnet - Solana 测试网
solana-devnet - Solana 开发网
特殊端点说明
某些区块链网络提供多种类型的API端点,需要根据具体用途选择:
Ethereum 多端点支持
| 端点类型 |
用途 |
示例 |
| 执行层 RPC |
标准 eth_* 调用、交易、合约 |
/234582f3d3f0555c0c6817de16a0d346 |
| WebSocket |
实时订阅(新区块、交易等) |
wss://.../234582f3d3f0555c0c6817de16a0d346 |
| Beacon API |
PoS共识层、验证者、质押 |
/beacon/234582f3d3f0555c0c6817de16a0d346 |
*说明*:以太坊合并后分为两层
- **执行层**(Execution Layer):处理交易和智能合约
- **共识层**(Consensus Layer):PoS 共识和验证者管理
Tron 多端点支持
Tron网络提供三种不同的API接口:
| 端点类型 |
路径 |
用途 |
特点 |
| JSON-RPC |
/jsonrpc |
标准RPC调用 |
兼容以太坊RPC,通用接口 |
| Wallet API |
/wallet |
实时钱包操作 |
最新数据,适合发送交易 |
| Wallet Solidity |
/walletsolidity |
查询已确认数据 |
延迟约3秒,但数据稳定可靠 |
*使用建议*:
1
2
3
|
发送交易 → 使用 /wallet
查询余额/数据 → 使用 /walletsolidity(更可靠)
兼容性调用 → 使用 /jsonrpc
|
*权重分配*:
/jsonrpc - weight=20(标准接口)
/wallet - weight=18(实时操作)
/walletsolidity - weight=22(查询首选,最稳定)
节点提供商
Chainstack(付费)
*优势*:
- ⚡ 高性能、低延迟
- 🔒 专属资源
- ✅ 稳定性高
- 🌐 支持 HTTP + WebSocket
*配置*:
- Priority: 1(最高)
- Weight: 18-22(根据端点类型)
- 支持链:
- Arbitrum(HTTP + WS)
- Solana(HTTP + WS)
- BSC(HTTP + WS)
- Ethereum(HTTP + WS + Beacon API)
- Tron(JSON-RPC + Wallet + Wallet Solidity)
Infura(免费)
*优势*:
- 🆓 免费额度
- 📊 稳定性较好
- 🌐 支持 HTTP + WebSocket
- 🔧 易于使用
*限制*:
*配置*:
- Priority: 2
- Weight: 15
- 支持链:Ethereum、Sepolia、BSC
公共RPC节点
*优势*:
*限制*:
- ⚠️ 可能不稳定
- ⚠️ 可能有限流
- ⚠️ 延迟可能较高
*配置*:
- Priority: 3(最低)
- Weight: 8-10
- 作为备用节点
*提供商包括*:
- LlamaNodes
- Ankr
- PublicNode
- dRPC
- 各链官方节点
节点优先级说明
1
2
3
4
5
6
7
8
9
10
11
|
选择流程:
1. 筛选所有健康节点(is_healthy = true)
2. 按 priority 分组(值越小优先级越高)
3. 选择最小 priority 的节点组
4. 在该组内根据 weight 进行加权随机选择
5. 如果该组所有节点都故障,降级到下一个 priority 组
示例:
- 如果 Chainstack 节点健康 → 100% 使用 Chainstack
- 如果 Chainstack 故障 → 自动切换到 Infura
- 如果 Infura 也故障 → 自动切换到公共节点
|
节点管理
添加自定义节点
通过 API 添加节点:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
curl -X POST http://localhost:8080/api/nodes \
-H "Content-Type: application/json" \
-d '{
"chain_id": "ethereum",
"http_url": "https://your-node.example.com",
"ws_url": "wss://your-node.example.com",
"provider": "YourProvider",
"priority": 1,
"weight": 20,
"node_type": "both",
"api_key_type": "bearer",
"api_key": "your-api-key"
}'
|
修改节点优先级
1
2
3
4
5
6
|
curl -X PUT http://localhost:8080/api/nodes/{id} \
-H "Content-Type: application/json" \
-d '{
"priority": 1,
"weight": 25
}'
|
禁用节点
1
2
3
4
5
|
curl -X PUT http://localhost:8080/api/nodes/{id} \
-H "Content-Type: application/json" \
-d '{
"is_active": false
}'
|
查看节点状态
1
2
3
4
5
6
7
8
|
# 查看所有节点
curl http://localhost:8080/api/nodes
# 查看特定链的节点
curl http://localhost:8080/api/nodes?chain_id=ethereum
# 只看健康节点
curl http://localhost:8080/api/nodes?is_healthy=true
|
节点健康监控
系统会自动:
- 每30秒检查一次节点健康状态
- 记录响应时间(avg_response_time)
- 统计连续失败次数
- 自动识别并处理错误类型(链ID不匹配、认证失败、限流等)
- 达到阈值后自动禁用节点
API文档
所有管理接口返回统一格式:
1
2
3
4
5
|
{
"code": 0, // 0成功,1失败
"msg": "success",
"data": {} // 业务数据
}
|
RPC代理接口
POST /{chain_id}
在专用域名 crpc.coinexus.life 下,按链ID挂在根路径,例如:
- Ethereum:
POST https://crpc.coinexus.life/ethereum
- BSC:
POST https://crpc.coinexus.life/bsc
- Arbitrum:
POST https://crpc.coinexus.life/arbitrum
- Solana:
POST https://crpc.coinexus.life/solana
转发RPC请求到该链的健康节点。支持 单个请求 和 *批量请求*(JSON-RPC 2.0 规范)。
-
支持链与请求地址一览
统一规则:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
# ========== 主流 EVM 链 ==========
ethereum 以太坊主网
https://crpc.coinexus.life/ethereum
sepolia 以太坊 Sepolia 测试网
https://crpc.coinexus.life/sepolia
goerli 以太坊 Goerli 测试网
https://crpc.coinexus.life/goerli
bsc BNB Smart Chain 主网
https://crpc.coinexus.life/bsc
bsc-testnet BSC 测试网
https://crpc.coinexus.life/bsc-testnet
polygon Polygon 主网
https://crpc.coinexus.life/polygon
polygon-mumbai Polygon Mumbai 测试网
https://crpc.coinexus.life/polygon-mumbai
polygon-zkevm Polygon zkEVM 主网
https://crpc.coinexus.life/polygon-zkevm
avalanche Avalanche C-Chain 主网
https://crpc.coinexus.life/avalanche
fantom Fantom Opera 主网
https://crpc.coinexus.life/fantom
cronos Cronos 主网
https://crpc.coinexus.life/cronos
# ========== Layer 2 / Rollup ==========
arbitrum Arbitrum One 主网
https://crpc.coinexus.life/arbitrum
arbitrum-sepolia Arbitrum Sepolia 测试网
https://crpc.coinexus.life/arbitrum-sepolia
optimism Optimism 主网
https://crpc.coinexus.life/optimism
base Coinbase Base 主网
https://crpc.coinexus.life/base
zksync zkSync Era 主网
https://crpc.coinexus.life/zksync
# ========== 非 EVM 链 ==========
solana Solana 主网
https://crpc.coinexus.life/solana
solana-testnet Solana Testnet
https://crpc.coinexus.life/solana-testnet
solana-devnet Solana Devnet
https://crpc.coinexus.life/solana-devnet
tron Tron 主网(Chainstack JSON-RPC / Wallet / Solidity 端)
https://crpc.coinexus.life/tron
|
-
单个请求
最常见的RPC调用方式。
*请求示例*:
1
2
3
4
5
6
7
8
9
|
POST /ethereum
Content-Type: application/json
{
"jsonrpc": "2.0",
"method": "eth_blockNumber",
"params": [],
"id": 1
}
|
*响应示例*:
1
2
3
4
5
|
{
"jsonrpc": "2.0",
"result": "0x1234567",
"id": 1
}
|
-
批量请求(Batch Request)
✅ CRPC 完全支持 JSON-RPC 2.0 批量请求规范,与所有主流RPC客户端库兼容(ethers.js, web3.js, viem等)。
**优势**:
- 🚀 *性能提升*:1次网络往返完成多个RPC调用,延迟降低60-80%
- ⚡ *并发处理*:CRPC内部并发转发批量请求中的每个调用,充分利用多核CPU
- 💰 *节省成本*:减少HTTP连接数和网络开销
- 🔧 *标准兼容*:符合JSON-RPC 2.0规范,所有上游节点都支持
**批量请求示例**:
1
2
3
4
5
6
7
8
|
POST /ethereum
Content-Type: application/json
[
{"jsonrpc":"2.0","id":1,"method":"eth_blockNumber","params":[]},
{"jsonrpc":"2.0","id":2,"method":"eth_gasPrice","params":[]},
{"jsonrpc":"2.0","id":3,"method":"eth_chainId","params":[]}
]
|
**批量响应示例**:
1
2
3
4
5
|
[
{"jsonrpc":"2.0","id":1,"result":"0x1234567"},
{"jsonrpc":"2.0","id":2,"result":"0x3b9aca00"},
{"jsonrpc":"2.0","id":3,"result":"0x1"}
]
|
**批量账户余额查询示例**:
1
2
3
4
5
|
[
{"jsonrpc":"2.0","id":1,"method":"eth_getBalance","params":["0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045","latest"]},
{"jsonrpc":"2.0","id":2,"method":"eth_getBalance","params":["0x8B3192f5eEBD8579568A2Ed41E6FEB402f93f73F","latest"]},
{"jsonrpc":"2.0","id":3,"method":"eth_getBalance","params":["0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb","latest"]}
]
|
**批量请求限制**:
为防止恶意超大批量请求,CRPC设置了批量请求大小限制:
| 配置项 |
默认值 |
说明 |
max_batch_size |
100 |
单次批量请求最大调用数 |
超过限制的请求将返回错误:
1
2
3
4
5
|
{
"code": 1,
"msg": "批量请求数量超过限制 (最大: 100, 实际: 150)",
"data": null
}
|
**使用场景**:
| 场景 |
说明 |
示例 |
| 网络检测 |
ethers.js初始化时自动发送批量请求检测网络 |
eth_chainId + net_version |
| 批量查询余额 |
一次查询多个钱包地址的余额 |
多个 eth_getBalance |
| 数据聚合 |
获取多种链上数据(区块、Gas价格、Nonce等) |
eth_blockNumber + eth_gasPrice + eth_getTransactionCount |
| 批量读取交易 |
一次获取多笔交易详情 |
多个 eth_getTransactionByHash |
**测试批量请求**:
1
2
3
|
# 运行批量请求测试脚本
cd /home/smokey/Desktop/crpc
./scripts/test_batch_requests.sh
|
该脚本会测试:
- ✅ 小型批量(2个):网络检测
- ✅ 中型批量(5个):多个只读查询
- ✅ 批量余额查询(10个地址)
- ✅ 混合查询(不同方法)
- ✅ 大批量(20个):压力测试
- ✅ 超大批量(150个):限流测试
- ✅ 性能对比:批量 vs 单个请求
管理接口
GET /health
健康检查接口。
*响应示例*:
1
2
3
4
|
{
"status": "ok",
"instance_id": "crpc-1"
}
|
GET /api/chains
获取所有链列表。
GET /api/chains/{chain_id}
获取单个链信息。
GET /api/nodes
获取节点列表。
*查询参数*:
GET /api/nodes/{id}
获取单个节点详情。
POST /api/nodes
创建新节点。
*请求体*:
1
2
3
4
5
6
7
8
|
{
"chain_id": "ethereum",
"node_url": "https://rpc.example.com",
"provider": "Example",
"priority": 10,
"weight": 5,
"is_active": true
}
|
PUT /api/nodes/{id}
更新节点信息。
*请求体*:
1
2
3
4
5
|
{
"priority": 5,
"weight": 10,
"is_active": false
}
|
DELETE /api/nodes/{id}
删除节点。
GET /api/stats/nodes
获取节点统计信息。
*响应示例*:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
{
"code": 0,
"msg": "success",
"data": {
"ethereum": {
"total": 3,
"healthy": 2
},
"bsc": {
"total": 2,
"healthy": 2
}
}
}
|
配置说明
config.toml 详解
配置文件位于 config/config.toml。
Server配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
[server]
port = 8080 # HTTP服务端口
mode = "release" # 运行模式: debug, release
read_timeout = 30 # 读取超时(秒)
write_timeout = 30 # 写入超时(秒)
instance_id = "crpc-1" # 实例ID,多实例需不同
# 监听模式配置(新增)
enable_http = true # 是否启用HTTP监听(TCP)
enable_socket = false # 是否启用Unix Socket监听
# ⚠️ 开发环境示例:可以使用/tmp,便于本机调试
# socket_path = "/tmp/crpc.sock" # 开发环境示例
# 生产环境请避免使用/tmp(Nginx通常启用PrivateTmp=true,看不到/tmp里的socket)
# 推荐放在服务目录或/run下,例如:
# socket_path = "/srv/crpc/crpc.sock"
# socket_path = "/run/crpc.sock"
socket_path = "/tmp/crpc.sock" # 默认值(开发环境友好),生产环境请按上面说明修改
|
*多实例配置*:
- 可通过环境变量
CRPC_INSTANCE_ID 覆盖 instance_id
- 每个实例必须使用不同的端口和ID(HTTP模式)
- 每个实例必须使用不同的socket路径(Socket模式)
*监听模式选择*:
enable_http = true, enable_socket = false:仅HTTP(跨服务器)
enable_http = false, enable_socket = true:仅Socket(同服务器,推荐)
enable_http = true, enable_socket = true:双监听(最大灵活性)
- 至少需要启用一种模式
Database配置
1
2
3
4
5
6
7
8
9
10
|
[database]
host = "localhost"
port = 5432
user = "crpc"
password = "crpc_password"
dbname = "crpc"
sslmode = "disable"
max_open_conns = 25 # 最大打开连接数
max_idle_conns = 5 # 最大空闲连接数
conn_max_lifetime = 300 # 连接最大生命周期(秒)
|
健康检查配置
1
2
3
4
5
6
|
[health_check]
enabled = true # 是否启用健康检查
interval = 10 # 检查间隔(秒)
timeout = 5 # RPC请求超时(秒)
max_failures = 3 # 连续失败次数阈值
recovery_interval = 30 # 不健康节点重新检查间隔(秒)
|
代理配置
1
2
3
4
5
6
|
[proxy]
retry_count = 2 # 失败重试次数
request_timeout = 30 # 请求超时(秒)
enable_cache = false # 是否启用缓存(未实现)
cache_ttl = 60 # 缓存TTL(秒)
max_batch_size = 100 # 批量请求最大数量(防止恶意超大批量请求)
|
*说明*:
max_batch_size:限制单次批量请求(Batch Request)的最大调用数
- 超过此限制的批量请求将被拒绝并返回错误
- 默认值100对大多数场景足够,可根据实际需求调整
- 设置为0表示使用默认值100
日志配置
1
2
3
4
|
[log]
level = "info" # 日志级别: debug, info, warn, error
output = "stdout" # 输出位置: stdout, file
file_path = "../log/crpc.log" # 日志文件路径
|
日志清理配置
1
2
3
4
|
[log_cleanup]
enabled = true # 是否启用自动清理
interval = 24 # 清理间隔(小时),默认每24小时
retention_days = 7 # 日志保留天数,删除N天前的日志
|
*说明*:
- 应用启动时自动开始清理任务
- 清理任务在后台运行,不影响主服务
- 可通过API手动触发清理:
POST /api/logs/cleanup
- 可通过API查看日志统计:
GET /api/stats/logs
- 无需重启PostgreSQL数据库
数据库Schema
数据库Schema定义在 migration/001_init_schema.sql。
主要表结构
*chains表*:链信息
chain_id:链ID(如ethereum、bsc)
chain_name:链名称
description:描述
*rpc_nodes表*:RPC节点配置
chain_id:所属链
node_url:节点URL
provider:提供商
priority:优先级(数字越小优先级越高)
weight:负载均衡权重
is_active:是否启用
is_healthy:是否健康
failure_count:连续失败次数
success_count:成功次数
avg_response_time:平均响应时间(毫秒)
*health_check_logs表*:健康检查日志
node_id:节点ID
is_healthy:检查结果
response_time:响应时间
error_message:错误信息
checked_by:检查的实例ID
*request_logs表*:请求日志(可选)
chain_id:链ID
node_id:使用的节点
method:RPC方法
success:是否成功
response_time:响应时间
instance_id:处理请求的实例ID
部署指南
生产环境部署(使用Systemd)
1. 准备环境
1
2
3
4
5
6
7
8
9
10
|
# 创建部署目录
sudo mkdir -p /opt/crpc
sudo chown $USER:$USER /opt/crpc
# 复制项目文件
cp -r /home/smokey/Desktop/crpc/* /opt/crpc/
# 编译程序
cd /opt/crpc
make build
|
2. 创建Systemd服务
创建 /etc/systemd/system/crpc@.service:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
[Unit]
Description=CRPC Instance %i
After=network.target postgresql.service
[Service]
Type=simple
User=crpc
WorkingDirectory=/opt/crpc/program
Environment="CRPC_INSTANCE_ID=crpc-%i"
ExecStart=/opt/crpc/program/crpc
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
|
3. 创建配置文件
为每个实例创建配置:
1
2
3
4
5
6
7
8
9
10
11
12
|
# 实例1(端口8080)
cp /opt/crpc/config/config.toml /opt/crpc/config/config.instance1.toml
# 实例2(端口8081)
cp /opt/crpc/config/config.toml /opt/crpc/config/config.instance2.toml
sed -i 's/port = 8080/port = 8081/' /opt/crpc/config/config.instance2.toml
sed -i 's/instance_id = "crpc-1"/instance_id = "crpc-2"/' /opt/crpc/config/config.instance2.toml
# 实例3(端口8082)
cp /opt/crpc/config/config.toml /opt/crpc/config/config.instance3.toml
sed -i 's/port = 8080/port = 8082/' /opt/crpc/config/config.instance3.toml
sed -i 's/instance_id = "crpc-1"/instance_id = "crpc-3"/' /opt/crpc/config/config.instance3.toml
|
4. 启动服务
1
2
3
4
5
6
7
8
9
|
# 启动实例
sudo systemctl enable crpc@1 crpc@2 crpc@3
sudo systemctl start crpc@1 crpc@2 crpc@3
# 查看状态
sudo systemctl status crpc@*
# 查看日志
sudo journalctl -u crpc@1 -f
|
5. 配置Nginx
1
2
3
4
5
6
7
8
9
|
# 复制Nginx配置
sudo cp /opt/crpc/config/nginx.conf /etc/nginx/nginx.conf
# 测试配置
sudo nginx -t
# 重启Nginx
sudo systemctl restart nginx
sudo systemctl enable nginx
|
添加新链
通过SQL添加
1
2
3
4
5
6
7
8
9
10
11
12
13
|
-- 连接数据库
psql -h localhost -U crpc -d crpc
-- 添加新链
INSERT INTO chains (chain_id, chain_name, description)
VALUES ('arbitrum', 'Arbitrum One', 'Arbitrum Layer 2网络');
-- 添加节点
INSERT INTO rpc_nodes (chain_id, node_url, provider, priority, weight)
VALUES
('arbitrum', 'https://arb1.arbitrum.io/rpc', 'Official', 1, 10),
('arbitrum', 'https://rpc.ankr.com/arbitrum', 'Ankr', 2, 10),
('arbitrum', 'https://arbitrum.llamarpc.com', 'LlamaNodes', 3, 5);
|
通过API添加
1
2
3
4
5
6
7
8
9
10
11
|
# 先手动添加链到数据库,然后通过API添加节点
curl -X POST http://localhost/api/nodes \
-H "Content-Type: application/json" \
-d '{
"chain_id": "arbitrum",
"node_url": "https://arb1.arbitrum.io/rpc",
"provider": "Official",
"priority": 1,
"weight": 10,
"is_active": true
}'
|
自动化部署脚本
项目提供了三个自动化部署脚本,简化生产环境部署:
1. 单实例远程部署 (deploy_to_remote.sh)
快速编译并部署单实例到远程服务器:
1
2
3
4
5
6
7
8
9
10
11
|
cd scripts
# 部署到默认服务器(d1.smokey)
./deploy_to_remote.sh
# 脚本会自动:
# 1. 本地编译应用
# 2. 上传二进制文件到远程服务器
# 3. 询问是否更新配置文件
# 4. 重启远程服务
# 5. 验证服务健康状态
|
*适用场景*:
2. 多实例远程部署 (deploy_multi_instance.sh)
一键在远程服务器部署多个CRPC实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
cd scripts
# 部署3个实例到 d1.smokey(默认)
./deploy_multi_instance.sh
# 部署5个实例到指定服务器
./deploy_multi_instance.sh your-server 5
# 脚本会自动:
# 1. 编译应用程序
# 2. 上传二进制文件
# 3. 为每个实例生成独立配置(端口 8080-808N)
# 4. 创建 systemd 服务单元 (crpc@1, crpc@2, ...)
# 5. 更新 Nginx 负载均衡配置
# 6. 启动所有实例并验证
|
*生成的服务*:
crpc@1.service (端口 8080)
crpc@2.service (端口 8081)
crpc@3.service (端口 8082)
- …
*管理命令*:
1
2
3
4
5
6
7
8
9
10
11
|
# 查看所有实例状态
ssh d1.smokey systemctl status 'crpc@*'
# 重启所有实例
ssh d1.smokey systemctl restart 'crpc@*'
# 停止单个实例
ssh d1.smokey systemctl stop crpc@2
# 查看实例日志
ssh d1.smokey journalctl -u crpc@1 -f
|
*适用场景*:
- 生产环境高可用部署
- 需要负载均衡的场景
- 服务器资源充足(多核CPU)
3. 性能基准测试 (benchmark.sh)
全面测试CRPC服务性能:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
cd scripts
# 测试默认服务器(192.168.1.21)
./benchmark.sh
# 测试指定地址,10并发,100请求
./benchmark.sh http://your-server 10 100
# 测试内容包括:
# 1. 健康检查端点延迟测试
# 2. RPC代理响应时间测试(eth_blockNumber)
# 3. API管理接口功能测试
# 4. 负载均衡验证(实例分配统计)
# 5. 并发压力测试(需要 ab 工具)
# 6. 节点和日志统计信息
|
*典型输出*:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
============================================
📊 CRPC 性能测试
============================================
目标地址: http://192.168.1.21
并发数: 10
请求数: 100
============================================
测试1: 健康检查端点
GET http://192.168.1.21/health
请求 1: {"instance_id":"crpc-1","status":"ok"} (耗时: 0.001ms)
...
测试2: RPC代理 - eth_blockNumber
✅ 请求 1: 成功 - 区块高度 0x16b77f2 (耗时: 0.5s)
...
统计结果:
成功: 20
失败: 0
平均响应时间: 0.45s
测试4: 负载均衡测试
实例分配统计:
crpc-1: 4 次请求
crpc-2: 3 次请求
crpc-3: 3 次请求
|
4. 实时监控面板 (monitor.sh)
实时监控CRPC服务运行状态:
1
2
3
4
5
6
7
8
9
|
cd scripts
# 监控默认服务器,每5秒刷新
./monitor.sh
# 监控指定服务器,每3秒刷新
./monitor.sh your-server 3
# 按 Ctrl+C 退出监控
|
*监控内容*:
- 🔧 *服务状态*:所有CRPC实例运行状态
- 🏥 *健康检查*:Nginx代理健康状态
- 🌐 *RPC节点统计*:总节点、健康节点、异常节点数量
- 📝 *日志统计*:请求日志、健康检查日志数量
- 🚦 *Nginx状态*:活跃连接、总请求数
- 💻 *系统资源*:CPU、内存、磁盘使用情况
- 🚨 *错误日志*:最近5分钟的错误(如有)
*监控面板示例*:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
============================================
📊 CRPC 服务监控面板
============================================
主机: d1.smokey
刷新间隔: 5s
时间: 2025-11-17 19:30:45
============================================
🔧 服务状态
----------------------------------------
crpc@1.service: ✅ 运行中
crpc@2.service: ✅ 运行中
crpc@3.service: ✅ 运行中
🏥 健康检查
----------------------------------------
Nginx 代理: ✅ 正常
响应: {"instance_id":"crpc-2","status":"ok"}
🌐 RPC 节点统计
----------------------------------------
总节点数: 15
健康节点: 13
异常节点: 2
💻 系统资源
----------------------------------------
CPU 使用率: 8.5%
内存: 已用: 1850MB / 总计: 3950MB (46.8%)
磁盘: 已用: 12.5G / 总计: 50G (26%)
|
部署流程建议
*初次部署生产环境*:
1
2
3
4
5
6
7
8
9
|
# 步骤1:部署多实例
cd scripts
./deploy_multi_instance.sh your-server 3
# 步骤2:运行性能测试
./benchmark.sh http://your-server
# 步骤3:启动实时监控(另开终端)
./monitor.sh your-server 5
|
*日常更新*:
1
2
3
|
# 快速重新部署(已配置好systemd和Nginx)
cd scripts
./deploy_to_remote.sh
|
监控和运维
健康检查
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# 检查Nginx状态
curl http://localhost/health
# 检查特定实例(直连)
curl http://localhost:8080/health
curl http://localhost:8081/health
curl http://localhost:8082/health
# 检查所有实例
for i in 8080 8081 8082; do
echo "实例端口$i:"
curl -s http://localhost:$i/health | jq
done
|
日志查看
1
2
3
4
5
6
7
8
9
10
|
# 本地运行
tail -f log/crpc.log
# Systemd管理的服务
sudo journalctl -u crpc@1 -f
sudo journalctl -u crpc@2 -f
sudo journalctl -u crpc@3 -f
# 查看所有实例日志
sudo journalctl -u 'crpc@*' -f
|
数据库监控
查看节点健康状态
1
2
3
4
5
6
7
8
|
SELECT
chain_id,
COUNT(*) as total,
SUM(CASE WHEN is_healthy THEN 1 ELSE 0 END) as healthy,
SUM(CASE WHEN is_healthy THEN 0 ELSE 1 END) as unhealthy
FROM rpc_nodes
WHERE is_active = true
GROUP BY chain_id;
|
查看最近的健康检查日志
1
2
3
4
5
6
7
8
9
10
11
|
SELECT
n.chain_id,
n.node_url,
h.is_healthy,
h.response_time,
h.checked_by,
h.checked_at
FROM health_check_logs h
JOIN rpc_nodes n ON h.node_id = n.id
ORDER BY h.checked_at DESC
LIMIT 20;
|
查看请求统计
1
2
3
4
5
6
7
8
|
SELECT
chain_id,
COUNT(*) as total_requests,
SUM(CASE WHEN success THEN 1 ELSE 0 END) as success_count,
AVG(response_time) as avg_response_time
FROM request_logs
WHERE created_at > NOW() - INTERVAL '1 hour'
GROUP BY chain_id;
|
日志管理与自动清理
系统提供了**应用内置的自动清理功能**,无需安装额外的PostgreSQL扩展,也**无需重启数据库**。
应用内置自动清理(推荐✅)
CRPC应用内置了后台定时清理任务,自动管理日志数据。
-
工作原理
- ✅ 应用启动时,自动创建后台清理任务
- ✅ 按配置的间隔(默认24小时)自动执行清理
- ✅ 应用停止时,清理任务自动停止
- ✅ 完全在Go应用内部实现,无需外部依赖
- ✅ 不需要重启PostgreSQL数据库
-
API接口
**查看日志统计**:
1
|
curl http://localhost:8080/api/stats/logs
|
返回示例:
1
2
3
4
5
6
7
8
9
10
|
{
"code": 0,
"msg": "success",
"data": {
"request_logs_count": 12345,
"health_check_logs_count": 67890,
"oldest_request_log": "2024-11-10T10:30:00Z",
"oldest_health_log": "2024-11-10T10:30:00Z"
}
}
|
**手动触发清理**:
1
|
curl -X POST http://localhost:8080/api/logs/cleanup
|
- 优点
| 特性 |
说明 |
| ✅ 无需外部依赖 |
不需要安装pg_cron扩展 |
| ✅ 无需重启数据库 |
不影响生产环境其他服务 |
| ✅ 易于管理 |
通过配置文件控制 |
| ✅ 自动启停 |
跟随应用生命周期 |
| ✅ 日志可见 |
清理过程记录在应用日志中 |
| ✅ API支持 |
可手动触发和查看统计 |
监控日志增长
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
-- 查看表大小和行数
SELECT
schemaname,
tablename,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size,
n_live_tup AS row_count
FROM pg_stat_user_tables
WHERE tablename IN ('request_logs', 'health_check_logs')
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
-- 查看每天的日志增长
SELECT
DATE(created_at) as date,
COUNT(*) as request_count
FROM request_logs
WHERE created_at > NOW() - INTERVAL '30 days'
GROUP BY DATE(created_at)
ORDER BY date DESC;
|
性能优化
数据库优化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
-- 定期清理旧日志(保留7天)
DELETE FROM health_check_logs WHERE checked_at < NOW() - INTERVAL '7 days';
DELETE FROM request_logs WHERE created_at < NOW() - INTERVAL '7 days';
-- 创建定时任务
CREATE EXTENSION IF NOT EXISTS pg_cron;
SELECT cron.schedule(
'cleanup-old-logs',
'0 2 * * *', -- 每天凌晨2点
$$
DELETE FROM health_check_logs WHERE checked_at < NOW() - INTERVAL '7 days';
DELETE FROM request_logs WHERE created_at < NOW() - INTERVAL '7 days';
$$
);
|
禁用请求日志(可选)
如果不需要详细的请求日志,可以注释掉代码中的日志记录:
1
2
|
# 编辑 program/proxy/proxy.go
# 注释掉 CreateRequestLog 相关调用
|
Nginx优化
编辑 config/nginx.conf:
1
2
3
4
5
6
7
8
|
# 增加worker进程
worker_processes auto;
# 增加keepalive连接
keepalive 64;
# 增加连接超时
keepalive_timeout 65;
|
故障排查
实例无法启动
检查配置文件
1
2
3
|
cd program
./crpc
# 查看错误信息
|
检查数据库连接
1
|
psql -h localhost -U crpc -d crpc -c "SELECT 1;"
|
检查端口占用
1
2
3
4
|
# 检查端口是否被占用
netstat -tunlp | grep 8080
netstat -tunlp | grep 8081
netstat -tunlp | grep 8082
|
节点始终不健康
查看节点详情
1
|
SELECT * FROM rpc_nodes WHERE id = 1;
|
查看健康检查日志
1
2
3
4
|
SELECT * FROM health_check_logs
WHERE node_id = 1
ORDER BY checked_at DESC
LIMIT 10;
|
手动测试节点
1
2
3
|
curl -X POST https://eth.llamarpc.com \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
|
手动重置节点状态
1
2
3
|
UPDATE rpc_nodes
SET is_healthy = true, failure_count = 0
WHERE id = 1;
|
负载不均衡
检查Nginx配置
1
2
|
sudo nginx -t
curl http://localhost/nginx_status
|
检查实例运行状态
1
2
3
4
5
|
# Systemd
sudo systemctl status crpc@1 crpc@2 crpc@3
# 直接检查进程
ps aux | grep crpc
|
查看请求分布
1
2
3
4
5
6
|
SELECT
instance_id,
COUNT(*) as request_count
FROM request_logs
WHERE created_at > NOW() - INTERVAL '1 hour'
GROUP BY instance_id;
|
数据库连接池耗尽
如果看到 “too many connections” 错误:
1
2
3
4
|
# 编辑 config/config.toml
[database]
max_open_conns = 50 # 增加最大连接数
max_idle_conns = 10 # 增加空闲连接数
|
常见问题
Q: 如何实现零停机更新?
A: 使用滚动更新策略:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
# 1. 更新实例1
sudo systemctl stop crpc@1
cd /opt/crpc/program
go build -o crpc .
sudo systemctl start crpc@1
sleep 10 # 等待健康检查通过
# 2. 更新实例2
sudo systemctl stop crpc@2
sudo systemctl start crpc@2
sleep 10
# 3. 更新实例3
sudo systemctl stop crpc@3
sudo systemctl start crpc@3
|
Nginx会自动将流量切到健康实例。
Q: 实例数量如何选择?
A: 根据请求量选择:
- 低流量(<1000 req/s):2-3个实例
- 中流量(1000-5000 req/s):3-5个实例
- 高流量(>5000 req/s):5+个实例
建议至少3个实例保证高可用。
Q: 数据库会成为瓶颈吗?
A: 当前设计下,数据库压力很小:
- 读操作:每个RPC请求查询一次节点列表(可以加索引)
- 写操作:健康检查每10秒一次
如果成为瓶颈,可以:
- 增加数据库连接池
- 使用PostgreSQL只读从库(读写分离)
- 添加Redis缓存层
- 定期清理历史日志
Q: 如何跨数据中心部署?
A: 可以在每个数据中心部署独立的CRPC集群:
- 每个数据中心独立的CRPC实例 + PostgreSQL
- 数据库使用主从复制或多主复制同步配置
- 使用全局负载均衡器(CloudFlare、AWS Route53)分发流量
- 每个集群独立运行健康检查
Q: 如何添加认证?
A: 可以在API层添加中间件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// program/api/handler.go
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("X-API-Key")
if token == "" || !validateToken(token) {
c.JSON(401, gin.H{"error": "Unauthorized"})
c.Abort()
return
}
c.Next()
}
}
// 在SetupRoutes中使用
api := r.Group("/api")
api.Use(AuthMiddleware())
{
// ... 路由定义
}
|
Q: 如何监控节点的实时状态?
A: 创建一个监控脚本:
1
2
3
|
#!/bin/bash
# 每5秒刷新一次节点状态
watch -n 5 "curl -s http://localhost/api/stats/nodes | jq"
|
或者集成Prometheus + Grafana(未来实现)。
Q: 某个节点经常不稳定怎么办?
A: 几个处理方法:
- 降低该节点的优先级:
1
2
3
|
curl -X PUT http://localhost/api/nodes/{id} \
-H "Content-Type: application/json" \
-d '{"priority": 99, "weight": 1}'
|
- 临时禁用该节点:
1
2
3
|
curl -X PUT http://localhost/api/nodes/{id} \
-H "Content-Type: application/json" \
-d '{"is_active": false}'
|
- 更换为其他RPC提供商的节点
性能指标
单实例性能
- RPC请求:~1000-5000 req/s
- 数据库连接:25个(可调整)
- 内存占用:~50-100MB
- CPU占用:~10-30%(取决于请求量)
3实例集群
- 总吞吐:~3000-15000 req/s
- 高可用:任意1个实例宕机不影响服务
- 数据库压力:很小(读多写少)
响应时间
- 健康检查:~1ms (Nginx本地代理)
- RPC转发:+10-50ms(取决于后端RPC节点)
- API管理接口:<100ms
性能测试结果
基于 d1.smokey (192.168.1.21) 的实际测试结果:
健康检查性能
- 延迟:**0.7-1.2ms** (通过Nginx代理)
- 稳定性:100% 成功率
- 吞吐:>10000 req/s
RPC代理性能
- 首次请求:~9s(健康检查初始化)
- 后续请求:~0.5-2s(取决于上游节点)
- 成功率:>95%(取决于上游节点健康度)
资源消耗
- 单实例内存:~50MB
- 单实例CPU:<5%(空闲)、10-30%(负载)
性能优化措施
本项目已实施以下性能优化:
1. Nginx层优化
连接池 (keepalive)
1
2
3
|
upstream crpc_backend {
keepalive 64; # 保持64个长连接到后端
}
|
Gzip压缩
1
2
3
|
gzip on;
gzip_comp_level 6;
gzip_types application/json;
|
- ✅ 减少网络传输量(JSON响应压缩 ~70%)
- ✅ 适合API和RPC响应
请求缓冲优化
1
2
3
|
proxy_buffering on;
proxy_buffer_size 8k;
proxy_buffers 8 8k;
|
- ✅ 避免后端慢速影响Nginx
- ✅ 支持大型RPC响应
负载均衡算法
1
2
3
4
|
upstream crpc_backend {
least_conn; # 最少连接算法
server localhost:8080 max_fails=3 fail_timeout=30s;
}
|
- ✅ 智能分配请求到最空闲实例
- ✅ 自动故障转移(3次失败后30秒超时)
2. Go应用层优化
数据库连接池
1
2
3
4
|
[database]
max_open_conns = 50 # 最大连接数
max_idle_conns = 10 # 空闲连接池
conn_max_lifetime = 3600 # 连接复用时长(秒)
|
结构化日志 (zap)
HTTP/1.1 长连接
1
2
|
proxy_http_version 1.1;
proxy_set_header Connection "";
|
- ✅ 复用HTTP连接到上游RPC节点
- ✅ 减少握手延迟
3. 数据库优化
索引优化
1
2
3
4
5
6
7
8
9
|
-- 链ID索引(常用查询)
CREATE INDEX idx_nodes_chain ON rpc_nodes(chain_id);
-- 健康节点查询索引(组合索引)
CREATE INDEX idx_nodes_healthy ON rpc_nodes(chain_id, is_healthy, is_active);
-- 日志时间索引(清理操作)
CREATE INDEX idx_request_logs_time ON request_logs(created_at);
CREATE INDEX idx_health_logs_time ON health_check_logs(checked_at);
|
自动日志清理
- ✅ 应用内置定时清理(避免PostgreSQL重启)
- ✅ 保留7天日志(可配置)
- ✅ 每24小时自动清理一次
4. 架构优化
多实例部署
- ✅ 水平扩展能力(3实例 = 3倍吞吐量)
- ✅ 高可用(单实例故障不影响服务)
健康检查与故障切换
- ✅ 每30秒检查一次节点健康
- ✅ 自动禁用故障节点
- ✅ 故障节点恢复后自动启用
智能节点选择
- ✅ 优先级 + 权重 + 平均响应时间
- ✅ 自动避开慢速/故障节点
- ✅ 支持多节点重试
5. 未来优化方向
缓存层
1
2
3
4
|
[计划中] 添加 Redis 缓存
- 缓存 eth_blockNumber 等轻量查询(5秒TTL)
- 缓存节点列表(减少数据库查询)
- 预期提升:50-100% 吞吐量
|
连接复用池
1
2
3
4
|
[计划中] 上游RPC节点连接池
- 预建立和复用HTTP/WebSocket连接
- 减少TLS握手延迟
- 预期提升:20-50ms 延迟降低
|
分布式追踪
1
2
3
4
|
[计划中] 集成 OpenTelemetry
- 端到端请求追踪
- 性能瓶颈分析
- 更精准的监控告警
|
安全建议
1. 数据库安全
- 使用强密码
- 限制数据库访问IP
- 启用SSL连接
- 定期备份
2. API安全
- 添加认证中间件(JWT或API Key)
- 配置CORS策略
- 限制管理接口访问(内网或VPN)
- 实现请求限流
3. 网络安全
- 使用HTTPS(配置SSL证书)
- 配置防火墙规则
- 定期更新系统和依赖
- 监控异常流量
4. 监控告警
- 节点故障告警
- 实例宕机告警
- 数据库连接异常告警
- 请求成功率监控
开发计划
已完成
计划中
下一步目标(按优先级)
-
性能与稳定性强化(已在生产上线的基础上迭代)
-
可观测性与告警体系
-
安全加固(控制面)
-
缓存与速率控制
-
WebSocket 与实时能力
-
开发者体验与运营
WebSocket支持
CRPC已完成WebSocket支持的**数据库层和代码框架**,默认禁用,可按需启用。
当前状态
- ✅ **数据库支持**:已添加
ws_url 和 node_type 字段
- ✅ **数据模型**:完整支持
- ✅ **代码框架**:WebSocket代理模块已创建
- ⏸️ **功能状态**:默认禁用(
config.toml 中 websocket.enabled = false)
快速启用
1
2
3
4
5
6
7
8
9
|
# 1. 运行WebSocket迁移
make migrate-ws
# 2. 启用配置(编辑 config/config.toml)
[websocket]
enabled = true
# 3. 重启服务
make build && make run
|
客户端使用
JavaScript/TypeScript (Ethers.js)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
import { ethers } from 'ethers';
// HTTP Provider(查询操作)
const httpProvider = new ethers.JsonRpcProvider('http://localhost/ethereum');
// WebSocket Provider(订阅操作)
const wsProvider = new ethers.WebSocketProvider('ws://localhost/ws/ethereum');
// 1. 订阅新区块
wsProvider.on('block', (blockNumber) => {
console.log('新区块:', blockNumber);
});
// 2. 监听智能合约事件
const contract = new ethers.Contract(contractAddress, abi, wsProvider);
contract.on('Transfer', (from, to, amount) => {
console.log('转账:', from, '->', to, amount.toString());
});
|
Python (Web3.py)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
from web3 import Web3
from web3.providers.websocket import WebsocketProvider
# 创建WebSocket连接
ws_provider = WebsocketProvider('ws://localhost/ws/ethereum')
w3 = Web3(ws_provider)
# 订阅新区块
def handle_block(block_hash):
block = w3.eth.get_block(block_hash)
print(f'新区块: {block.number}')
block_filter = w3.eth.filter('latest')
w3.eth.watch_filter(block_filter, handle_block)
|
WebSocket节点配置
Infura
1
2
3
4
5
6
|
INSERT INTO rpc_nodes (chain_id, http_url, ws_url, provider, priority, weight, node_type, api_key_type)
VALUES
('ethereum',
'https://mainnet.infura.io/v3/YOUR_PROJECT_ID',
'wss://mainnet.infura.io/ws/v3/YOUR_PROJECT_ID',
'Infura', 1, 15, 'both', 'none');
|
Alchemy
1
2
3
4
5
6
|
INSERT INTO rpc_nodes (chain_id, http_url, ws_url, provider, priority, weight, node_type, api_key_type)
VALUES
('ethereum',
'https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY',
'wss://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY',
'Alchemy', 1, 15, 'both', 'none');
|
node_type 字段说明
| 值 |
说明 |
http_url |
ws_url |
http |
仅支持HTTP |
必填 |
可选 |
ws |
仅支持WebSocket |
可选 |
必填 |
both |
两者都支持(推荐) |
必填 |
必填 |
API Key 支持
CRPC **完全兼容**两种主流的API Key使用方式,无论你的RPC提供商使用哪种方式,都能无缝支持。
支持的认证方式
方式1:URL内嵌(最常见)
大多数公共RPC提供商(Infura、Alchemy、QuickNode等)将API Key直接嵌入URL中。
1
2
3
4
5
|
-- 示例:Infura
INSERT INTO rpc_nodes (chain_id, http_url, provider, api_key_type)
VALUES ('ethereum',
'https://mainnet.infura.io/v3/YOUR_PROJECT_ID',
'Infura', 'none');
|
**特点**:
- ✅ API Key在URL中
- ✅ 设置
api_key_type = 'none'
- ✅ 无需额外配置
- ✅ 向后兼容(原有配置继续工作)
某些企业级或私有RPC服务要求通过HTTP Header传递API Key。
(a) Authorization Bearer 格式
1
2
3
4
|
INSERT INTO rpc_nodes (chain_id, http_url, provider, api_key_type, api_key)
VALUES ('ethereum',
'https://api.example.com/eth',
'Enterprise', 'header', 'your-secret-token');
|
发送的HTTP请求:
1
2
3
|
POST /eth HTTP/1.1
Authorization: Bearer your-secret-token ← 自动添加
Content-Type: application/json
|
(b) 自定义Header(如X-API-Key)
1
2
3
4
5
6
7
8
|
INSERT INTO rpc_nodes (
chain_id, http_url, provider,
api_key_type, api_key, custom_header_name
) VALUES (
'ethereum',
'https://rpc.custom.com',
'Custom', 'custom', 'your-api-key', 'X-API-Key'
);
|
发送的HTTP请求:
1
2
3
|
POST / HTTP/1.1
X-API-Key: your-api-key ← 自动添加
Content-Type: application/json
|
快速启用
1
2
3
4
5
6
7
8
|
# 1. 运行数据库迁移
make migrate-apikey
# 2. 配置节点(根据提供商要求选择方式)
# 详见下面的示例或 API_KEY_GUIDE.org
# 3. 重启服务(自动生效)
make build && make run
|
常见RPC提供商配置
Infura(URL内嵌)
1
2
3
4
5
|
INSERT INTO rpc_nodes (chain_id, http_url, ws_url, provider, priority, weight, node_type, api_key_type)
VALUES ('ethereum',
'https://mainnet.infura.io/v3/YOUR_PROJECT_ID',
'wss://mainnet.infura.io/ws/v3/YOUR_PROJECT_ID',
'Infura', 1, 15, 'both', 'none');
|
Alchemy(URL内嵌)
1
2
3
4
5
|
INSERT INTO rpc_nodes (chain_id, http_url, ws_url, provider, priority, weight, node_type, api_key_type)
VALUES ('ethereum',
'https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY',
'wss://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY',
'Alchemy', 1, 15, 'both', 'none');
|
1
2
3
4
|
INSERT INTO rpc_nodes (chain_id, http_url, provider, priority, weight, api_key_type, api_key)
VALUES ('ethereum',
'https://enterprise-rpc.company.com',
'Private', 1, 20, 'header', 'enterprise_secret_token');
|
数据库字段说明
| 字段 |
类型 |
说明 |
示例 |
api_key |
VARCHAR(500) |
API密钥(敏感) |
‘your-secret-key’ |
api_key_type |
VARCHAR(20) |
认证类型 |
’none’, ‘header’, ‘custom’ |
custom_header_name |
VARCHAR(100) |
自定义Header名称 |
‘X-API-Key’ |
安全特性
- ✅ **API Key不对外暴露**:API响应中不会返回
api_key 字段
- ✅ **日志脱敏**:日志中不记录API Key原文
- ✅ **灵活配置**:支持混合使用不同认证方式的节点
- ✅ **自动处理**:健康检查和请求转发自动添加正确的认证Header
测试验证
1
2
3
4
5
6
7
|
# 测试URL内嵌方式(Infura)
curl -X POST http://localhost/ethereum \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
# 查看节点统计
curl http://localhost/api/stats/nodes
|
故障排查
API Key无效
1
2
3
4
|
-- 查看节点配置
SELECT id, http_url, api_key_type,
CASE WHEN api_key IS NULL THEN 'NULL' ELSE '***' END as api_key_status
FROM rpc_nodes WHERE id = 1;
|
1
2
3
4
|
-- 更新为正确的Header名称
UPDATE rpc_nodes
SET custom_header_name = 'X-Correct-Header-Name'
WHERE id = 1;
|
智能错误识别
CRPC具备**智能错误识别和分类**能力,能够自动检测RPC节点返回的各种错误,并采取相应的处理策略。
核心能力
- ✅ **自动识别链ID不匹配**:检测节点配置错误(如Arbitrum节点却要求Base链ID)
- ✅ **错误自动分类**:认证失败、限流、超时、连接失败等
- ✅ **致命错误自动切换**:遇到严重错误立即切换到其他健康节点
- ✅ **详细错误统计**:记录每个节点的错误历史和类型分布
- ✅ **自动禁用问题节点**:链ID不匹配次数 ≥ 5 次自动禁用
支持的错误类型
| 错误类别 |
说明 |
自动切换 |
典型场景 |
chain_mismatch |
链ID不匹配 |
✅ 是 |
ARB节点返回"invalid chain ID" |
auth |
认证失败 |
✅ 是 |
API Key过期或错误 |
rate_limit |
请求限流 |
✅ 是 |
超过API配额 |
timeout |
请求超时 |
✅ 是 |
节点响应慢 |
connection |
连接失败 |
✅ 是 |
网络故障 |
invalid_param |
参数错误 |
❌ 否 |
客户端请求错误 |
快速启用
1
2
3
4
5
|
# 1. 运行数据库迁移
make migrate-chain-validation
# 2. 重启服务(自动生效)
make build && make run
|
实战示例
场景:链ID不匹配
你请求Arbitrum链,但Chainstack节点配置错误返回:
1
2
3
4
5
6
|
{
"error": {
"code": -32000,
"message": "invalid chain ID"
}
}
|
**CRPC的自动处理**:
1
2
3
4
5
|
1️⃣ 识别错误类型:chain_mismatch(致命)
2️⃣ 记录错误:chain_id_mismatch_count + 1
3️⃣ 标记节点不健康:is_healthy = false
4️⃣ 自动切换到其他节点(如Alchemy)
5️⃣ 重试成功 → 返回给客户端
|
**日志输出**:
1
2
3
4
5
6
7
8
|
[ERROR] ⚠️ 检测到链ID不匹配错误!
node_id=5
provider=Chainstack
expected_chain=arbitrum
error="invalid chain ID"
[INFO] 自动切换到下一个节点 node_id=6 provider=Alchemy
[INFO] RPC请求成功 method=eth_sendRawTransaction
|
**查询问题节点**:
1
2
3
4
5
6
7
8
9
|
-- 查看链ID不匹配的节点
SELECT id, provider, chain_id, http_url,
chain_id_mismatch_count, last_error_message
FROM rpc_nodes
WHERE chain_id_mismatch_count > 0;
-- 输出:
-- id | provider | chain_id | mismatch_count | last_error
-- 5 | Chainstack | arbitrum | 3 | invalid chain ID
|
错误统计
1
2
3
4
5
|
-- 查看节点错误统计(最近1小时)
SELECT * FROM node_error_stats;
-- 手动禁用问题节点(链ID不匹配 >= 5次)
SELECT * FROM auto_disable_problematic_nodes();
|
工作原理
系统不仅检查HTTP状态码,更重要的是**检查RPC响应体中的error字段**:
1
2
3
|
HTTP 200 OK ✅ → 继续检查
响应体: {"jsonrpc":"2.0", "error": {"code": -32000, "message": "invalid chain ID"}}
↑ 发现错误!识别类型 → 自动切换节点 → 重试成功
|
错误分类逻辑
1
2
3
4
5
6
7
8
9
10
11
12
|
// 1. 基于RPC错误码
if rpcError.Code == -32000 {
if contains(message, "invalid chain id") {
return CategoryChainMismatch // 致命,自动切换
}
}
// 2. 基于错误消息关键词
keywords := ["invalid chain id", "chain not supported", ...]
if containsAny(message, keywords) {
return CategoryChainMismatch
}
|
管理操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
-- 查看问题节点
SELECT id, provider, chain_id, http_url,
chain_id_mismatch_count, last_error_message, is_healthy
FROM rpc_nodes
WHERE chain_id_mismatch_count > 0
ORDER BY chain_id_mismatch_count DESC;
-- 查看错误统计(最近1小时)
SELECT * FROM node_error_stats;
-- 手动禁用问题节点
SELECT * FROM auto_disable_problematic_nodes();
-- 重置节点错误计数(问题修复后)
UPDATE rpc_nodes
SET chain_id_mismatch_count = 0,
failure_count = 0,
last_error_message = NULL,
is_healthy = true
WHERE id = 5;
|
最佳实践
- **配置多个节点**:每条链至少2-3个不同提供商的节点
- **定期检查错误统计**:使用
node_error_stats 视图监控
- **及时修复问题节点**:收到链ID不匹配告警后立即检查配置
- **监控日志**:关注
chain_mismatch 和 auth 类错误
1
2
|
# 实时监控链ID不匹配错误
tail -f log/crpc.log | grep -i "chain.*mismatch\|invalid.*chain"
|
许可证
MIT License
贡献
欢迎提交 Issue 和 Pull Request!
项目地址:https://github.com/your-org/crpc
联系方式