ホームページ  >  記事  >  バックエンド開発  >  RPC アクション EPIGo で単純な RPC インターフェイスを実装する

RPC アクション EPIGo で単純な RPC インターフェイスを実装する

PHPz
PHPzオリジナル
2024-09-09 18:30:11947ブラウズ

RPC Action EPImplement a simple RPC interface in Go

RPC(Remote Procedure Call)は、分散システムの異なるノード間で広く使用されている通信方法であり、インターネット時代の基盤技術です。 Go の標準ライブラリは、net/rpc パッケージで RPC の単純な実装を提供します。この記事は、net/rpc パッケージを使用して単純な RPC インターフェイスを実装する手順を説明することで、RPC を理解できるようにすることを目的としています。

この記事は、中型 MPP プランで初めて公開されました。あなたが Medium ユーザーであれば、Medium で私をフォローしてください。誠にありがとうございます。

net/rpc で関数をリモートから呼び出せるようにするには、次の 5 つの条件を満たす必要があります。

  • メソッドの型がエクスポートされます。
  • メソッドがエクスポートされます。
  • メソッドには 2 つの引数があり、どちらもエクスポート (または組み込み) 型です。
  • メソッドの 2 番目の引数はポインタです。
  • メソッドの戻り値の型はエラーです。

つまり、関数シグネチャは次のようにする必要があります:

func (t *T) MethodName(argType T1, replyType *T2) error

単純な RPC リクエストの作成

これらの 5 つの条件に基づいて、単純な RPC インターフェイスを構築できます。

type HelloService struct{}  
func (p *HelloService) Hello(request string, reply *string) error {  
    log.Println("HelloService Hello")  
    *reply = "hello:" + request  
    return nil  
}

次に、HelloService タイプのオブジェクトを RPC サービスとして登録できます。

func main() {
    _ = rpc.RegisterName("HelloService", new(HelloService))  
    listener, err := net.Listen("tcp", ":1234")  
    if err != nil {  
        log.Fatal("ListenTCP error:", err)  
    }  
    for {  
        conn, err := listener.Accept()  
        if err != nil {  
           log.Fatal("Accept error:", err)  
        }  
        go rpc.ServeConn(conn)  
    }
}

クライアント側の実装は次のとおりです:

func main() {
    conn, err := net.Dial("tcp", ":1234")
    if err != nil {
        log.Fatal("net.Dial:", err)
    }
    client := rpc.NewClient(conn)
    var reply string
    err = client.Call("HelloService.Hello", "hello", &reply)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println(reply)
}

まず、クライアントは rpc.Dial を使用して RPC サービスにダイヤルし、次に client.Call() を介して特定の RPC メソッドを呼び出します。最初のパラメータは RPC サービス名とメソッド名をドットで組み合わせたもので、2 番目は入力、3 番目は戻り値 (ポインタ) です。この例は、RPC の使用がいかに簡単であるかを示しています。

サーバー コードとクライアント コードの両方で、RPC サービス名 HelloService とメソッド名 Hello を覚えておく必要があります。これは開発中にエラーにつながりやすいため、共通部分を抽象化することでコードを少しラップすることができます。完全なコードは次のとおりです:

// server.go
const ServerName = "HelloService"  

type HelloServiceInterface = interface {  
    Hello(request string, reply *string) error  
}  

func RegisterHelloService(srv HelloServiceInterface) error {  
    return rpc.RegisterName(ServerName, srv)  
}  

type HelloService struct{}  

func (p *HelloService) Hello(request string, reply *string) error {  
    log.Println("HelloService Hello")  
    *reply = "hello:" + request  
    return nil  
}

func main() {  
    _ = RegisterHelloService(new(HelloService))  
    listener, err := net.Listen("tcp", ":1234")  
    if err != nil {  
       log.Fatal("ListenTCP error:", err)  
    }  
    for {  
       conn, err := listener.Accept()  
       if err != nil {  
          log.Fatal("Accept error:", err)  
       }  
       go rpc.ServeConn(conn)  
    }  
}
// client.go

