rpc.ServerCodec还在服务吗?

时间:2016-07-08 17:32:32

标签: go tcp rpc json-rpc

我正在进行一些RPC测试,偶然发现了一个我似乎无法解决的问题。在我的测试中,我创建了三个独立的RPC服务器,我尝试关闭和关闭所有这些服务器。但是在执行我的上一次测试(TestRpcCodecServerClientComm)时,似乎我的客户端连接正在连接到我启动的第一个RPC服务器(我知道这是因为我在某些时候将ID附加到RPCHandlers),即使我尝试了所有我能做的事情来确保它被关闭了。虽然代码不存在,但我试图检查每一个错误,但这并没有带来什么。

rpc.go

package rbot

import (
    "io"
    "net"
    "net/rpc"
    "net/rpc/jsonrpc"
)

func RpcCodecClientWithPort(port string) (rpc.ClientCodec, error) {
    conn, err := net.Dial("tcp", "localhost:"+port)
    if err != nil {
        return nil, err
    }
    return jsonrpc.NewClientCodec(conn), nil
}

func RpcCodecServer(conn io.ReadWriteCloser) rpc.ServerCodec {
    return jsonrpc.NewServerCodec(conn)
}

rpc_test.go

package rbot

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

type RPCHandler struct {
    RPCServer net.Listener
    conn      rpc.ServerCodec
    done      chan bool
    TestPort  string
    stop      bool
    GotRPC    bool
}

func (r *RPCHandler) SetupTest() {
    r.stop = false
    r.GotRPC = false
    r.done = make(chan bool)
    r.TestPort = "5556"
}

// TODO: Create separate function to handle erroring
func (r *RPCHandler) CreateRPCServer() error {
    rpc.RegisterName("TestMaster", TestAPI{r})

    var err error
    r.RPCServer, err = net.Listen("tcp", ":"+r.TestPort)

    if err != nil {
        return err
    }

    go func() {
        for {
            conn, err := r.RPCServer.Accept()
            if err != nil || r.stop {
                r.done <- true
                return
            }
            r.conn = RpcCodecServer(conn)
            rpc.ServeCodec(r.conn)
        }
    }()
    return nil
}

func (r *RPCHandler) CloseRPCServer() error {
    r.stop = true
    if r.conn != nil {
        err := r.conn.Close()
        if err != nil {
            fmt.Println(err)
        }
    }
    err := r.RPCServer.Close()
    <-r.done
    return err
}

type TestAPI struct {
    t *RPCHandler
}

func (tapi TestAPI) Send(msg string, result *string) error {
    if msg == "Got RPC?" {
        tapi.t.GotRPC = true
        return nil
    }
    return errors.New("Didn't receive right message")
}

// Check if we can create and close an RPC server successfully using the RPC server codec.
func TestRpcCodecServer(t *testing.T) {
    r := RPCHandler{}
    r.SetupTest()

    err := r.CreateRPCServer()
    if err != nil {
        t.Fatalf("Could not create rpc server! %s:", err.Error())
    }

    err = r.CloseRPCServer()
    if err != nil {
        t.Fatalf("Could not close RPC server! %s:", err.Error())
    }
}

// Check if we can create a client without erroring.
func TestRpcCodecClientWithPortt(t *testing.T) {
    r := RPCHandler{}
    r.SetupTest()
    r.CreateRPCServer()
    defer r.CloseRPCServer()

    RPCClient, err := RpcCodecClientWithPort(r.TestPort)
    defer RPCClient.Close()
    if err != nil {
        t.Fatalf("Could not create an RPC client! %s:", err.Error())
    }
}

// Let's double check and make sure our server and client can speak to each other
func TestRpcCodecServerClientComm(t *testing.T) {
    r := RPCHandler{}
    r.SetupTest()
    r.CreateRPCServer()
    defer r.CloseRPCServer()

    RPCCodec, _ := RpcCodecClientWithPort(r.TestPort)
    RPCClient := rpc.NewClientWithCodec(RPCCodec)
    defer RPCClient.Close()

    var result string
    err := RPCClient.Call("TestMaster.Send", "Got RPC?", &result)
    if err != nil {
        t.Fatalf("Error while trying to send RPC message: %s", err.Error())
    }

    if !r.GotRPC {
        t.Fatalf("Could not send correct message over RPC")
    }
}

我不确定我是否只是错误地处理了连接或其他类似情况,我们将非常感谢您的帮助。

对于记录 RPC api确实收到正确的字符串消息

1 个答案:

答案 0 :(得分:1)

虽然不是您的问题的根源,但您的测试配置有一些竞争条件,您应该在它们引起问题之前进行处理。始终使用-race选项检查问题。您还应该让操作系统分配端口,这样您就不会遇到冲突。例如,参见httptest.Server如何运作。

您在此失败的原因是,您没有为每项测试创建新的rpc.Server,而是重新使用rpc.DefaultServer。第一次调用CreateRPCServer会在名称TestAPI下注册TestMaster。每个后续调用都使用已注册的实例。

如果您在每次设置测试并注册新的TestAPI时创建新的rpc.Server,则最终测试将通过。

srv := rpc.NewServer()
srv.RegisterName("TestMaster", testAPI)

...
// and then use srv to handle the new connection
srv.ServeCodec(RpcCodecServer(conn))