1. 遠程過程調用協議
1.1 定義
??遠程過程調用(Remote Procedure Call,PRC是一種進程間通信技術,它使得程序可以像調用本地函數一樣調用遠程服務器上的函數。RPC 屏蔽了底層的通信細節,讓開發者能夠更專注于業務邏輯,而無需關心網絡編程的復雜性。其流程如下:
1.2 grpc
??gRPC是由 Google 開發的一個高性能、開源的 RPC 框架,基于 HTTP/2 協議并使用Protocol Buffers(protobuf)作為接口描述語言。
2. Protocol Buffers
??Protocol Buffers(Protobuf)是由Google開發的一種語言中立、平臺中立、可擴展的序列化數據格式。它用于在不同的程序和系統之間進行高效的數據交換。Protobuf 允許開發者定義數據結構(消息)并自動生成代碼來處理這些數據結構的序列化和反序列化。Protocol Buffers具有以下優點:
- 高效性:Protobuf 使用緊湊的二進制格式,比傳統的文本格式(如 XML 或 JSON)更節省空間和帶寬,序列化和反序列化速度快,適合高性能需求的場景。
- 跨語言支持:Protobuf 支持多種編程語言,包括但不限于 C++, Java, Python, Go, C#, Ruby, JavaScript 等。這種跨語言特性使得它在異構系統中非常有用。
- 可擴展性:Protobuf 允許數據結構的靈活擴展,新的字段可以添加而不會破壞已有的數據格式。這種向后兼容性使得在系統升級和維護過程中更容易處理。
- 自動生成代碼:開發者定義數據結構的
.proto
文件后,可以使用 protoc 編譯器生成目標語言的代碼,減少手工編碼的錯誤和工作量。
2.1 基礎語法
??Protobuf的定義文件.proto
一般包含語法版本聲明、包聲明、消息定義和服務定義四個部分。其基本結構如下:
syntax = "proto3";
option go_package="./project/generated"
package example;
message MessageName {//定義消息// 字段定義
}
service Greeter { //定義服務
}
- 語法版本聲明:使用
syntax
指定.proto
文件的語法版本,一般常用的語法版本有:proto2
和proto3
。 - 包聲明:
package
關鍵字用于指定代碼的命名空間。 option
中go_pacakge
指定了生成的Go代碼應該放置的路徑名。go_package
中至少要包含一個/
.- 消息定義:使用
message
關鍵字聲明消息類型。消息類型中存放數據結構。一個消息類型包含一個或多個字段,每個字段有一個唯一的編號。舉例如下:
message Person {string name = 1; // 類型、字段名及其唯一編號。int32 id = 2;string email = 3;
}
- 服務定義:使用
service
關鍵字聲明服務類型,服務用于定義 RPC 服務接口。舉例如下:
service Greeter {rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {string name = 1;
}
message HelloReply {string message = 1;
}
3. go環境下的grpc案例
3.1 安裝相關的包
先在go語言的project中安裝grpc相關的包,具體命令如下
go get google.golang.org/grpc
go get google.golang.org/protobuf
go get google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
go get google.golang.org/protobuf/cmd/protoc-gen-go@latest
3.2 編寫helloworld.proto文件
如果go環境使用的是GoLand等,需要先安裝protobuf
插件。具體過程這里不在贅述。
syntax = "proto3";
option go_package="./generated";
package helloworld;
service Greeter {rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {string name = 1;
}
message HelloReply {string message = 1;
}
接著使用protoc
命令將helloworld.proto
文件編譯成go語言的源代碼。具體命令如下:
protoc --go_out=. --go-grpc_out=. helloworld.proto
其中:
--go_out
指定生成Go代碼的輸出目錄。
--go-grpc_out
指定生成Go gRPC代碼的輸出目錄。
3.3 客戶端/服務端代碼
服務端代碼server.go
package main
import ("context""google.golang.org/grpc"pb "grpc_test/proto/generated" //引用protoc生成的go package"log""net"
)
type server struct {pb.UnimplementedGreeterServer
}
//實現sayhello函數
func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {return &pb.HelloReply{Message: "Hello, " + req.Name}, nil
}
func main() {lis, err := net.Listen("tcp", ":50051")if err != nil {log.Fatalf("failed to listen: %v", err)}s := grpc.NewServer()pb.RegisterGreeterServer(s, &server{})if err := s.Serve(lis); err != nil {log.Fatalf("failed to serve: %v", err)}
}
客戶端代碼client.go
package main
import ("context""flag""log""time""google.golang.org/grpc""google.golang.org/grpc/credentials/insecure"pb "grpc_test/proto/generated"
)
const (defaultName = "world"
)
var (addr = flag.String("addr", "localhost:50051", "the address to connect to")name = flag.String("name", defaultName, "Name to greet")
)
func main() {flag.Parse()// Set up a connection to the server.conn, err := grpc.NewClient(*addr, grpc.WithTransportCredentials(insecure.NewCredentials()))if err != nil {log.Fatalf("did not connect: %v", err)}defer conn.Close()c := pb.NewGreeterClient(conn)// Contact the server and print out its response.ctx, cancel := context.WithTimeout(context.Background(), time.Second)defer cancel()r, err := c.SayHello(ctx, &pb.HelloRequest{Name: *name})if err != nil {log.Fatalf("could not greet: %v", err)}log.Printf("Greeting: %s", r.GetMessage())
}
這里完整的代碼結構如下:
然后分別server.go
和client.go
文件(如果是在GoLand中,這兩個文件要分別運行在兩個終端中),可以看到如下結果:
2024/06/25 20:51:17 Greeting: Hello, world
參考資料
- https://blog.csdn.net/m0_68949064/article/details/123974867