如何通过Ruby的GRPC与Vitess对话

95 阅读2分钟

在你用几个数据库运行一个简单的Vitess集群后,你可能想把一些东西自动化,比如说你要做大量的 分片垂直分割

你如何编写与Vitess互动的脚本?你可以用Bash脚本来完成所有的vtctlclient ,但在某些时候,这将变得很脆弱。

Vitess提供了世界一流的Go APIs,但对于我想尝试的东西来说,Ruby更适合。

在这篇文章中,我想分享我是如何通过Ruby的GRPC与Vitess对话的。

GRPC的冒险

GRPC依赖于代码的生成。如果你来自Ruby生态系统,这就有点不寻常了,但尽管如此。

你必须安装grpc-tools gem,它可以提供代码生成工具。

一旦你安装了它,你应该能够运行类似的东西。

$ grpc_tools_ruby_protoc -I /Users/kirs/src/github.com/vitessio/vitess/proto --ruby_out=lib --grpc_out=lib /Users/kirs/src/github.com/vitessio/vitess/proto/*.proto

是的--你应该克隆Vitess repo(在我的例子中,克隆到/Users/kirs/src/github.com/vitessio/vitess ),以根据Vitess repo中proto/ 目录下的protobuf定义生成Ruby类。--ruby_out=lib --grpc_out=lib 告诉它将生成的Ruby代码输出到你本地项目的lib/

从protobufs生成代码后,你的lib/将看起来像这样。

tree lib
lib
├── automation_pb.rb
├── automationservice_pb.rb
├── automationservice_services_pb.rb
├── binlogdata_pb.rb
├── binlogservice_pb.rb
...
├── vtrpc_pb.rb
├── vttest_pb.rb
├── vttime_pb.rb
├── vtworkerdata_pb.rb
├── vtworkerservice_pb.rb
├── vtworkerservice_services_pb.rb
└── workflow_pb.rb

现在你有足够的代码来调用Vitess上的GRPC命令。

两种方法

让我们想象一下,你想调用VReplicationExec RPC。至少有两种方法可以做到这一点。

一种方法是与顶层拓扑服务vtctld对话。

require 'vtctlservice_pb'
require 'vtctlservice_services_pb'

service = Vtctlservice::Vtctl::Stub.new('<address-of-vtctld>:15999', :this_channel_is_insecure)
tablet_name = "zone1-0428408676"
response = service.execute_vtctl_command(
  ::Vtctldata::ExecuteVtctlCommandRequest.new(
    args: ["VReplicationExec", "-json", tablet_name, "select id from _vt.vreplication"]
  )
)
response.each do |r|
  pp JSON.parse(r.event.value)
end

你可以看到,execute_vtctl_command 在一个通用的RPC调用中,将另一个RPC的名称(VReplicationExec )作为第一个参数,然后是实际参数。最后,你必须将结果解析为JSON。

另一种涉及较少手工操作的方法是向实际的vttablet地址发送RPC。

require 'tabletmanagerdata_pb'
require 'tabletmanagerservice_services_pb'

s = Tabletmanagerservice::TabletManager::Stub.new('<address-of-vttablet>:15999', :this_channel_is_insecure)
s.v_replication_exec(Tabletmanagerdata::VReplicationExecRequest.new(query: "select id from _vt.vreplication"))
# => <Tabletmanagerdata::VReplicationExecResponse: result: <Query::QueryResult: fields: [<Query::Field: name: "id", type: :INT32, table: "vreplication", org_table: "vreplication", database: "_vt", org_name: "id", column_length: 11, charset: 63, decimals: 0, flags: 49667, column_type: "">], rows_affected: 0, insert_id: 0, rows: []>>

你可以注意到,响应已经被解析了。

我花了一些时间才明白,我必须注意GRPC的端点地址。很容易把本来是给vttablet的RPC发送到vtctld,而得到类似unknown service vtctlservice.Vtctl 的错误信息。

我发现自己先看了*.proto ,然后在生成的Ruby代码中引用了它们,然后在IRB会话中尝试了一些东西。

在Ruby中使用GRPC并不理想,而且感觉很不寻常,但这是为严格的类型化远程过程调用所付出的代价,它有其好处。

享受你在Ruby中使用Vitess的黑客行为吧。