はじめにーgRPCとは?
現代のソフトウェア開発の分野において、分散システムは重要性を持ちます。効率的なコミュニケーション手段が不可欠であり、Googleが開発したフレームワーク-gRPCが重要な役割を果たしてきました。gRPCはRPCの効率を活用しつつ、HTTP/2の機能と共に進化しています。
簡単に言うと、gRPCは、クライアントがローカルのオブジェクトを呼び出すかのように直接異なるマシン上のサーバーアプリケーションのメソッドを直接に呼び出すことができるというフレームワークです。
サーバーサイドでは、サーバーがインターフェースを実装し、gRPCサーバーを実行してクライアントからの呼び出しを処理します。一方、クライアントサイドでは、クライアントがサーバーと同じメソッドを提供するStub(クライアント)を持っています。
gRPCのクライアントとサーバーは、Protocol Buffersを使用して異なる環境で実行し、互いに通信することができます。また、gRPCはgRPCがサポートする言語のどれでも記述することができます。例えば、JavaでgRPCサーバーを作成し、Go、Python、またはRubyでクライアントを作成することが簡単にできます。
gRPCへの進化
もとより、RPCはSOAP(Simple Object Access Protocol)やCORBA(Common Object Request Broker Architecture)のような技術に基づいて実装していましたが、これらの技術は複雑さや相互運用性の問題、パフォーマンスの制約があります。
なので、シンプルさ、パフォーマンス、クロスプラットフォームのサポートを重視したフレームワーク-gRPCが爆誕しました!(笑)
Protocol BuffersやHTTP/2といった基盤の上に構築されたgRPCは、異なる環境間で通信できること、標準化された効率的で、言語に依存しないアプローチを提供しています。
gRPCの主要な特徴
1. IDL駆動開発:
gRPCはInterface Definition Language(IDL)としてProtocol Buffersを使用し、開発者がサービスの構造やデータ型を簡潔で言語に依存しない方法で定義できます。
2. 効率的な通信:
HTTP/2を活用することで、gRPCはマルチプレキシング、ヘッダー圧縮、サーバープッシュなどの機能を提供し、レイテンシーの低減とネットワークの効率的な利用を実現しています。
3. 複数言語のサポート:
gRPCはさまざまなプログラミング言語をサポートしており、開発者が異なるテックスタック間でシームレスに統合し、通信することができます。
4. ストリーミング対応:
gRPCは単項およびストリーミングメソッドをサポートしており、1対1、1対多、双方向の効率的なサービス間通信を可能にしています。
5. コード生成とツール:
Protocol Buffersに基づくコード生成は開発を効率化し、サービス間の型安全で効率的な通信を保証します。
6. HTTP/2による効率的な通信:
gRPCはHTTP/1.1よりも大幅に進化したHTTP/2の機能を利用し、マルチプレキシング、ヘッダー圧縮、サーバープッシュなどの機能を提供しています。
HTTP/2の特徴
HTTP/2は、効率を大幅に向上させる特長を導入することで、ウェブ通信を革新しています。
1. マルチプレキシング:単一のTCP接続上で複数のリクエストとレスポンスを非同期に送信できるため、複数の接続を必要とすることなくレイテンシを低減させます。
2. ヘッダー圧縮:HTTPヘッダーデータを圧縮することで、オーバーヘッドと帯域消費を減少させ、情報の高速な伝送を実現します。
3. サーバープッシュ:サーバーがクライアントの要求前にリソースを予め送信することで、パフォーマンスを最適化し待ち時間を削減します。
4. バイナリフレーミングレイヤー:HTTP/1.xのようなテキスト形式ではなくバイナリ形式のフレームを使用し、より効率的な解析とオーバーヘッドの削減を実現します。
まとめ
結論として、gRPCの強力な性能、多言語・多プラットフォームのサポート、そして強力なHTTP/2の機能が、効率的な方法で分散システムが互いに通信できるようにしました。また、標準化されたインターフェース(Protocol Buffer)を提供し、異なる言語で開発されたサービス間でも効率的な開発可能です。
gRPCは開発者に強力でスケーラブルなシステムの構築を可能にし、従来のRPC手法の制限を超えたものとしています。
実際にやってみましょう
前提条件
gRPCサービスを構築するために、下記の前提条件を確認してください!
1. Protocol Buffers Compiler (protoc)インストール:
protocコンパイラは、.protoファイルからgRPCコードを生成するために必要です。
2. Go インストール:
この記事で Go 1.19.3 を使っています。
3. Protocol Buffers CompilerのGoプラグインをインストール:
# プラグインをインストール
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
# PATHを更新
$ export PATH="$PATH:$(go env GOPATH)/bin"
実装手順
1. Rootフォルダを作成:
この記事で、名前grpc-learningのディレクトリを作成しました。grpc-learningのパスに下記のコマンドを実行。
# rootフォルダと同じ名前
go mod init grpc-learning
2. grpc-learningディレクトリにpbフォルダを作成
3. サービスの定義
Protocol Buffersを使用して、サービスの構造を定義してください。サービスのメソッド、データ型、エンドポイントを指定する.protoファイルを作成して、pbフォルダに保存します。サービスのインターフェースとそれが使用するメッセージタイプを定義します。
syntax = "proto3";
option go_package = "grpc-learning/pb";
service HelloWorld {
rpc PrintHelloWorld (HelloRequest) returns (HelloResponse) {}
}
message HelloRequest {
string message = 1;
}
message HelloResponse {
string message = 1;
}
4. Protocol Buffersコードの生成
Protocol Buffersコンパイラ(protoc)を使用して、定義された.protoファイルに基づいて、選択した言語のコードを生成してください。
protoc --go_out=. --go_opt=paths=source_relative \
--go-grpc_out=. --go-grpc_opt=paths=source_relative \
--go-grpc_opt require_unimplemented_servers=false \
./pb/*.proto
5. サーバーの実装
定義されたサービスインターフェースを実装するサーバーサイドのコードを記述してください。これには、.protoファイルで定義されたサービスメソッドを処理するためのクラスまたは関数を作成することが含まれます。
grpc-learning/serverディレクトリに保存してください。
package main
import (
"context"
"flag"
"fmt"
"log"
"net"
"grpc-learning/pb"
"google.golang.org/grpc"
)
var (
port = flag.Int("port", 50051, "server port")
)
// HelloWorldServerを実装
type HelloWorldServer struct{}
func (hws *HelloWorldServer) PrintHelloWorld(ctx context.Context, hr *pb.HelloRequest) (*pb.HelloResponse, error) {
log.Printf("Received: %v", hr.GetMessage())
return &pb.HelloResponse{Message: hr.GetMessage() + "World"}, nil
}
func main() {
flag.Parse()
lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterHelloWorldServer(s, &HelloWorldServer{})
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
6. クライアントの実装
サーバーとやり取りするクライアントサイドのコードを開発してください。生成されたクライアントスタブを使用して、リモートプロシージャコールをサーバーに行い、そのサービスを利用します。
grpc-learning/clientディレクトリに保存してください。
package main
import (
"context"
"flag"
"log"
"time"
"grpc-learning/pb"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
const (
defaultMsg = "Hello"
)
var (
addr = flag.String("addr", "localhost:50051", "server address")
msg = flag.String("msg", defaultMsg, "say hello")
)
func main() {
flag.Parse()
conn, err := grpc.Dial(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Printf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewHelloWorldClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
log.Printf(*msg)
r, err := c.PrintHelloWorld(ctx, &pb.HelloRequest{Message: *msg})
if err != nil {
log.Fatalf("error when sending request: %v", err)
}
log.Printf("received: %s", r.GetMessage())
}
7. フォルダ構造を確認
grpc-learning
│
└───pb
│ │helloworld.proto
│ │helloworld.pb.go
│ │helloworld_grpc.pb.go
│
└───server
│ │server.go
│
└───client
│ │client.go
│
│go.mod
│go.sum
8. 実行
実行前に、プロジェクトに必要なモジュールをダウンロード
go mod tidy
サーバを実行
go run server/server.go
クライエントを実行
go run client/client.go
以上、gRPCの簡単な実装と紹介です!
ありがとうございました!
参考URL:
gRPC Official Website
gRPC WIKI
What is RPC? gRPC Introduction - ByteByteGo
HTTP/2 Overview - IETF
HTTP/2 WIKI