详情

首页手游攻略 yellowstone-grpc:实践指南

yellowstone-grpc:实践指南

佚名 2026-09-11 17:00:02

面对实际交付,我看yellowstone-grpc的重点不在星标,而在这项能力:Triton 的 Dragon's Mouth Yellowstone gRPC 高性能 Solana 流媒体服务。这类日常自动化工具真正难在输入边界、依赖和失败处理如果不清楚就很难稳定复用,仓库说明只能作为第一层证据。与其反复读介绍,不如用一项范围明确的真实任务完成最小试跑,再依据配置时间、输出质量、异常信息和维护痕迹做取舍。我的判断是,它更适合愿意先做小范围验证并复查原始文档的团队;若眼下没有这类需求,先保留观察即可。

Yellowstone Dragon's Mouth - 适用于 Solana 的基于 Geyser 的 gRPC 接口

该存储库包含适用于 Solana 的功能齐全的 gRPC 接口,由 Triton One 构建和维护。它是围绕 Solana 的 Geyser 界面构建的。在此存储库中,我们有多种语言的插件和示例客户端。

它提供了通过标准化路径获取槽、块、交易、预执行交易和账户更新通知的能力。

有关其他文档,请参阅:https://docs.triton.one/rpc-pool/grpc-subscriptions

已知错误

gRPC插件内的块重建基于BlockMeta提供的信息,不幸的是,验证器上生成的块的条目数始终等于零。这些块的条目始终为零。请参阅 GitHub 上的问题:https://github.com/solana-labs/solana/issues/33823

验证器

solana-validator --geyser-plugin-config yellowstone-grpc-geyser/config.json

插件配置检查

cargo-fmt && cargo run --bin config-check -- --config yellowstone-grpc-geyser/config.json

gRPC 、TLS 和身份验证配置

配置 gRPC 侦听器的推荐方法是 grpc.listen

grpc.listen 是一个数组,其中每个项目可以定义:

  • address:必填
  • tls:可选,每个侦听器 TLS 配置
  • auth:可选,每者身份验证模式

地址格式

address 接受:

  • TCP 套接字为字符串,例如:"0.0.0.0:10000"
  • Unix 域套接字作为字符串,例如:"unix:///var/run/geyser.sock"
  • 具有显式权限模式的Unix域套接字对象:
{
   "address": {
      "path": "unix:///var/run/geyser.sock",
      "mode": 432
   }
}

mode 是十进制(上面的示例是 0o660)。

TLS 选项 ( listen[].tls )

您可以通过两种方式配置TLS:

  1. 身份对(cert_path + key_path
  2. 包含 PEM 文件的证书目录 (cert_dir)
{
   "tls": {
      "identity": {
         "cert_path": "/etc/yellowstone/tls/server.crt",
         "key_path": "/etc/yellowstone/tls/server.key"
      }
   }
}
{
   "tls": {
      "cert_dir": "/etc/yellowstone/certs"
   }
}

两种形式都接受可选的 watch_file 标志(默认 false)。当 true 时,服务器监视磁盘上的相关路径并热交换所提供的证书,而无需删除现有连接或需要重新启动:

  • 身份对:单独监视 cert_pathkey_path 并在其中任何一个发生更改时重建身份。
  • 证书目录:递归监视 cert_dir 并在其下的任何文件发生更改时重建 SNI 解析器。
{
   "tls": {
      "cert_dir": "/etc/yellowstone/certs",
      "watch_file": true
   }
}

注意事项:

  • 对于 Unix 域套接字,TLS 被忽略。
  • ALPN 自动配置为 HTTP/2。

身份验证选项 ( listen[].auth )

每个侦听器的身份验证均使用 type 进行配置。

type: "http"

  • 使用外部 HTTP 解析器验证请求。
  • 解析器预期的请求查询参数:hosttoken
  • 解析器成功负载应包括 subscription_id 和速率限制(rate_limitsratelimits)。

HTTP 解析器合约:

  • 方法:GET
  • URL: <subscription_resolver_url>?host=<request-host>&token=<x-token>
  • 所需的查询参数:
    • host:从传入的gRPC请求URI中提取的主机
    • token:来自传入 x-token 标头的值
  • 预期状态代码:
    • 200 OK:有效的(host, token)对,返回订阅信息JSON
    • 404 Not Found:无效的 (host, token)

404 行为很重要:gRPC 服务器将其视为对该 host/token 对的未经身份验证的请求。

请求示例:

GET http://127.0.0.1:8080/?host=api.example.com

示例 200 OK 响应正文:

{
    "subscription_id": "test-sub",
    "ratelimits": {
         "methods": {
            "/geyser.Geyser/Subscribe": 2000,
            "/geyser.Geyser/SubscribeReplayInfo": 2000,
            "/geyser.Geyser/Ping": 2000,
            "/geyser.Geyser/GetLatestBlockhash": 2000,
            "/geyser.Geyser/GetBlockHeight": 2000,
            "/geyser.Geyser/GetSlot": 2000,
            "/geyser.Geyser/IsBlockhashValid": 2000,
            "/geyser.Geyser/GetVersion": 2000
         }
    }
}
{
   "auth": {
      "type": "http",
      "subscription_resolver_url": "http://127.0.0.1:8080/",
      "subscription_resolution_cache_ttl": "30s",
      "max_concurrent_auth_requests": 1000
   }
}

type: "file"

  • 验证来自本地 JSON 映射文件的请求。
  • 该文件应包含包含 tokenhostsubscription_info 的对象数组。
{
   "auth": {
      "type": "file",
      "subscription_resolver_path": "/etc/yellowstone/subscriptions.json"
   }
}

示例 /etc/yellowstone/subscriptions.json

[
   {
      "token": "test",
      "host": "127.0.0.1",
      "subscription_info": {
         "subscription_id": "test-sub",
         "ratelimits": {
            "methods": {
               "/geyser.Geyser/Subscribe": 2000,
               "/geyser.Geyser/SubscribeReplayInfo": 2000,
               "/geyser.Geyser/Ping": 2000,
               "/geyser.Geyser/GetLatestBlockhash": 2000,
               "/geyser.Geyser/GetBlockHeight": 2000,
               "/geyser.Geyser/GetSlot": 2000,
               "/geyser.Geyser/IsBlockhashValid": 2000,
               "/geyser.Geyser/GetVersion": 2000
            }
         }
      }
   },
   {
      "token": "prod-token-1",
      "host": "api.example.com",
      "subscription_info": {
         "subscription_id": "prod-sub-1",
         "ratelimits": {
            "methods": {
               "/geyser.Geyser/Subscribe": 2000,
               "/geyser.Geyser/SubscribeReplayInfo": 2000,
               "/geyser.Geyser/Ping": 2000,
               "/geyser.Geyser/GetLatestBlockhash": 2000,
               "/geyser.Geyser/GetBlockHeight": 2000,
               "/geyser.Geyser/GetSlot": 2000,
               "/geyser.Geyser/IsBlockhashValid": 2000,
               "/geyser.Geyser/GetVersion": 2000
            }
         }
      }
   }
]

NOTE:方法速率限制尚未实现。

type: "trusted-metadata"

  • 信任传入的 x-subscription-id 标头并跳过外部身份验证检查。
{
   "auth": {
      "type": "trusted-metadata"
   }
}

计费坚控( listen[].auth.billing )

计费坚控在listen[].auth.billing下配置。

目前支持的模式:

  • type: "http"
{
   "auth": {
      "type": "http",
      "subscription_resolver_url": "http://127.0.0.1:8080/",
      "billing": {
         "type": "http",
         "billing_endpoint_url": "http://127.0.0.1:8081/billing",
         "report_interval": "5s"
      }
   }
}

注意事项:

  • 计费坚控是可选的。
  • 如果省略,report_interval 默认为 5s
  • 计费事件作为 NDJSON(application/x-ndjson 样式有效负载)发送:每行一个 JSON 事件。
  • 事件 code 是以下之一:
    • bandwidth:每个请求路径和订阅的计量字节数
    • requests:每个请求路径和订阅的方法调用计数

NDJSON 有效负载示例:

{"code":"bandwidth","subscription_id":"sub-1","method":"/geyser.Geyser/Subscribe","quantity":8192}
{"code":"requests","subscription_id":"sub-1","method":"/geyser.Geyser/Subscribe","quantity":1}

完整的 listen 示例

{
   "grpc": {
      "listen": [
         {
            "address": "0.0.0.0:10000",
            "auth": {
               "type": "http",
               "subscription_resolver_url": "http://127.0.0.1:8080/",
               "subscription_resolution_cache_ttl": "30s",
               "max_concurrent_auth_requests": 1000
            }
         },
         {
            "address": "0.0.0.0:10001",
            "tls": {
               "identity": {
                  "cert_path": "/etc/yellowstone/tls/server.crt",
                  "key_path": "/etc/yellowstone/tls/server.key"
               },
               "watch_file": true
            },
            "auth": {
               "type": "trusted-metadata"
            }
         },
         {
            "address": {
               "path": "unix:///var/run/geyser.sock",
               "mode": 432
            },
            "auth": {
               "type": "file",
               "subscription_resolver_path": "/etc/yellowstone/subscriptions.json"
            }
         }
      ]
   }
}

遗留字段

grpc.addressgrpc.tls_configgrpc.cert_dir 是旧版本,已弃用,取而代之的是 grpc.listen

使用 grpc.listen 进行新配置,特别是如果每个端点需要不同的 TLS/auth 行为。

预提交挂钩

安装存储库挂钩:

make install-hooks

预提交挂钩将:

  • 确保启用提交签名(commit.gpgsign=true
  • 运行 cargo fmt --all -- --check 并在格式化失败时打印警告

块重建

Geyser 区块更新界面不提供有关交易和帐户更新的详细信息。要通过块消息提供此信息,我们必须收集所有消息并期望指定的顺序。默认情况下,如果我们无法重建完整块,我们会记录一条错误消息并增加 prometheus 指标中的 invalid_full_blocks_total 计数器。如果您想对无效重建感到恐慌,请将配置中的选项 block_fail_action 更改为 panic(默认值为 log)。

流数据过滤器

详情请查看yellowstone-grpc-proto/proto/geyser.proto。

  • commitment — 承诺级别:processed / confirmed / finalized
  • accounts_data_slice — 对象数组 { offset: uint64, length: uint64 },允许仅从帐户接收所需的数据
  • ping — 可选布尔字段。如果客户端在一段时间内没有发送任何内容,一些云提供商(例如 Cloudflare、Fly.io)会关闭流。作为解决方法,您可以每 N 秒发送相同的过滤器,但这不是最佳选择,因为您需要保留此过滤器。相反,您可以发送 ping 字段设置为 true 的订阅请求,并忽略请求中的其余字段。由于我们每 15 秒从服务器发送一条 Ping 消息,因此您可以发送带有 ping 作为回复的订阅请求并接收 Pong 消息。

  • filter_by_commitment — 默认情况下,将为所有承诺级别发送插槽,但使用此过滤器,您只能接收选定的承诺级别

账户

帐户可以通过以下方式过滤:

  • account — 帐户 Pubkey,与数组中的任何 Pubkey 匹配
  • owner — 帐户所有者 Pubkey,与数组中的任何 Pubkey 匹配
  • filters — 与 getProgramAccounts 过滤器相同,dataSizeMemcmp 数组(支持字节、base58、base64)

如果所有字段均为空,则广播所有帐户。否则,字段将用作逻辑 AND,数组中的值将用作逻辑 ORfilters 中的值将用作逻辑 AND)。

交易

  • vote — enable/disable 广播 vote 交易
  • failed — enable/disable 广播 failed 交易
  • signature — 只匹配指定的交易
  • account_include — 过滤使用列表中任何账户的交易
  • account_exclude — 与 account_include 相反
  • account_required — 要求在交易中使用列表中的所有账户

如果所有字段均为空,则广播所有交易。否则,字段作为逻辑 AND 工作,数组中的值作为逻辑 OR 工作。

粉碎交易

SubscribeDeshred 是用于预执行事务的单独双向流。服务器不是等待 Replay 执行事务并生成 TransactionStatusMeta,而是从传入的碎片中重建条目,并在解码的事务可用时立即对其进行流式传输。

这为您提供了比常规 transactions 流更早的信号,但它附带的上下文较少:

  • 可用字段 — signatureis_vote、原始 transactionloaded_writable_addressesloaded_readonly_addresses
  • 不可用字段 — 执行状态、错误详细信息、日志、内部指令、余额、计算使用情况、TransactionStatusMeta

loaded_writable_addressesloaded_readonly_addresses 包含从地址查找表解析的地址,因此 deshred 过滤器可以匹配静态帐户密钥和动态加载的地址。

可用性:

  • protobuf API 和 Rust 客户端公开 SubscribeDeshred
  • 此 RPC 仅在 Triton 扩展服务器上可用
  • 此存储库中的开源 yellowstone-grpc-geyser 服务器当前为 SubscribeDeshred 返回 UNIMPLEMENTED
  • 实现的版本目前位于 master-triton-ext 分支

deshred 事务过滤器支持:

  • vote — enable/disable 广播 vote 交易
  • account_include — 匹配提及任何列出帐户的交易,包括 ALT- 加载的地址
  • account_exclude — 排除提及任何列出账户的交易,包括 ALT- 加载的地址
  • account_required — 要求所有列出的帐户都存在,包括 ALT- 加载的地址

参赛作品

目前,我们没有条目过滤器,所有条目都是广播的。

积木

  • account_include — 过滤使用列表中任何帐户的交易和帐户
  • include_transactions — 包括所有交易
  • include_accounts — 包括所有帐户更新
  • include_entries — 包括所有条目

块元

Blocks 相同,但没有 transactionsaccounts 和条目。目前,我们没有块元过滤器,所有消息都是广播的。

限制过滤器

可以在配置中添加过滤器限制。如果省略 filters 字段,则过滤器没有任何限制。

"grpc": {
   "filters": {
      "accounts": {
         "max": 1,
         "any": false,
         "account_max": 10,
         "account_reject": ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"],
         "owner_max": 10,
         "owner_reject": ["11111111111111111111111111111111"]
      },
      "s": {
         "max": 1
      },
      "transactions": {
         "max": 1,
         "any": false,
         "account_include_max": 10,
         "account_include_reject": ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"],
         "account_exclude_max": 10,
         "account_required_max": 10
      },
      "blocks": {
         "max": 1,
         "account_include_max": 10,
         "account_include_any": false,
         "account_include_reject": ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"],
         "include_transactions": true,
         "include_accounts" : false,
         "include_entries" : false
      },
      "blocks_meta": {
         "max": 1
      },
      "entry": {
         "max": 1
      }
   }
}

一元 gRPC 方法

GetLatestBlockhash

GetBlockHeight

GetSlot

IsBlockhashValid

GetVersion

示例

  • 铁锈
  • TypeScript

有关 SubscribeDeshred CLI 示例,请参阅 examples/rust。

[!NOTE] 如果一段时间内客户端没有发送消息,某些负载均衡器将终止 gRPC 连接。 为了缓解这种情况,您需要定期发送消息。 SubscribeRequest 中的 ping 字段用于此目的。 gRPC 服务器已经向客户端发送 ping,因此您可以用 ping 进行回复,并且您的连接将保持打开状态。 您可以在 Rust 示例中看到如何使用客户端回复来自服务器的 ping。

基于Geyser gRPC的项目

  • https://github.com/rpcpool/yellowstone-grpc-kafka — 将 gRPC 流转发到 Kafka,进行重复数据删除,使用 gRPC 服务器从 Kafka 读取流
点击查看更多
推荐专题
热门阅读