Rpcx - Golang learning step 10-2

公開
注意最終更新から 1 年以上経過しています。最新の情報もあわせてご確認ください。

roadmap.sh > Go > Tool for Microservices > Rpcx の学習を進めていきます。

学習箇所

※ 学習メモとしての記録ですが、後にこのセクションを学ぶ道しるべとなるよう、ですます調で記載しています。

開発環境

  • チップ: Apple M2 Pro
  • OS: macOS Sonoma
  • go version: go1.23.2 darwin/arm64

参考 URL

Rpcx

Rpcx は、Go で実装された高性能な RPC (Remote Procedure Call) フレームワークです。gRPC に似ていますが、よりシンプルで Go に特化した設計がされています。以下の特徴があります。

  • 高性能: バイナリプロトコルによる高速通信
  • シンプルな API: サーバー・クライアントの実装が簡単
  • プラグイン対応: ロードバランシングやサービスディスカバリーを簡単に導入可能

Rpcx のインストール

Go のプロジェクトをセットアップし、Rpcx をインストールします。

go mod init rpcx-example
go get github.com/smallnest/rpcx

最小構成での rpcx サーバー構築

以下のファイルを作成し、まずは最小構成で rpcx サーバー構築していきます。

この章で作成するファイルは以下です。

rpcx-example/
├── client.go
├── server.go
└── types/
    └── hello.go

1. リクエスト・レスポンスの構造体定義

これから実装する SayHello メソッドの入出力に使われるリクエスト(input)とレスポンスの構造体を定義します。

types/hello.go

package types

// HelloRequest はクライアントが送るリクエストの構造体
type HelloRequest struct {
	Name string
}

// HelloResponse はサーバーが返すレスポンスの構造体
type HelloResponse struct {
	Message string
}

2. rpcx サーバーの実装

サーバーを作成し、クライアントからリモート呼び出しできる関数を定義します。

server.go

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/smallnest/rpcx/server"
	"rpcx-example/types"
)

// HelloService リモートから呼び出せるメソッドを持つ構造体
type HelloService struct{}

// SayHello リモートから呼び出されるメソッド
func (h *HelloService) SayHello(ctx context.Context, args *types.HelloRequest, reply *types.HelloResponse) error {
	reply.Message = fmt.Sprintf("Hello, %s!", args.Name)
	return nil
}

func main() {
	s := server.NewServer()
	err := s.RegisterName("HelloService", new(HelloService), "") // HelloService を登録

	fmt.Println("RPCサーバーを起動します...")
	if err := s.Serve("tcp", ":8972"); err != nil {
		log.Fatalf("サーバー起動エラー: %v", err)
	}
}

3. クライアントの実装

次に、この RPC サーバーに接続し、SayHello メソッドを呼び出すクライアントを作成します。

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/smallnest/rpcx/client"
	"rpcx-example/types"
)

func main() {
	d, _ := client.NewPeer2PeerDiscovery("tcp@localhost:8972", "")
	xclient := client.NewXClient("HelloService", client.Failover, client.RandomSelect, d, client.DefaultOption)
	defer xclient.Close()

	args := &types.HelloRequest{Name: "Go Developer"}
	reply := &types.HelloResponse{}

	// RPC メソッド呼び出し
	err := xclient.Call(context.Background(), "SayHello", args, reply)
	if err != nil {
		log.Fatalf("RPCエラー: %v", err)
	}

	fmt.Println("サーバーからのレスポンス:", reply.Message)
}

4. サーバー起動と動作確認

ターミナルを2つ開き、それぞれ以下のコマンドを実行します。

# 1. RPC サーバーを起動
go run server.go

# 2.(別のターミナル)クライアントを実行
go run client.go

クライアントを実行すると、サーバーが SayHello を実行し、以下のようなレスポンスが返ります。

サーバーからのレスポンス: Hello, Go Developer!

server.go 一つで rpcx サーバーを実現できました。

Protocol Buffers の導入

