151. 【go 語言】gRPC 環(huán)境搭建(二)

proto3 語言向?qū)ф溄樱?br> Proto3 Language Guide

重要提示(2021-12-12 新增:)

本次環(huán)境搭建的相關(guān)版本如下:
protoc-3.19.1-win64
google.golang.org/grpc v1.42.0
如果版本不一致可能會(huì)導(dǎo)致奇奇怪怪的錯(cuò)誤,不過作為一個(gè)合格的程序員,遇到問題應(yīng)該能自己解決(版本問題一般可以通過官方文檔,時(shí)間較新的技術(shù)文章)。

搭建準(zhǔn)備(windows)

  1. 安裝 protoc 編譯器
    protoc 點(diǎn)擊進(jìn)入下載地址
    進(jìn)入之后,找到最新的protoc-*-win64.zip下載即可。下載完之后,解壓,然后將里面的 bin 目錄添加到系統(tǒng)環(huán)境變量下即可。
  2. 安裝 protoc 插件(前提是正確的搭建了 go 語言開發(fā)環(huán)境,并配備了 $GOPATH 環(huán)境變量)
go get -u google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/protobuf/cmd/protoc-gen-go

在 $GOPATH 環(huán)境路徑下找到剛剛執(zhí)行的命令安裝好的文件,我這里是下面這樣的:


protoc-gen-go.exe文件路徑

復(fù)制 protoc-gen-go.exe 到第 1 步里解壓的目錄下的 bin 目錄下,我這里是下面這樣的:


復(fù)制 protoc-gen-go.exe 到第一步里的 bin 目錄下
  1. 繼續(xù)安裝 protoc 插件(2021-12-12 新增:)

2021-12-12 新增:

go get -u google.golang.org/grpc/cmd/protoc-gen-go-grpc
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc

2021-12-12 新增:


protoc-gen-go-grpc.exe 文件路徑

2021-12-12 新增:
在 $GOPATH 環(huán)境路徑下找到剛剛執(zhí)行的命令安裝好的文件,我這里現(xiàn)在是下面這樣的:


復(fù)制 protoc-gen-go-grpc.exe 到第一步里的 bin 目錄下

一、創(chuàng)建一個(gè) proto 文件

文件名:helloworld.proto

syntax = "proto3";

option go_package = "proto/helloworld";

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}
  • 文件的第一行指定你正在使用proto3語法:如果你不這樣做,協(xié)議緩沖區(qū)編譯器將假定你正在使用proto2。這必須是文件的第一個(gè)非空、非注釋行。
  • 文件的第三行 go_package 選項(xiàng)定義包的導(dǎo)入路徑,該路徑將包含為該文件生成的所有代碼。Go包名將是導(dǎo)入路徑的最后一個(gè)路徑組件。

二、生成 proto 文件

[注意] 在項(xiàng)目的根目錄下,執(zhí)行 protoc 的相關(guān)命令,生成對(duì)應(yīng)的 pd.go 文件,錯(cuò)誤的命令如下:

protoc --go_out=plugins=grpc:. ./proto/*.proto

執(zhí)行之后將會(huì)出現(xiàn)如下錯(cuò)誤

E:\v4_workspace_golang\project_protoc>protoc --go_out=plugins=grpc:. ./proto/*proto
protoc-gen-go: unable to determine Go import path for "proto/helloworld.proto"

Please specify either:
        ? a "go_package" option in the .proto source file, or
        ? a "M" argument on the command line.

See https://developers.google.com/protocol-buffers/docs/reference/go-generated#package for more information.

--go_out: protoc-gen-go: Plugin failed with status code 1.

可能是版本的原因吧,總之別那么執(zhí)行就好,參照官方文檔應(yīng)執(zhí)行下述正確的命令:

protoc -I=. --go_out=. ./proto/*.proto

命令的定義是:

protoc -I=$SRC_DIR --go_out=$DST_DIR $SRC_DIR/*.proto

SRC_DIR(應(yīng)用程序源代碼所在的目錄——如果不提供值,則使用當(dāng)前目錄),
DST_DIR(生成的代碼要去的目錄;通常與$SRC_DIR相同),以及.proto的路徑。

執(zhí)行完命令之后,在命令里指定的文件夾路徑下將會(huì)生成對(duì)應(yīng)的 helloworld.pb.go 文件


命令生成的文件

2021-12-12 新增
在我使用的版本里需要再次執(zhí)行下面的命令:

protoc -I=. --go-grpc_out=. ./proto/*.proto

這里做個(gè)簡單的說明:
我搭建環(huán)境使用的是最新的版本,和以前的版本相比,這個(gè)版本的 Service 需要單獨(dú)使用 protoc-gen-go-grpc 插件,額外執(zhí)行一次命令才能執(zhí)行。
執(zhí)行完命令之后,在命令里指定的文件夾路徑下將會(huì)生成對(duì)應(yīng)的 helloworld_grpc.pb.go 文件

三、簡單看下 .pb.go 文件

type HelloRequest struct {
    state         protoimpl.MessageState
    sizeCache     protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields

    Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
}

func (x *HelloRequest) Reset() {
    *x = HelloRequest{}
    if protoimpl.UnsafeEnabled {
        mi := &file_proto_helloworld_proto_msgTypes[0]
        ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
        ms.StoreMessageInfo(mi)
    }
}

func (x *HelloRequest) String() string {
    return protoimpl.X.MessageStringOf(x)
}

func (*HelloRequest) ProtoMessage() {}

func (x *HelloRequest) ProtoReflect() protoreflect.Message {
    mi := &file_proto_helloworld_proto_msgTypes[0]
    if protoimpl.UnsafeEnabled && x != nil {
        ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
        if ms.LoadMessageInfo() == nil {
            ms.StoreMessageInfo(mi)
        }
        return ms
    }
    return mi.MessageOf(x)
}

// Deprecated: Use HelloRequest.ProtoReflect.Descriptor instead.
func (*HelloRequest) Descriptor() ([]byte, []int) {
    return file_proto_helloworld_proto_rawDescGZIP(), []int{0}
}

func (x *HelloRequest) GetName() string {
    if x != nil {
        return x.Name
    }
    return ""
}
...

在上述代碼中,主要涉及 HelloRequest 類型,其包含了一組 Getters 方法,能夠提供便捷的取值方式,并且處理一些空指針取值的情況,還能通過 Reset 方法來重置該參數(shù)。而該方法通過實(shí)現(xiàn) ProtoMessage 方法,以表示這是一個(gè)實(shí)現(xiàn)了 proto.Message 的接口。

func (*HelloRequest) Descriptor() ([]byte, []int) {
    return file_proto_helloworld_proto_rawDescGZIP(), []int{0}
}
func (*HelloReply) Descriptor() ([]byte, []int) {
    return file_proto_helloworld_proto_rawDescGZIP(), []int{1}
}

每一個(gè) Message Type 中都包含 Descriptor 方法。Descriptor 方法指對(duì)一個(gè)消息體定義的描述,而這個(gè)方法會(huì)在 file_proto_*_proto_rawDescGZIP 中尋找對(duì)應(yīng)消息體的字段(Message Field) 所在的位置后再進(jìn)行返回。

四、小節(jié)

本文介紹了 Protobuf 的使用方法。proto 文件需要通過 Protobuf 的編譯器 protoc 來編譯后才能使用,而在各個(gè)語言的具體插件實(shí)現(xiàn)中,protoc-gen-go 插件是針對(duì) Go 語言的 protoc plugin,他們是相對(duì)隔離且解耦的。未來我們可以實(shí)現(xiàn)一個(gè) protoc plugin,針對(duì)企業(yè)內(nèi)部的定制化需求,非常的方便。


一些從書上看的命令,實(shí)際操作時(shí)還是出錯(cuò)了,最終解決還是靠的官方文檔。書上學(xué)習(xí)、看技術(shù)博客學(xué)習(xí)都是不錯(cuò)的途徑,不過都有一定的滯后性,做一個(gè)軟件開發(fā)者,最好的資料還是官方文檔,權(quán)威、靠譜。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容