【问题标题】:Golang rpc get wrong struct variableGolang rpc 得到错误的结构变量
【发布时间】:2021-07-07 07:09:10
【问题描述】:
  1. 我在端口号 8000 和 8100 上注册了两个 RPC 服务,它们是 分别是 server1 和 server2。
  2. 然后我在server1端创建了一个RPC连接,连接的目的地是server2。
  3. 连接成功后,我调用连接进行远程调用。
  4. 远程调用成功,但是当我获取远程调用的被调用方的端口号时,有时获取到server1的端口号,有时获取到server2的端口号。在我的理解中,远程调用的被调用者是serve2,程序应该返回server2的端口号。
  5. 这是什么原因造成的?

我的代码:

package main

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

type RPCServer struct {
    port    string
    clients map[string]rpc.Client
}

type MessageArgs struct {
    Sender  string
    Message string
}

type MessageReply struct {
    Receive bool
}

func (rs *RPCServer) SendMessage(args *MessageArgs, reply *MessageReply) error {
    log.Printf("current rpc server [%s] Receive Message [%s] from Sender[%s]", rs.port, args.Message, args.Sender)
    reply.Receive = true
    return nil
}

func (rs *RPCServer) server(wg *sync.WaitGroup) {
    l, e := net.Listen("tcp", rs.port)
    if e != nil {
        log.Fatal("listen error: ", e)
    } else {
        log.Printf("server start successfully on port %s\n", rs.port)
    }
    rpc.Register(rs)

    // wait for all server construct but not wait for loop
    wg.Done()

    for {
        conn, err := l.Accept()
        if err != nil {
            log.Fatal("accept error: ", err)
        }

        go rpc.ServeConn(conn)
    }
}

func (rs *RPCServer) connect(port string) {
    client, err := rpc.Dial("tcp", port)
    if err != nil {
        log.Fatal("dialing: ", err)
    } else {
        log.Printf("[%s] server connect other server [%s] successfully.\n", rs.port, port)
    }
    rs.clients[port] = *client
}

func main() {
    servers := make([]RPCServer, 2)
    ports := [2]string{":8000", ":8100"}
    for i := range servers {
        servers[i].clients = make(map[string]rpc.Client)
        servers[i].port = ports[i]
    }

    // start all rpc server
    var wg sync.WaitGroup
    for i := 0; i < 2; i++ {
        wg.Add(1)
        go servers[i].server(&wg)
    }
    wg.Wait()

    servers[0].connect(":8100")

    args := &MessageArgs{Message: "Hello", Sender: ":8000"}
    reply := &MessageReply{}
    clientCall := servers[0].clients[":8100"]
    err := clientCall.Call("RPCServer.SendMessage", args, reply)
    if err != nil {
        log.Fatal(err)
    }
}

有时程序的输出是

2021/07/07 14:39:10 server start successfully on port :8000
2021/07/07 14:39:10 server start successfully on port :8100
2021/07/07 14:39:10 [:8000] server connect other server [:8100] successfully.
2021/07/07 14:39:10 current rpc server [:8000] Receive Message [Hello] from Sender[:8000]

有时程序的输出是

2021/07/07 14:39:16 server start successfully on port :8100
2021/07/07 14:39:16 server start successfully on port :8000
2021/07/07 14:39:16 [:8000] server connect other server [:8100] successfully.
2021/07/07 14:39:16 current rpc server [:8100] Receive Message [Hello] from Sender[:8000]

【问题讨论】:

    标签: go rpc


    【解决方案1】:

    你打电话给rpc.Register(rs)两次

    1. 为您的代码添加错误检查:
        err := rpc.Register(rs)
        if err != nil {
            log.Println(err)
        }
    

    你会看到:

    rpc: service already defined: RPCServer
    

    因为你在这里运行了两个 goroutine:

    for i := 0; i < 2; i++ {
        wg.Add(1)
        go servers[i].server(&wg)
    }
    

    然后他们都将使用以下方式注册一个 rpc 服务器:

    rpc.Register(rs)
    

    所以其中之一是偶然的真正注册服务器。 由于不知道运行 goroutine 的时间,它会因每次运行而变化

    只需只注册一台服务器。 例如:

        go servers[1].server(&wg)
    
    

    1. 代码审查说明:
    rs.clients[port] = *client
    

    包含sync.Mutex 和:

    Mutex 在首次使用后不得复制。

    【讨论】:

    • 如何根据您的代码审查 cmets 更改我的代码?与 RPC 服务建立连接后,我存储 RPC 客户端以供下次调用。如果有两个 RPC 调用,我是否需要连接两次而不是连接一次并存储它以供第二次调用?
    • 使用对rpc.Client结构的引用例如:type RPCServer struct { port string; clients map[string]*rpc.Client} 然后rs.clients[port] = client和一个rpc.Dial就足够了
    • 示例(复制并在您的计算机上运行):play.golang.org/p/6KZulbK7suA
    猜你喜欢
    • 2018-09-01
    • 2016-07-11
    • 2014-07-31
    • 1970-01-01
    • 1970-01-01
    • 2020-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多