types/hello.go で定義した HelloRequest や HelloResponse を Protocol Buffers(protobuf) で定義することもできます。これにより、型の明確化、クロス言語対応、バイナリシリアライズによる効率化 が可能になります。

protoc をインストールします。(protobuf を Go で使うためには protoc も必要)

# Mac の場合
brew install protobuf

# Linux の場合(Ubuntu)
sudo apt install -y protobuf-compiler

# Windows の場合
# 公式サイトからダウンロード: https://github.com/protocolbuffers/protobuf/releases

protobuf をインストールします。

go get -u google.golang.org/protobuf

Protocol Buffers を使う構成

現在のディレクトリ構成を、Protocol Buffers を使うように拡張すると、最終的に以下のようになります。

rpcx-example/
├── client.go
├── server.go
├── proto/
│   ├── hello.proto  # Protocol Buffers の定義ファイル
│   ├── hello.pb.go  # protoc で生成された Go コード

1. Protocol Buffers の定義

まず、proto/hello.proto を作成します。

proto/hello.proto

syntax = "proto3";

package proto;

option go_package = "rpcx-example/proto"; // Go のパッケージ名を指定

// HelloRequest: クライアントから送られるリクエスト
message HelloRequest {
  string name = 1;
}

// HelloResponse: サーバーが返すレスポンス
message HelloResponse {
  string message = 1;
}

2. Protocol Buffers の Go コードを生成

Protocol Buffers を Go で使うには、以下のツールをインストールする必要があります。

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest

以下、protoc コマンドを実行し、.proto から .pb.go を生成します。

protoc --go_out=. --go_opt=paths=source_relative proto/hello.proto

このコマンドを実行すると、proto/hello.pb.go が自動生成されます。

3. server.go の修正

Protocol Buffers の proto/hello.pb.go を使うように修正します。

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/smallnest/rpcx/server"
	"rpcx-example/proto" // Protocol Buffers のパッケージをインポート
)

// HelloService: RPC サーバーのサービス
type HelloService struct{}

// SayHello: リモートで呼び出されるメソッド
func (h *HelloService) SayHello(ctx context.Context, args *proto.HelloRequest, reply *proto.HelloResponse) error {
	reply.Message = fmt.Sprintf("Hello, %s!", args.Name)
	return nil
}

func main() {
	s := server.NewServer()
	err := s.RegisterName("HelloService", new(HelloService), "")
	if err != nil {
		return
	} // サービスを登録

	fmt.Println("RPCサーバーを起動します...")
	if err := s.Serve("tcp", ":8972"); err != nil {
		log.Fatalf("サーバー起動エラー: %v", err)
	}
}

4. client.go の修正

client.go も proto/hello.pb.go を使うように修正します。

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/smallnest/rpcx/client"
	"rpcx-example/proto" // Protocol Buffers のパッケージをインポート
)

func main() {
	d, _ := client.NewPeer2PeerDiscovery("tcp@localhost:8972", "")
	client.DefaultOption.SerializeType = protocol.ProtoBuffer // シリアライズ方式を protobuf に設定
	xclient := client.NewXClient("HelloService", client.Failover, client.RandomSelect, d, client.DefaultOption)
	defer xclient.Close()

	args := &proto.HelloRequest{Name: "Go Developer"}
	reply := &proto.HelloResponse{}

	// RPC メソッド呼び出し
	err := xclient.Call(context.Background(), "SayHello", args, reply)
	if err != nil {
		log.Fatalf("RPCエラー: %v", err)
	}

	fmt.Println("サーバーからのレスポンス:", reply.Message)
}

動作確認

サーバーを起動し、クライアントを実行します

# 1. RPC サーバーを起動
go run server.go

# 2.(別のターミナル)クライアントを実行
go run client.go

実行結果:

サーバーからのレスポンス: Hello, Go Developer!

rpcx と gRPC の違い

