- 這篇文章裡我們要實現一個基於GoLang程式語言的gRPC的客戶端與服務端通訊的HelloWorld案例
編寫hello_world.proto檔案,如下程式碼:
syntax proto3
package proto
//介面請求入參
message HelloRequest{
string request = 1;
}
//介面返回出參
message HelloResponse{
string response = 1;
}
//定義介面
service HelloService{
//一個簡單的rpc
rpc HelloWorld(HelloRequest) returns (HelloResponse){}
//一個伺服器端流式rpc
rpc HelloWorldServerStream(HelloRequest) returns (stream HelloResponse){}
//一個客戶端流式rpc
rpc HelloWorldClientStream(stream HelloRequest) returns (HelloResponse){}
//一個客戶端和伺服器端雙向流式rpc
rpc HelloWorldClientAndServerStream(stream HelloRequest)
returns (stream HelloResponse){}
}
- 如上程式碼,通過protobuffer的service定義一個介面HelloService,介面中有四個方法都以HelloWorld開頭,入參是HelloRequest,出參是HelloResponse,通過最前面的rpc關鍵字標識為這是一個rpc介面
編譯hello_world.proto檔案生成對應的go檔案
- 筆者在go_common專案裡新建了grpc專案結構如下程式碼
- 專案地址 :http://github.com/sunpengwei1992/go_common
- 進入專案之後可以先閱讀README.md,瞭解各個包的功能
go_common
grpc
helloworld_new
client //存放客戶端程式碼
proto 存放proto檔案和生成的go檔案
server存放server檔案
- cd go_common/grpc/helloworld_new/proto,進入proto資料夾下
- 執行命令:protoc --go_out=plugins=grpc:. hello_world.proto
- 最後生成hello_world.pb.go檔案,主要有以下幾部分組成:
- 方法出入參結構體以及序列化和反序列方法
- 註冊出入參結構體的init方法
- 客戶端存根結構體和介面以及實現
- 服務端結構體和介面以及一個空實現
- stream的send和recv結構體和介面以及實現
- 服務的一些描述
- 程式碼太多,這裡粘一些核心程式碼,並且簡化了一些出入參,完成程式碼參考github上的原始碼
//包名
package proto
//介面請求入參
type HelloRequest struct {
Request string
}
//介面返回出參
type HelloResponse struct {
Response string
}
//生成的helloServce的客戶端介面,就跟java的api是的,讓別人引用
type HelloServiceClient interface {
//一個簡單的
rpc HelloWorld(in *HelloRequest) (*HelloResponse, error)
//一個伺服器端流式
rpc HelloWorldServerStream(in *HelloRequest) (ServerStream,error)
//一個客戶端流式
rpc HelloWorldClientStream() (ClientStream, error)
//一個客戶端和伺服器端雙向流式
rpc HelloWorldClientAndServerStream() (ServerClientStream error)
}
//helloServiceClient結構體
type helloServiceClient struct {
cc *grpc.ClientConn
}
//獲取客戶端存根,入參是一個client連線
func NewHelloServiceClient(cc *grpc.ClientConn) HelloServiceClient {
return &helloServiceClient{cc}
}
//這是伺服器端的介面,我們的服務端需要實現這些介面
type HelloServiceServer interface {
//一個簡單的
rpc HelloWorld(in *HelloRequest) (*HelloResponse, error)
//一個伺服器端流式
rpc HelloWorldServerStream(*HelloRequest, StreamServer) error
//一個客戶端流式
rpc HelloWorldClientStream(ClientStream) error
//一個客戶端和伺服器端雙向流式
rpc HelloWorldClientAndServerStream (ClientServerStream) error
}
編寫服務端程式碼
- 在server資料夾下新建hello_world_server.go檔案,按如下步驟進行
- 建立HelloWorldServer結構體
- 實現pb.go檔案中的HelloServiceServer介面(實現所有方法)
- StartServer(開啟服務)
- 詳細程式碼依然看github
type HelloServiceServer struct {}
//簡單rpc實現
func (*HelloServiceServer) HelloWorld() {
log.Printf("%v",req.Request)
return &pb.HelloResponse{Response:"hello my is gRpcServer"}, nil
}
//服務端流式rpc實現
func (*HelloServiceServer) HelloWorldServerStream() error {
log.Printf("%v",req.Request)
srv.Send(&pb.HelloResponse{Response:"hello my is gRpcServer stream"})
return nil
}
//客戶端流式rpc實現
func (*HelloServiceServer) HelloWorldClientStream() error {
for{
//不斷接受客戶端傳送的訊息
req,err := srv.Recv()
//直到傳送結束終止for迴圈
if err != nil && err.Error() == "EOF"{
break
}
if err != nil{
log.Fatalf("%v",err)
break
}else{
log.Printf("%v",req.Request)
}
}
//返回給客戶端處理結果
srv.SendAndClose(&pb.HelloResponse{Response:"hello my is gRpcServer"})
return nil
}
//雙向流式rpc
func (*HelloServiceServer) HelloWorldClientAndServerStream(){
for{
//接受客戶端訊息
req,err := srv.Recv()
if err != nil && err.Error() == "EOF"{
break
}
if err != nil{
log.Fatalf("%v",err)
break
}else{
log.Printf("%v",req.Request)
//返回客戶端結果
srv.Send(&pb.HelloResponse{Response:"hello my is gRpcServer stream"})
}
}
return nil
}
func StartServer(){
//開啟服務端監聽
lis, err := net.Listen("tcp", "127.0.0.1:8090")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
//建立grpcServer
gRpcServer := grpc.NewServer()
//註冊服務實現
pb.RegisterHelloServiceServer(gRpcServer, &HelloServiceServer{})
//啟動服務
gRpcServer.Serve(lis)
}
編寫客戶端
- 在client目錄下建立hello_world_client.go檔案
func StartClient(){
//連線grpc的服務端
conn, err := grpc.Dial("127.0.0.1:8090",grpc.WithInsecure())
//建立客戶端存根物件
c := pb.NewHelloServiceClient(conn)
// Contact the server and print out its response.
//設定超時時間
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
//呼叫服務端的方法
r, err := c.HelloWorldClientAndServerStream(ctx,grpc.EmptyCallOption{})
if err != nil{
log.Fatalf("%v", err)
return
}
//客戶端往服務端傳送10次訊息
for i:=0;i<10;i++{
r.Send(&pb.HelloRequest{Request:"my is golang gRpc client"})
}
//傳送完畢
r.CloseSend()
//迴圈接受服務端返回的結果,直到返回EOF
for{
res,err := r.Recv()
if err != nil && err.Error() == "EOF"{
break
}
if err != nil {
log.Fatalf("%v", err)
break
}
log.Printf("result:%v",res.Response)
}
//關閉連線
defer conn.Close()
}
github程式碼執行說明
- github程式碼截圖
- 先啟動 hello_world_server_test.go
- 再啟動hello_world_client_test.go
- 如遇到啟動的任何問題可留言
歡迎大家關注微信公眾號:“golang那點事”,更多精彩期待你的到來