douliao5467 2016-02-15 11:05
浏览 38
已采纳

调用Go RPC TCP服务时Ruby Socket Client挂起

I have a working RPC TCP service written in Go, but when using Ruby to connect to the service it hangs because no data appears to be sent back through the open socket connection.

Remote RPC Function:

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
", arg)
    *c = "some value"
    *reply = "Blah!"
    return nil
}

Remote RPC Endpoint Exposed:

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)
    }
}

Client Connection over TCP to Remote RPC function (using Golang so I know it works at least in that respect):

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)
}

Here is the Ruby code I'm attempting to use:

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

The reason I think it doesn't work is because the Go RPC requires a pointer to be provided as the second argument to the exposed RPC function but I'm not sure how that works in a different programming language such as Ruby?

I've also tried utilising a library such as https://github.com/chriskite/jimson

require "jimson"

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

But all I get back is:

RestClient::ServerBrokeConnection: Server broke connection

UPDATE

So as indicated by JimB in the comments, I needed to use the JSON RPC library instead. Things still don't quite work, as you'll see...

Here's the updated Ruby client:

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)

Followed by the updated Go client:

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
", args)
 *c = "some value"
 *reply = "Blah!"
 return nil
}

When I run this my Ruby client sees the following:

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

So I'm getting a result back, but if my actual RPC code tried to use the provided args in any way to generate the result then this would be a broken example because the stdout I get from the Go RPC function is a set of empty arg values?

Args received: &{foo: bar:}

I'm not sure why the RPC function isn't able to pull in the arguments I've provided to it?

Also the "id" key: why would I get one back from the RPC function? and how can I give it a meaningful value?

I tried setting an "id" key with a value from my Ruby client but that didn't effect the response from the Go RPC.

  • 写回答

1条回答 默认 最新

  • dongliu5475 2016-02-18 10:11
    关注

    OK, so the actual fix I needed to make in my code was as follows:

    The incoming args needed to be a custom type so you could export it and the fields themselves within that type need to be exported...

    type Args struct {
        Foo string
        Bar string
    }
    

    But more importantly, on the Ruby side, you need to send your JSON in a format that makes this requirement -> https://golang.org/src/net/rpc/jsonrpc/client.go#L45 <- happy

    So do this...

    b = {
      :method => "Compose.Details",
      :params => [{ :Foo => "Foo!", :Bar => "Bar!" }],
      :id     => "0" # id is just echo'ed back to the client
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 #MATLAB仿真#车辆换道路径规划
  • ¥15 java 操作 elasticsearch 8.1 实现 索引的重建
  • ¥15 数据可视化Python
  • ¥15 要给毕业设计添加扫码登录的功能!!有偿
  • ¥15 kafka 分区副本增加会导致消息丢失或者不可用吗?
  • ¥15 微信公众号自制会员卡没有收款渠道啊
  • ¥100 Jenkins自动化部署—悬赏100元
  • ¥15 关于#python#的问题:求帮写python代码
  • ¥20 MATLAB画图图形出现上下震荡的线条
  • ¥15 关于#windows#的问题:怎么用WIN 11系统的电脑 克隆WIN NT3.51-4.0系统的硬盘