type HelloServiceClient struct {  
    *rpc.Client  
}  

var _ HelloServiceInterface = (*HelloServiceClient)(nil)  

const ServerName = "HelloService" 

func DialHelloService(network, address string) (*HelloServiceClient, error) {  
    conn, err := net.Dial(network, address)  
    client := rpc.NewClient(conn)  
    if err != nil {  
       return nil, err  
    }  
    return &HelloServiceClient{Client: client}, nil  
}

func (p *HelloServiceClient) Hello(request string, reply *string) error {  
    return p.Client.Call(ServerName+".Hello", request, reply)  
}
func main() {
    client, err := DialHelloService("tcp", "localhost:1234")  
    if err != nil {  
        log.Fatal("net.Dial:", err)  
    }  
    var reply string  
    err = client.Hello("hello", &reply)  
    if err != nil {  
        log.Fatal(err)  
    }  
    fmt.Println(reply)
}

見覚えはありますか?

Go の net/rpc パッケージを使用した JSON コーデックの実装

デフォルトでは、Go の標準 RPC ライブラリは Go 独自の Gob エンコーディングを使用します。ただし、Protobuf や JSON などの他のエンコーディングをその上に実装するのは簡単です。標準ライブラリはすでに jsonrpc エンコーディングをサポートしており、サーバーとクライアントのコードに若干の変更を加えることで JSON エンコーディングを実装できます。

// server.go
func main() {  
    _ = rpc.RegisterName("HelloService", new(HelloService))  
    listener, err := net.Listen("tcp", ":1234")  
    if err != nil {  
       log.Fatal("ListenTCP error:", err)  
    }  
    for {  
       conn, err := listener.Accept()  
       if err != nil {  
          log.Fatal("Accept error:", err)  
       }  
       go rpc.ServeCodec(jsonrpc.NewServerCodec(conn))  
       //go rpc.ServeConn(conn)  
    }  
}

//client.go
func DialHelloService(network, address string) (*HelloServiceClient, error) {  
    conn, err := net.Dial(network, address)  
    //client := rpc.NewClient(conn)  
    client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn))  
    if err != nil {  
       return nil, err  
    }  
    return &HelloServiceClient{Client: client}, nil  
}

JSON リクエスト データ オブジェクトは、内部的に 2 つの構造に対応しています。クライアント側では clientRequest、サーバー側ではserverRequest です。 clientRequest 構造とserverRequest 構造の内容は本質的に同じです:

type clientRequest struct {  
    Method string `json:"method"`  
    Params [1]any `json:"params"`  
    Id     uint64 `json:"id"`  
}
type serverRequest struct {  
    Method string           `json:"method"`  
    Params *json.RawMessage `json:"params"`  
    Id     *json.RawMessage `json:"id"`  
}

ここで、Method は、serviceName と Method から構成されるサービス名を表します。 Params の最初の要素はパラメーターであり、Id は呼び出し元によって維持される一意の呼び出し番号であり、同時シナリオでリクエストを区別するために使用されます。

nc を使用してサーバーをシミュレートし、クライアント コードを実行して、JSON エンコードされたクライアントがサーバーに送信する情報を確認できます。

 nc -l 1234

nc コマンドは次のデータを受け取ります:

 {"method":"HelloService.Hello","params":["hello"],"id":0}

これは、serverRequest と一致します。

サーバー コードを実行し、NC を使用してリクエストを送信することもできます。

echo -e '{"method":"HelloService.Hello","params":["Hello"],"Id":1}' | nc localhost 1234 
--- 
{"id":1,"result":"hello:Hello","error":null}

結論

この記事では、Go の標準ライブラリの rpc パッケージを紹介し、そのシンプルさと強力なパフォーマンスを強調しました。多くのサードパーティ rpc ライブラリは、rpc パッケージ上に構築されています。この記事は、RPC 研究に関するシリーズの最初の記事として機能します。次の記事では、protobuf と RPC を組み合わせて、最終的には独自の RPC フレームワークを実装します。

以上がRPC アクション EPIGo で単純な RPC インターフェイスを実装するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。