gRPC は、Google によって開発された 高性能な RPC(Remote Procedure Call)フレームワーク です。rpcx も Go 向けの RPC フレームワークですが、gRPC は クロス言語対応 や 強力なストリーミング機能 などを備えた RPC システムです。

比較項目 rpcx gRPC
開発元 コミュニティ開発 Google 開発・維持
通信プロトコル 独自バイナリプロトコル(Go RPC) HTTP/2(TLS対応)
シリアライズ gob(デフォルト)、protobuf、JSON protobuf(標準)
クロス言語対応 Go のみ(他言語バインディングなし) Go, Java, Python, C++, Rust など
ストリーミング なし(非同期処理は可能) 双方向ストリーミング対応
ロードバランス クライアント側で可能 内蔵ロードバランスあり
認証 独自実装が必要 TLS/SSL & Google Auth 対応
サービスディスカバリー etcd, Consul などと連携 Kubernetes, etcd, Consul など
拡張性 etcd, Consul との連携可能だがエコシステムは小規模 Google Cloud, Istio など広範なエコシステム

rpcx は Go プログラム内でシンプルに使えますが、gRPC はマイクロサービスや分散システムで広く採用されているのが特徴です。

rpcx も優れたフレームワークですが、gRPC には以下のような利点があります。

項目 説明
クロスプラットフォーム対応 複数のプログラミング言語と通信可能(Go, Java, Python など)
高性能通信 HTTP/2 を活用し、高速で効率的なデータ転送が可能
双方向ストリーミング クライアント・サーバー間でリアルタイムなデータ通信が可能
強力な認証機能 TLS/SSL や Google 認証が標準で利用可能
ロードバランス内蔵 クライアント・サーバー間の負荷分散が組み込みで利用可能
豊富なエコシステム Kubernetes との親和性が高く、クラウド環境向けに最適化

これらより、rpcx は Go のプロジェクト内で軽量な RPC を作るのに便利 ですが、gRPC は 企業レベルのマイクロサービスやクロス言語環境に適しています。

rpcx から gRPC へ移行するメリット

  • クロスプラットフォーム対応(Go 以外の言語とも通信できる)
  • HTTP/2 による高性能な通信
  • 双方向ストリーミングやチャットのような機能が簡単に実装可能
  • ロードバランシングや認証のサポートが強力

もし Go 内部での RPC だけを考えるなら rpcx で十分ですが、マイクロサービスの拡張性や他言語との連携を考えるなら、gRPC の方が優れた選択肢になるでしょう。

rpcx から gRPC へ移行を検討すべきケース

以下のような要件がある場合は、rpcx から gRPC への移行を検討するとよいでしょう。

  • 複数のプログラミング言語で通信したい: rpcx は Go 専用だが、gRPC は他の言語とも通信可能
  • HTTP/2 による効率的な通信が必要: gRPC は HTTP/2 を標準採用し、効率的な通信が可能
  • 双方向ストリーミングが必要: rpcx にはない機能で、リアルタイム通信に適用可能
  • マイクロサービス環境での採用を検討している: gRPC は Kubernetes との親和性が高く、大規模システム向き
  • ロードバランシングや認証を組み込みで利用したい: gRPC なら標準機能で提供されるため、実装コストを削減できる

一方で、Go 内部での RPC 通信をシンプルに実装したい場合は、rpcx が有力な選択肢となります。

まとめ

  • Rpcx は Go に特化した高性能な RPC フレームワーク
  • デフォルトのシリアライズ方式は gob
    • Protocol Buffers を利用する場合、SerializeType を protocol.ProtoBuffer に設定する必要あり
  • 最小構成でサーバーとクライアントを実装可能
  • Protocol Buffers を導入することで型の明確化やクロス言語対応が可能
  • gRPC はクロスプラットフォーム対応やストリーミング機能を備える
  • rpcx は Go 内部の RPC に適し、シンプルで軽量
  • gRPC はマイクロサービス環境や多言語対応が求められる場合に有用
Ritolabo