1.RPC
1.1 何が、なぜ必要ですか?
一言で言えば、RPC は分散システムの基礎です。
RPC(リモート プロシージャ コール)、中国語名はリモート プロシージャ コールです。これはゼロックスによって最初に提案され、次のように定義されました。 「RPC は、コンピューター上で実行されているプログラムが、通信媒体として特定のチャネルを使用して別のアドレス空間を呼び出すことを可能にする
言語レベルの通信プロトコルです。」
- タイプの観点から見ると、RPC は通信プロトコルです。
- 機能的に言えば、RPC によって実装される機能は、あるマシン上の別のマシンのアドレス空間を呼び出すことです。これは関数や変数などに対応します。
- 実現手段としては、RPCはコンピュータネットワークのトランスポート層を利用してパイプライン通信を実現する必要があります。トランスポート層でのパイプライン通信は、IP アドレスとポート番号によって通信パイプラインの両端を決定するものとして理解できます。
インターネットの発展に伴い、「クライアント、サーバー、データベース」のモノリシック アーキテクチャ (モノリシック アーキテクチャ) では、ますます複雑化するビジネス ロジックとビジネス アクセスの増加に対応できなくなったため、マイクロサービス アーキテクチャ (マイクロサービス アーキテクチャ) に切り替える必要があります。Google Cloud Platform におけるマイクロサービスの定義は次のとおりです。
マイクロサービス アーキテクチャ (マイクロサービスと短縮されることが多い) は、アプリケーションの開発に使用されるアーキテクチャの形式を指します。マイクロサービスを使用すると、大規模なアプリケーションを、それぞれが独自の責任領域を持つ独立したコンポーネントに分解できます。ユーザーリクエストを処理するとき、マイクロサービスベースのアプリケーションは多くの内部マイクロサービスを呼び出して、その応答を集合的に生成することがあります。
上記の定義によれば、マイクロサービス アーキテクチャにおけるリクエストの場合、各サービスは最終的に対応するレスポンスを生成するために相互に呼び出しを行う必要があるため、サービス間の呼び出しが重要な問題になります。そして、RPC はまさにこの問題を解決できるため、 「マイクロサービスを理解するには、まず RPC を取得する必要がある」という人もいます。適切な RPC フレームワークをマイクロサービス アーキテクチャに追加すると、マイクロサービス アーキテクチャの開発コストが削減されるだけでなく、サービス間の呼び出し効率も向上します。
2.gRPC
2.1 一般的なオープンソース フレームワーク
gRPC 公式ドキュメントの中国語版: Open Source China。長い間更新されていないようです。最新の英語版ドキュメントについてはdocsを参照してください。
gRPC は、もともとGoogleによって開発された、言語中立、プラットフォーム中立のオープンソースのリモート プロシージャ コール (RPC) システムです。gRPC はHTTP/2 標準に基づいて設計されており、単一の TCP 接続上で双方向フロー、フロー制御、ヘッダー圧縮、リクエストの多重化などの機能を実現します。これらの機能により、モバイル デバイスでのパフォーマンスが向上し、電力とスペースが節約されます。grpc.ioによると、このフレームワークは現在、Java、C#、Go、C++、Dart、Python、Kotlin、PHP、Ruby、Objective-C、Node などの複数の言語をサポートしています。
2.2 gRPC 環境の構成
- go.mod で gRPC コア ライブラリを構成します (
$GOPATH/pkg
に配置されます)
$ go get google.golang.org/grpc
- 対応する言語のコード生成プラグインをダウンロードします (
$GOPATH/bin
に配置されます)
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
3. Golangで簡単なRPCサービスを実現
3.1 proto ファイルを作成し、対応する Go コードを生成する
proto ファイルの部分的な構文
service
サービス名 {rpc
rpc メソッド名 (リクエストメッセージ)returns
(レスポンスメッセージ) { } }message
メッセージ名 { 変数の型 変数名 = メッセージ内の位置; }
server.proto ファイルを作成します。宣言構文はGo の構文と似てproto3
おり、Go の構文と似ています。ShakeHands がリクエストを受け入れ、結果を返すRPC メソッドであることを示します。service
func
message
struct
rpc
ShakeReq
ShakeRes
syntax = "proto3";
option go_package = ".;service"; // 生成的.go文件在哪个目录的哪个包下(用;隔开 目录;包名)
service ShakeHands {
rpc ShakeHands(ShakeReq) returns (ShakeRes) {
}
}
message ShakeReq {
string requestName = 1; // 这是在定义变量在 message 中的位置
}
message ShakeRes {
string responseMsg = 1;
}
次に、proto ファイルが配置されているディレクトリに移動し、protoc コマンドを実行します。
$ cd path/to/proto/file
$ protoc --go_out=. server.proto
$ protoc --go-grpc_out=. server.proto
生成されるファイルは次のとおりです。 -go_out は.pb.go
ファイル出力のディレクトリの場所を示し、-go-grpc_out は_grpc.pb.go
ファイル出力のディレクトリの場所を示します。
3.2 サーバーコードを実現する
主なプロセスは次のとおりです。
_grpc.pb.go
ファイルに定義されているサービス (または RPC メソッド)を実装します。- TCPポートを開きます。
- gRPC サービスを作成し、実装したサービスを gRPC サービスに登録します。
- gRPC サービスを開始します。
package main
import (
"context"
"fmt"
"net"
"google.golang.org/grpc"
pb "gRPC/server/proto"
)
type server struct {
pb.UnimplementedShakeHandsServer
}
func (s *server) ShakeHands(ctx context.Context, req *pb.ShakeReq) (res *pb.ShakeRes, err error) {
return &pb.ShakeRes{
ResponseMsg: "res: hello!" + req.RequestName}, nil
}
func main() {
listen, _ := net.Listen("tcp", "127.0.0.1:9999") // 开启tcp端口
grpcServer := grpc.NewServer() // 创建grpc服务
pb.RegisterShakeHandsServer(grpcServer, &server{
}) // 在grpc服务中注册我们的服务
err := grpcServer.Serve(listen) // 启动服务
if err != nil {
fmt.Println("server error!")
return
}
}
3.3 セキュリティ認証のないクライアント
クライアントのロジックは非常に単純で、サーバーとの接続を確立することです。
- ダイヤルして接続を開始します。
grpc.Dial()
サーバーの IP アドレスとポート。 - ファイルによって提供されるメソッドを通じて
_grpc.pb.go
クライアント インスタンスを作成します。 - RPC メソッドを呼び出してリクエストを開始します。
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pb "gRPC/server/proto"
)
func main() {
conn, err := grpc.Dial("127.0.0.1:9999", grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
fmt.Println("connection error!")
}
defer conn.Close()
client := pb.NewShakeHandsClient(conn) // 建立连接
resp, err := client.ShakeHands(context.Background(), &pb.ShakeReq{
RequestName: "test"})
if err != nil {
fmt.Println(err.Error())
}
fmt.Println(resp.GetResponseMsg())
}