一、为什么监听事件?
在真实业务中,我们经常需要「实时响应链上状态变化」:
- 钱包余额更新(收款到账)
- 代币转账或 NFT 交易
- 合约(Program)执行事件
- 大额交易监控、套利机会识别
- 扫链与索引器系统(Indexer)
以太坊使用 eth_subscribe 来监听事件,
而 Solana 则提供了两种主流方式:
| 方式 | 协议 | 说明 |
|---|---|---|
| WebSocket 监听 | WSS | 最常用,直接从 RPC 节点订阅实时消息 |
| gRPC 监听 | gRPC Stream | 更高性能、结构化更强,适合自建节点或生产系统 |
这两种方式本质相同 —— 都是实时订阅链上数据变化。
不同的是:gRPC 具备更好的类型安全与流控制能力。
二、准备环境
我们继续使用 solana-go 库。
依赖安装如下:
go get github.com/gagliardetto/[email protected]
go get github.com/gagliardetto/[email protected]
RPC 地址建议选择 Devnet 或自建节点:
RPC(HTTP/WSS)端点
https://api.devnet.solana.com wss://api.devnet.solana.comgRPC 端点(部分节点提供)
grpc://api.devnet.solana.com:10000
三、监听类型概览
Solana 的事件订阅支持多种类型。
常见监听类型如下表所示:
| 监听类型 | 说明 | 常用场景 |
|---|---|---|
| Account Subscribe | 监听指定账户数据变化 | 监听钱包余额变动、NFT 元数据更新 |
| Logs Subscribe | 监听指定 Program 的日志输出 | 解析交易事件(如 Token Program 的 Transfer) |
| Program Subscribe | 监听某个 Program 下所有账户变化 | 建立索引器、同步 DApp 状态 |
| Signature Subscribe | 监听交易签名状态变化 | 等待交易确认或失败 |
| Slot Subscribe | 监听新区块(Slot) | 实时获取最新区块高度 |
| Root Subscribe | 监听已确认区块根 | 追踪 Finalized 状态 |
四、使用 WebSocket 实现监听
我们先来看 最简单、最常见的方式:
通过 WSS 订阅日志流。
package main
import (
"context"
"fmt"
"log"
"github.com/gagliardetto/solana-go"
"github.com/gagliardetto/solana-go/rpc"
)
func main() {
ctx := context.Background()
// 连接 WSS
client, err := rpc.ConnectWS(ctx, "wss://api.devnet.solana.com")
if err != nil {
log.Fatalf("无法连接 WebSocket: %v", err)
}
// 订阅 Token Program 日志
stream, err := client.LogsSubscribe(ctx, rpc.LogsSubscribeFilterMentions{
Mentions: []solana.PublicKey{
solana.MustPublicKeyFromBase58("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"),
},
}, rpc.CommitmentFinalized)
if err != nil {
log.Fatalf("订阅失败: %v", err)
}
fmt.Println(" 正在监听 Token Program 的日志...")
// 实时接收
for {
msg, err := stream.Recv()
if err != nil {
log.Fatalf("接收失败: %v", err)
}
fmt.Printf(" 区块 %d 日志:\n", msg.Context.Slot)
for _, l := range msg.Value.Logs {
fmt.Println("🪶", l)
}
}
}
运行后你会看到来自 Token Program 的实时日志输出:
正在监听 Token Program 的日志...
区块 31212400 日志:
🪶 Program log: Instruction: Transfer
🪶 Program log: Transfer 1000000 tokens
这就意味着,你的 Go 程序正在“听”整个 Solana 网络上的事件。
五、通过 gRPC 订阅(生产级)
WSS 适合开发调试,但在高并发生产环境下,
gRPC 更加高效、安全,且支持强类型结构化数据。
在 solana-go 库中,我们可以这样实现:
package main
import (
"context"
"fmt"
"log"
"github.com/gagliardetto/solana-go"
"github.com/gagliardetto/solana-go/rpc"
)
func main() {
ctx := context.Background()
client := rpc.New(rpc.DevNet_RPC)
stream, err := client.LogsSubscribe(ctx, rpc.LogsSubscribeFilterMentions{
Mentions: []solana.PublicKey{
solana.MustPublicKeyFromBase58("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"),
},
}, rpc.CommitmentConfirmed)
if err != nil {
log.Fatalf("gRPC 订阅失败: %v", err)
}
fmt.Println(" gRPC 实时监听中...")
for {
msg, err := stream.Recv()
if err != nil {
log.Fatalf("接收失败: %v", err)
}
fmt.Printf("[Slot %d] 日志数量: %d\n", msg.Context.Slot, len(msg.Value.Logs))
for _, l := range msg.Value.Logs {
fmt.Println("→", l)
}
}
}
注意:部分 RPC 服务商(如 Helius、Triton)会开放专门的 gRPC 端点。
建议生产系统接入这类服务,而非直接连接官方 Devnet。
六、常见事件类型示例
| Program | 事件 | 示例日志 |
|---|---|---|
| Token Program | Transfer | Program log: Instruction: Transfer |
MintTo | Program log: Instruction: MintTo | |
Burn | Program log: Instruction: Burn | |
| System Program | Transfer | Program log: Instruction: Transfer |
| Metaplex NFT Program | CreateMetadataAccount | Program log: Instruction: CreateMetadataAccount |
UpdateMetadata | Program log: Instruction: UpdateMetadata |
七、结构化日志解析
日志虽然是字符串输出,但通过模式匹配或正则即可提取核心信息。
例如,我们可以检测 Instruction: Transfer 并捕获代币转账事件:
if strings.Contains(line, "Instruction: Transfer") {
fmt.Println(" 检测到转账事件!")
}
生产系统中,可以进一步将日志解析为结构化 JSON,
存入数据库或推送至 Kafka,供下游服务使用。
八、监听账户变化(AccountSubscribe)
除了日志,开发者还可以直接监听某个账户的状态变化:
stream, err := client.AccountSubscribe(ctx, walletAddress, "", rpc.CommitmentConfirmed)
if err != nil {
log.Fatalf("订阅账户失败: %v", err)
}
for {
msg, err := stream.Recv()
if err != nil {
log.Fatal("接收失败:", err)
}
fmt.Println("账户余额更新:", msg.Value.Lamports)
}
当钱包收到或发送 SOL 时,程序会立即收到通知。
这对于钱包类应用、收款服务尤其实用。
九、生产环境最佳实践
在真实业务中,仅仅「打印日志」是不够的。
以下是常见的生产架构思路:
1. 使用消息队列(MQ)
将事件推送到 Kafka / RabbitMQ / NATS,
供其他微服务异步消费,避免阻塞主线程。
2. 增量存储
将已处理 Slot 记录到数据库中,
确保应用重启后能从中断处恢复监听。
3. 多通道监听
同时监听多个 Program(Token、System、Metaplex),
并根据类型路由到不同的处理逻辑。
4. gRPC + WebSocket 混合
在内部服务使用 gRPC 接口,
对外提供标准化的 WebSocket 通知流,便于前端接入。
十、练习与思考
- 使用 gRPC 监听 Token Program 的日志,统计过去 1 小时内的转账次数。
- 将监听结果存入 PostgreSQL,并实现简单的区块索引。
- 尝试监听特定钱包的 Token Account 变化,实现“到账提醒”。
- 思考:如果要做一个 Solana 版的 Etherscan 监听模块,应该如何设计?
十一、小结
| 能力 | 方法 | 实现 |
|---|---|---|
| 实时监听日志 | LogsSubscribe() | 捕获链上 Program 输出 |
| 监听账户变化 | AccountSubscribe() | 检测余额更新 |
| gRPC 流式订阅 | client.LogsSubscribe() | 稳定高性能监听 |
| 稳定生产架构 | MQ + 增量同步 | 实现可恢复监听系统 |
