调用 Go RPC TCP 服务时 Ruby Socket 客户端挂起

标签 ruby sockets go tcp

我有一个用 Go 编写的工作 RPC TCP 服务,但是当使用 Ruby 连接到服务时它挂起,因为似乎没有数据通过打开的套接字连接发回。

远程 RPC 函数:

package remote

import "fmt"

// Compose is our RPC functions return type
type Compose string

// Details is our exposed RPC function
func (c *Compose) Details(arg string, reply *string) error {
    fmt.Printf("Arg received: %+v\n", arg)
    *c = "some value"
    *reply = "Blah!"
    return nil
}

暴露的远程 RPC 端点:

package remote

import (
    "fmt"
    "net"
    "net/rpc"
)

// Endpoint exposes our RPC over TCP service
func Endpoint() {
    compose := new(Compose)
    rpc.Register(compose)

    listener, err := net.Listen("tcp", ":8080")
    // error handling omitted

    for {
        conn, err := listener.Accept()
        // error handling omitted

        go rpc.ServeConn(conn)
    }
}

通过 TCP 的客户端连接到远程 RPC 功能(使用 Golang,所以我知道它至少在这方面有效):

package main

import (
    "fmt"
    "log"
    "net/rpc"
)

func main() {
    client, err := rpc.Dial("tcp", "localhost:8080")
    if err != nil {
        log.Fatal("dialing:", err)
    }

    var reply string

    e := client.Call("Compose.Details", "my string", &reply)
    if e != nil {
        log.Fatalf("Something went wrong: %v", e.Error())
    }

    fmt.Printf("The 'reply' pointer value has been changed to: %s", reply)
}

这是我尝试使用的 Ruby 代码:

require "socket"
require "json"

socket = TCPSocket.new "localhost", "8080"

b = {
  method: "Compose.Details",
  params: "foobar"
}

socket.write(JSON.dump(b))

resp = JSON.load(socket.readline)

p resp

我认为它不起作用的原因是因为 Go RPC 需要提供一个指针作为公开的 RPC 函数的第二个参数,但我不确定它在不同的编程语言(如 Ruby)中如何工作?

我也尝试过使用类似 https://github.com/chriskite/jimson 的库

require "jimson"

client = Jimson::Client.new("localhost:8080")
result = client.Details("foobar")

但我得到的只是:

RestClient::ServerBrokeConnection: Server broke connection

更新

因此,正如 JimB 在评论中指出的那样,我需要改用 JSON RPC 库。正如您将看到的那样,事情仍然不太顺利......

这是更新后的 Ruby 客户端:

require "socket"
require "json"

socket = TCPSocket.new "localhost", "8080"

b = {
  method: "Compose.Details",
  params: [{ :args => { :foo => "Foo!", :bar => "Bar!" }, :id => "0" }]
}

socket.write(JSON.dump(b))

p JSON.load(socket.readline)

紧随其后的是更新后的 Go 客户端:

package remote

import "fmt"

// Args is structured around the client's provided parameters
type Args struct {
 foo string
 bar string
}

// Compose is our RPC functions return type
type Compose string

// Details is our exposed RPC function
func (c *Compose) Details(args *Args, reply *string) error {
 fmt.Printf("Args received: %+v\n", args)
 *c = "some value"
 *reply = "Blah!"
 return nil
}

当我运行它时,我的 Ruby 客户端会看到以下内容:

{"id"=>nil, "result"=>"Blah!", "error"=>nil}

所以我得到了一个结果,但是如果我的实际 RPC 代码试图以任何方式使用提供的参数来生成结果,那么这将是一个错误的例子,因为我从 Go RPC 函数获得的标准输出是一组空的 arg 值?

Args received: &{foo: bar:}

我不确定为什么 RPC 函数无法提取我提供给它的参数?

还有“id”键:为什么我会从 RPC 函数中取回一个?我怎样才能给它一个有意义的值(value)?

我尝试使用我的 Ruby 客户端的值设置一个“id”键,但这并没有影响 Go RPC 的响应。

最佳答案

好的,所以我需要在我的代码中进行的实际修复如下:

传入的 args 需要是自定义类型,以便您可以导出它,并且需要导出该类型中的字段本身...

type Args struct {
    Foo string
    Bar string
}

但更重要的是,在 Ruby 方面,您需要以符合此要求的格式发送 JSON -> https://golang.org/src/net/rpc/jsonrpc/client.go#L45 <- 快乐

那么这样做...

b = {
  :method => "Compose.Details",
  :params => [{ :Foo => "Foo!", :Bar => "Bar!" }],
  :id     => "0" # id is just echo'ed back to the client
}

关于调用 Go RPC TCP 服务时 Ruby Socket 客户端挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35407717/

相关文章:

xml - Go:一次仅解码一个 XML 节点

go - 何时使用 var 或 := in Go?

ruby - 我如何知 Prop 有默认值的参数是否有明确的参数?

ruby - 使用正则表达式将数字更改为字符

ruby-on-rails - 将 Postgres 查询转换为 Rails ActiveRecord?

C select总是检测到套接字可读

c++ - 套接字和映射的内存泄漏

linux - socket编程问题ipv6+udp

go - 如何发现网络设备类型?

ruby - 使用默认值将 Ruby 字符串转换为整数