go使用grpc通信示例


一. protobuf環境配置

  1.下載protobuf編譯器protoc, 下載地址: https://github.com/protocolbuffers/protobuf/releases

   注意根據電腦的版本下載,這里使用的是 protoc-3.19.4-win64.zip,下載完成后解壓,進入bin目錄中發現有一個protoc.exe二進制文件;

      將這個bin地址添加到環境變量中.

   驗證安裝:在cmd中輸入protoc --version查看

     

  2.安裝protobuf插件

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

    執行完成后在GOPATH/bin下會生成protoc-gen-go.exe和protoc-gen-go-grpc.exe文件,安裝完成.

    之前版本可以通過go get命令安裝.

二.編寫proto文件test.proto

  protobuf文件規范參考官網.

syntax = "proto3";

option go_package = "./;pb";
package pb;

message Student {
  string name = 1;
  int32 age = 2;
}

message Class {
  string className = 1;
}

service FindClass {
  rpc MyClass(Student) returns (Class){}
}

三.通過插件生成go代碼

  在test.proto中定義了一個grpc服務,在test.proto所在的目錄中執行:

protoc --go_out=./ --go-grpc_out=./ test.proto

  發現在本目錄下會生成兩個.go文件:test.pb.go和test_grpc.pb.go,結構如下:

    

四.go文件解讀

  1.test.pb.go: 源碼

    

     在test.pb.go文件中可以看到定義了兩個結構體Student和Class,以及一些它們各自對應的方法,對比test.proto文件可以發現,正是我們用message關鍵字定義的兩個消息體,而且結構體中也包含着消息體中對應的字段,其它內容感興趣的可以自己研究.

  2.test_grpc.pb.go:源碼

    

     FindClass是test.proto中定義的服務名稱,MyClass是服務中使用的方法.

    client部分定義了一個包含MyClass方法的接口.NewFindClassClient函數用於初始化grpc的客戶端,可以看到其返回正是上邊定義的接口類型,這就使初始化后的客戶端可以直接調用findClassClient結構體的MyClass方法.

    server部分同樣也定義了一個包含MyClass方法的接口,接口中還包含了一個內部方法,此處不做分析.RegisterFindClassServer函數用於服務注冊,其參數分別為一個grpc服務和一個FindClassServer接口類型.

五.grpc服務端

package main

import (
	"context"
	"errors"
	"fmt"
	"google.golang.org/grpc"
	"net"
	"protobuf/pb"
)

type Teacher struct {
	// 由於在test_grpc.pb.go文件中FindClassServer接口不僅包含MyClass方法,還包含mustEmbedUnimplementedFindClassServer內部方法
	// 所以這里直接繼承 pb.FindClassServer,否則在調用注冊服務時會失敗
	pb.FindClassServer
}

// MyClass方法接收一個上下文和一個Student對象,返回Class對象

func (t *Teacher) MyClass(ctx context.Context, stu *pb.Student) (*pb.Class, error) {
	// 初始化Class對象
	cla := pb.Class{}
	if stu.Name == "root" && stu.Age == 3 {
		cla.ClassName = "Chinese"
		return &cla, nil
	} else {
		return &cla, errors.New("no this student")
	}
}

func main() {
	// 初始化一個grpc對象
	service := grpc.NewServer()
	// 注冊服務
	pb.RegisterFindClassServer(service, new(Teacher))
	// 設置監聽
	listener, err := net.Listen("tcp", ":8080")
	if err != nil {
		fmt.Println(err)
		return
	}
	defer listener.Close()
	//啟動服務
	err = service.Serve(listener)
	if err != nil {
		fmt.Println(err)
		return
	}
} 

六.grpc客戶端

package main

import (
	"context"
	"fmt"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	"protobuf/pb"
)

func main() {
	// 連接grpc服務, 添加證書
	client, err := grpc.Dial(":8080", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		fmt.Println(err)
		return
	}
	defer client.Close()
	// 初始化grpc客戶端
	findClassClient := pb.NewFindClassClient(client)
	// findClassClient是一個包含了MyClass方法的接口類型,所以可以直接調用MyClass方法
	class, err := findClassClient.MyClass(context.Background(), &pb.Student{
		Name: "root",
		Age:  3,
	})
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(class.ClassName) // Chinese
} 

七.注意事項

  1. 由於版本問題,在通過proto文件生成.go文件時,之前可以通過: protoc --go_out=plugins=grpc:./ test.proto 來生成test_grpc.pb.go文件,最新版本此方法以及廢棄,需要通過protoc --go-grpc_out=./ test.proto來實現;

  2.在編寫服務端時,之前的版本可以直接定義一個空結構體去注冊服務,但在新版本中,如果直接new一個空結構體會編譯失敗:

    錯誤如下:

      Cannot use 'new(Teacher)' (type *Teacher) as the type FindClassServerType does not implement 'FindClassServer' as some methods are missing:mustEmbedUnimplementedFindClassServer()

    可以看到是缺少mustEmbedUnimplementedFindClassServer()方法,在test_grpc.pb.go中也可以看到FindClassServer接口中確實包含了兩個方法:

      

  3.如果你使用的編輯器是goland2021.3版本,會發現在test.pb.go文件中有兩處標紅的地方(但並不影響編譯):

      

      

     錯誤信息如下:

      Cannot use 'ms' (type *messageState) as the type protoreflect.MessageType does not implement 'protoreflect.Message'need the method: ProtoMethods() *methodshave the method: ProtoMethods() *protoiface.Methods

    查了部分資料還是沒得到明確的處理方法,有人說是goland2021.3的問題,不知道其他同學有沒有遇到同樣的問題,參考: https://github.com/grpc/grpc-go/issues/4980.

  4.在編寫客戶端連接grpc服務時,grpc.Dial()方法中后一個參數是證書設置,之前的版本中可以使用grpc.WithInsecure(),但在新版本中會提示'WithInsecure' is deprecated ,此方法已廢除,但並不影響程序運行,因為此例子只是簡單的grpc示例,所以為了以防萬一,使用了官方提供的方法:

    提示信息: 

      WithInsecure returns a DialOption which disables transport security for this ClientConn. Under the hood, it uses insecure.NewCredentials().Note that using this DialOption with per-RPC credentials (through WithCredentialsBundle or WithPerRPCCredentials) which require transport security is incompatible and will cause grpc.Dial() to fail.Deprecated: use WithTransportCredentials and insecure.NewCredentials() instead. Will be supported throughout 1.x.

client, err := grpc.Dial(":8080", grpc.WithTransportCredentials(insecure.NewCredentials()))

 

  

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM