net/rpc
Goのパッケージを使用して、サーバーからクライアントへのRPC呼び出しを実際に行うことは可能ですか? いいえの場合、より良い解決策はありますか?
3 に答える
私は現在、サーバー - >クライアントおよびクライアント - >サーバーのRPC機能にthrift( thrift4go )を使用しています。デフォルトでは、thrift は net/rpc と同様に client->server 呼び出しのみを行います。サーバーからクライアントへの通信も必要だったので、調査を行ったところ、bidi-thriftが見つかりました。Bidi-thrift では、Java サーバー + Java クライアントを接続して双方向の倹約通信を行う方法について説明しています。
bidi-thrift の機能とその制限。
TCP 接続には、着信および発信通信回線 (RC および TX) があります。bidi-thrift の考え方は、RS と TX を分割し、これらをクライアント アプリケーションとサーバー アプリケーションの両方でサーバー (プロセッサ) とクライアント (リモート) に提供することです。Goでこれを行うのは難しいことがわかりました。また、この方法では「応答」はありません (応答行が使用されています)。したがって、サービスのすべてのメソッドは「oneway void」である必要があります。(起動して忘れると、呼び出しは結果を返しません)。
ソリューション
私は bidi-thrift の考え方を変えて、クライアントがサーバーへの 2 つの接続 (A と B) を開くようにしました。最初の接続 (A) は、クライアント -> サーバー通信 (クライアントが通常どおり呼び出しを行う場所) を実行するために使用されます。2 番目の接続 (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();
}
それを実装するrpc2に出会いました。例:
Server.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)
}
RPC は (リモート) サービスです。一部のコンピューターがリモート サービスを要求するときは常に、サーバーにサービスの提供を要求するクライアントとして機能します。この「定義」内では、サーバーがクライアント RPC を呼び出すという概念には明確な意味はありません。