【问题标题】:RPC from both client and server in GoGo 中来自客户端和服务器的 RPC
【发布时间】:2012-10-21 09:17:08
【问题描述】:

真的可以使用 Go 中的 net/rpc 包从服务器到客户端进行 RPC 调用吗?如果没有,是否有更好的解决方案?

【问题讨论】:

    标签: go rpc


    【解决方案1】:

    我目前正在使用 thrift (thrift4go) 来实现服务器->客户端和客户端->服务器 RPC 功能。默认情况下,thrift 只执行客户端-> 服务器调用,就像 net/rpc 一样。由于我还需要服务器->客户端通信,所以我做了一些研究,发现bidi-thrift。 Bidi-thrift 解释了如何连接 java server + java client 进行双向 thrift 通信。

    bidi-thrift 的作用及其局限性。

    TCP 连接具有传入和传出通信线路(RC 和 TX)。 bidi-thrift 的想法是拆分 RS 和 TX,并将它们提供给客户端应用程序和服务器应用程序上的服务器(处理器)和客户端(远程)。我发现这在 Go 中很难做到。此外,这种方式不可能有“响应”(响应行正在使用中)。因此,服务中的所有方法都必须是“oneway void”。 (一劳永逸,调用没有结果)。

    解决办法

    我改变了bidi-thrift的想法,让客户端打开两个到服务器的连接,A和B。第一个连接(A)用于执行客户端->服务器通信(客户端像往常一样进行调用)。第二个连接(B)被“劫持”,并连接到客户端上的服务器(处理器),同时连接到服务器上的客户端(远程)。我已经将它与 Go 服务器和 Java 客户端一起使用。它工作得很好。它既快速又可靠(就像普通的节俭一样)。

    一些来源.. B 连接(服务器->客户端)是这样设置的:

    转到服务器

    // factories
    framedTransportFactory := thrift.NewTFramedTransportFactory(thrift.NewTTransportFactory())
    protocolFactory := thrift.NewTBinaryProtocolFactoryDefault()
    
    // create socket listener
    addr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:9091")
    if err != nil {
        log.Print("Error resolving address: ", err.Error(), "\n")
        return
    }
    serverTransport, err := thrift.NewTServerSocketAddr(addr)
    if err != nil {
        log.Print("Error creating server socket: ", err.Error(), "\n")
        return
    }
    
    // Start the server to listen for connections
    log.Print("Starting the server for B communication (server->client) on ", addr, "\n")
    err = serverTransport.Listen()
    if err != nil {
        log.Print("Error during B server: ", err.Error(), "\n")
        return //err
    }
    
    // Accept new connections and handle those
    for {
        transport, err := serverTransport.Accept()
        if err != nil {
            return //err
        }
        if transport != nil {
            // Each transport is handled in a goroutine so the server is availiable again.
            go func() {
                useTransport := framedTransportFactory.GetTransport(transport)
                client := worldclient.NewWorldClientClientFactory(useTransport, protocolFactory)
    
                // Thats it!
                // Lets do something with the connction
                result, err := client.Hello()
                if err != nil {
                    log.Printf("Errror when calling Hello on client: %s\n", err)
                }
    
                // client.CallSomething()
            }()
        }
    }
    

    Java 客户端

    // preparations for B connection
    TTransportFactory transportFactory = new TTransportFactory();
    TProtocolFactory protocolFactory = new TBinaryProtocol.Factory();
    YourServiceProcessor processor = new YourService.Processor<YourServiceProcessor>(new YourServiceProcessor(this));
    
    
    /* Create thrift connection for B calls (server -> client) */
    try {
        // create the transport
        final TTransport transport = new TSocket("127.0.0.1", 9091);
    
        // open the transport
        transport.open();
    
        // add framing to the transport layer
        final TTransport framedTransport = new TFramedTransport(transportFactory.getTransport(transport));
    
        // connect framed transports to protocols
        final TProtocol protocol = protocolFactory.getProtocol(framedTransport);
    
        // let the processor handle the requests in new Thread
        new Thread() {
            public void run() {
                try {
                    while (processor.process(protocol, protocol)) {}
                } catch (TException e) {
                    e.printStackTrace();
                } catch (NullPointerException e) {
                    e.printStackTrace();
                }
            }
        }.start();
    } catch(Exception e) {
        e.printStackTrace();
    }
    

    【讨论】:

      【解决方案2】:

      我遇到了实现它的rpc2。一个例子:

      服务器.go

      // server.go
      package main
      
      import (
       "net"
       "github.com/cenkalti/rpc2"
       "fmt"
      )
      
      type Args struct{ A, B int }
      type Reply int
      
      
      func main(){
           srv := rpc2.NewServer()
           srv.Handle("add", func(client *rpc2.Client, args *Args, reply *Reply) error{
          // Reversed call (server to client)
          var rep Reply
          client.Call("mult", Args{2, 3}, &rep)
          fmt.Println("mult result:", rep)
      
          *reply = Reply(args.A + args.B)
          return nil
       })
      
       lis, _ := net.Listen("tcp", "127.0.0.1:5000")
       srv.Accept(lis)
      }
      

      Client.go

      // client.go
      package main
      
      import (
       "fmt"
       "github.com/cenkalti/rpc2"
       "net"
      )
      
      type Args struct{ A, B int }
      type Reply int
      
      func main(){
           conn, _ := net.Dial("tcp", "127.0.0.1:5000")
      
           clt := rpc2.NewClient(conn)
           clt.Handle("mult", func(client *rpc2.Client, args *Args, reply *Reply) error {
          *reply = Reply(args.A * args.B)
          return nil
         })
         go clt.Run()
      
          var rep Reply
          clt.Call("add", Args{5, 2}, &rep)
          fmt.Println("add result:", rep)
         }
      

      【讨论】:

        【解决方案3】:

        RPC 是一种(远程)服务。每当某些计算机请求远程服务时,它就充当客户端,要求服务器提供服务。在这个“定义”中,服务器调用客户端 RPC 的概念没有明确定义的含义。

        【讨论】:

        • 我想知道的是,当客户端连接到服务器时,服务器是否可以使用从客户端连接到服务器的流向客户端发送请求?
        • “远程过程调用”并不意味着客户端发出请求,它仅表示主机打算调用远程主机上的过程。不确定客户端/服务器与它有什么关系。
        • @Pierre-LucBertrand:来自Wikipedia:“一个RPC由client发起,它向一个已知的远程server发送请求消息> 使用提供的参数执行指定的过程。”_ 这是关于语义的。为请求提供服务的计算机始终是服务器
        猜你喜欢
        • 2016-01-05
        • 2015-09-11
        • 1970-01-01
        • 2013-03-10
        • 2012-03-10
        • 2012-08-19
        • 2018-03-05
        • 1970-01-01
        • 2023-03-10
        相关资源
        最近更新 更多