科技改變生活 · 科技引領(lǐng)未來
1、wikipedia概念在分布式計(jì)算,遠(yuǎn)程過程調(diào)用(英語:RemoteProcedureCall,縮寫為RPC)是一個計(jì)算機(jī)通信協(xié)議。該協(xié)議允許運(yùn)行于一臺計(jì)算機(jī)的程序調(diào)用另一個地址空間(通常為一個開放網(wǎng)絡(luò)的一臺計(jì)算機(jī))的子程序,而程序員就
1、wikipedia概念
在分布式計(jì)算,遠(yuǎn)程過程調(diào)用(英語:Remote Procedure Call,縮寫為 RPC)是一個計(jì)算機(jī)通信協(xié)議。該協(xié)議允許運(yùn)行于一臺計(jì)算機(jī)的程序調(diào)用另一個地址空間(通常為一個開放網(wǎng)絡(luò)的一臺計(jì)算機(jī))的子程序,而程序員就像調(diào)用本地程序一樣,無需額外地為這個交互作用編程(無需關(guān)注細(xì)節(jié))。RPC是一種服務(wù)器-客戶端(Client/Server)模式,經(jīng)典實(shí)現(xiàn)是一個通過發(fā)送請求-接受回應(yīng)進(jìn)行信息交互的系統(tǒng)。
如果涉及的軟件采用面向?qū)ο缶幊?,那么遠(yuǎn)程過程調(diào)用亦可稱作遠(yuǎn)程調(diào)用或遠(yuǎn)程方法調(diào)用,例:Java RMI。
RPC是一種進(jìn)程間通信的模式,程序分布在不同的地址空間里。如果在同一主機(jī)里,RPC可以通過不同的虛擬地址空間(即便使用相同的物理地址)進(jìn)行通訊,而在不同的主機(jī)間,則通過不同的物理地址進(jìn)行交互。許多技術(shù)(常常是不兼容)都是基于這種概念而實(shí)現(xiàn)的。
2、http和rpc的區(qū)別
1、http指的是一個應(yīng)用層協(xié)議,它提供的只是一個傳輸協(xié)議
2、rpc講的是一個遠(yuǎn)程過程調(diào)用,它是一個過程,一個rpc架構(gòu)包含了多個層級,以dubbo的架構(gòu)來
rpc其實(shí)架構(gòu)很簡單,就是下面一圖,但是具體實(shí)現(xiàn)上差異還是有點(diǎn),比如我們所了解的 http + json 只能說是dubbo 只能說是dubbo的最下層實(shí)現(xiàn),所以 rpc相對來說偏向于服務(wù)治理這一塊
3、為什么我們需要rpc框架,http1.1 提供的rest api不行嗎
1、不支持長連接,keepalive
2、http1.1 ping-pang client - server http , 建立很多個連接 (http tcp 連接慢,傳輸/窗口)
3、rpc 多路復(fù)用能力 (http2/3) client - server (io) ,server 包 (二進(jìn)制) -> go 順序 http2 奇偶
4、并發(fā)性
5、rpc tcp 傳輸 -> http1.1 純文本 頭部, rcp hello 行頭體
5、 json rpc
4、比較出名的rpc框架
大廠用的吧,各大廠都有自己的輪子,比如 thrift,gprc,Tars,brpc ,motan, dubbo 還有很多吧
其實(shí)論生態(tài)的話,絕對是開源項(xiàng)目的生態(tài)比較好,所以開源項(xiàng)目中生態(tài)比較好的就是 grpc,thrift,dubbo ,使用難度上來看 dubbo是最簡單的,如果你們?nèi)獼ava的話它是個不錯的選擇!
2、grpc介紹
gRPC 是一個現(xiàn)代的開源高性能遠(yuǎn)程過程調(diào)用(Remote Procedure Call,RPC)框架,可以在任何環(huán)境中運(yùn)行。它可以高效地連接數(shù)據(jù)中心內(nèi)部和跨數(shù)據(jù)中心的服務(wù),并為負(fù)載平衡、跟蹤、健康檢查和身份驗(yàn)證提供可插拔的支持。它也適用于最后一英里的分布式計(jì)算連接設(shè)備,移動應(yīng)用程序和瀏覽器的后端服務(wù)。
1、開源的grpc官方,主要提供的能力
2、grpc主要采用的技術(shù)
3、推薦學(xué)習(xí)文章,其實(shí)grpc是奔著一個規(guī)范去走了,所以在開源項(xiàng)目中所使用grpc的項(xiàng)目有很多,云原生中大量的項(xiàng)目使用grpc
關(guān)于序列化和反序列化的思考
關(guān)于HTTP2相關(guān)知識
XDS標(biāo)準(zhǔn)引入GRPC
關(guān)于xsd的學(xué)習(xí)
? grpc-go git:(master) ? tree -L 2 . ├── api ## pb 項(xiàng)目(不同業(yè)務(wù)組名稱是不一樣的,像我們組直接叫項(xiàng)目名字直接叫api) │ ├── Makefile ## 腳本 │ ├── bin ## protoc / protoc-gen-go/ protoc-gen-gofast 腳本用來生成pb文件 │ ├── dto ## 傳輸層數(shù)據(jù) │ ├── go.mod │ ├── go.sum │ └── third ## rpc接口,我們這里叫做third └── service ## 業(yè)務(wù)項(xiàng)目 ├── client.go ├── common ##common ├── go.mods ├── go.sum ├── lbs-service.go ├── service ## 具體業(yè)務(wù)層 └── user-service.go
3、protobuf介紹
1、介紹和文檔
Protocol Buffers 是一種與語言無關(guān)、平臺無關(guān)、可擴(kuò)展的序列化結(jié)構(gòu)數(shù)據(jù)的方法,它可用于(數(shù)據(jù))通信協(xié)議、數(shù)據(jù)存儲等。Protocol Buffers 是一種靈活,高效,自動化機(jī)制的結(jié)構(gòu)數(shù)據(jù)序列化方法-可類比 XML,但是比 XML 更?。? ~ 10倍)、更快(20 ~ 100倍)、更為簡單。你可以定義數(shù)據(jù)的結(jié)構(gòu),然后使用特殊生成的源代碼輕松地在各種數(shù)據(jù)流中使用各種語言進(jìn)行編寫和讀取結(jié)構(gòu)數(shù)據(jù)。你甚至可以更新數(shù)據(jù)結(jié)構(gòu),而不破壞由舊數(shù)據(jù)結(jié)構(gòu)編譯的已部署程序。
2、基礎(chǔ)學(xué)習(xí)
1、基本格式
syntax = "proto3"; // 默認(rèn)走的是 proto2,所以需要強(qiáng)制指定,pb3比pb2語法上簡單 package dto; //當(dāng)前文件的package,我們項(xiàng)目不喜歡使用前綴類似于 project-name.dto,因?yàn)楦静粫嬖诙鄠€項(xiàng)目互相引用!根據(jù)你們需求去決定 import "dto/demo.proto"; // import 其他文件,相對于--proto_path 路徑的相對路徑 option java_multiple_files = true; option java_package = "com.grpc.api.third"; //java包的路徑 option go_package="api/dto";// go的包路徑(切記一定要寫全,和go的import有關(guān)系,如果寫 dto,那么別人引入你就是 dto了,顯然是引入不到的,一定要寫全) message SearchRequest { // 每個字段都有四塊組成: 字段規(guī)則,類型,名稱,編號 string query = 1; int32 page_number = 2; int32 result_per_page = 3; repeated string hobby = 4; }
2、類型
3、編號
? 消息定義中的每個字段都有一個唯一的編號。這些字段編號用于以消息二進(jìn)制格式標(biāo)識字段,在使用消息類型后不應(yīng)更改。注意,范圍1到15中的字段編號需要一個字節(jié)進(jìn)行編碼,包括字段編號和字段類型。范圍16到2047的字段編號采用兩個字節(jié)。因此,應(yīng)該為經(jīng)常出現(xiàn)的消息元素保留數(shù)字1到15。記住為將來可能添加的頻繁出現(xiàn)的元素留出一些空間。
4、字段規(guī)則
5、注釋
兩種格式,和java/c++/go保持一致
// 注釋
6、刪除/保留字段
message SearchRequest { string query = 1; int32 page_number = 2; int32 result_per_page = 3; repeated string hobby = 4; reserved 15, 9 to 11; reserved "foo", "bar"; }
7、其他
1、支持任意的結(jié)構(gòu)體嵌套
message Outer { // Level 0 message MiddleAA { // Level 1 message Inner { // Level 2 int64 ival = 1; bool booly = 2; } } message MiddleBB { // Level 1 message Inner { // Level 2 int32 ival = 1; bool booly = 2; } } }
2、任意類型
? 這里代表的不是任意的數(shù)據(jù)類型,指的是Message 類型!
syntax="proto3"; package dto; option java_multiple_files = true; option java_package = "com.grpc.api.third"; option go_package="dto"; import "google/protobuf/any.proto"; message City { uint64 id=1; string name=2; // 引用包名稱.類型名稱 google.protobuf.Any any = 9; }
3、map類型
message City { uint64 id=1; string name=2; // 引用包名稱.類型名稱 google.protobuf.Any any = 9; map demo=10; }
4、oneof類型
下面為例子,就是你只能選擇 v3 或者 v4 !
message BenchMarkModel { string v1=1; uint64 v2=2; oneof test_oneof { string v3 = 3; uint32 v4 = 4; } }
8、import 作用
首先 import 是引用 路徑的,那個路徑是相對于你的--proto_path 路徑的路徑
比如我的項(xiàng)目中,引用就是,項(xiàng)目路徑是/Users/fanhaodong/project/programing/grpc-go/api
bin/protoc --proto_path /Users/fanhaodong/project/programing/grpc-go/api --proto_path /Users/fanhaodong/go/src/github.com/gogo/protobuf/protobuf --plugin=protoc-gen-go=bin/protoc-gen-go --go_out=. ./dto/*.proto
那么我的import 可以來自于兩個路徑
import "dto/city.proto"; import "google/protobuf/any.proto";
首先dto/city.proto ,絕對路徑是/Users/fanhaodong/project/programing/grpc-go/api/dto/city.proto 我們的編譯目錄是/Users/fanhaodong/project/programing/grpc-go/api ,所以去除前綴就是dto/city.proto
然后看google/protobuf/any.proto 也是同理
9、package 作用
主要是區(qū)分 package 區(qū)分 import 可能引用了相同的 message ,所以就需要 package指定,一般package命令為比如我的目錄是api/dto,所以一般命名為api.dto ,一般來說跨業(yè)務(wù)組是不允許相互引用,只能引用一些common的結(jié)構(gòu)
比如下面這種情況,我引用了兩個 文件dto/demo.proto,google/protobuf/any.proto
demo.proto 定義了 Any 類型
message Any { string name=1; }
但是我這時候沒有告訴我到底用的哪個 Any,此時編譯期無法解決
syntax="proto3"; package dto; option java_multiple_files = true; option java_package = "com.grpc.api.dto"; option go_package="dto"; import "google/protobuf/any.proto"; import "dto/demo.proto"; message City { uint64 id=1; string name=2; // 引用包名稱.類型名稱 Any any = 9; map demo=10; }
可以看到
type City struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Id uint64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // 引用包名稱.類型名稱 Any *Any `protobuf:"bytes,9,opt,name=any,proto3" json:"any,omitempty"` Demo map[string]uint64 `protobuf:"bytes,10,rep,name=demo,proto3" json:"demo,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` }
所以存在這種問題,此時就需要 package解決引用的問題
message City { uint64 id=1; string name=2; // 引用包名稱.類型名稱 google.protobuf.Any any = 9; map demo=10; }
3、Protobuf的編碼算法原理
Encoding
4、編譯工具
使用測試的pb 文件
syntax="proto3"; package dto; option java_multiple_files = true; option java_package = "com.grpc.api.dto"; option go_package="api/dto"; import "google/protobuf/any.proto"; import "google/protobuf/duration.proto"; import "google/protobuf/timestamp.proto"; import "google/protobuf/empty.proto"; import "google/protobuf/wrappers.proto"; message BenchMarkModel { string v1=1; uint64 v2=2; int64 v3=3; double v4=4; float v5=5; bool v6=6; enum Corpus { UNIVERSAL = 0; WEB = 1; IMAGES = 2; LOCAL = 3; NEWS = 4; PRODUCTS = 5; VIDEO = 6; } Corpus v7=7; map v8=8; oneof test_oneof { string v9 = 9; uint32 v10 = 10; } bytes v11=11; message Msg { string content=1; } repeated Msg v12=12; google.protobuf.Any v13 = 13; google.protobuf.Duration v14=14; google.protobuf.Empty v15=15; google.protobuf.UInt64Value v16=16; google.protobuf.Timestamp v17=17; }
2、測試代碼
package dto import ( "github.com/golang/protobuf/proto" "github.com/stretchr/testify/assert" "google.golang.org/protobuf/types/known/anypb" "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/emptypb" "google.golang.org/protobuf/types/known/timestamppb" "google.golang.org/protobuf/types/known/wrapperspb" "math" "testing" "time" ) func newModel(b testing.TB) *BenchMarkModel { any := &anypb.Any{} if err := any.MarshalFrom(wrapperspb.UInt64(math.MaxUint64)); err != nil { b.Fatal(err) } return &BenchMarkModel{ V1: "hello 12345424234234", V2: math.MaxUint64, V3: math.MaxInt64, V4: math.MaxFloat64, V5: math.MaxFloat32, V6: true, V7: BenchMarkModel_PRODUCTS, V8: map[string]uint32{"1": 1, "2": 2, "3": 3}, TestOneof: &BenchMarkModel_V10{ V10: math.MaxUint32, }, V11: []byte("hello 1234567890"), V12: []*BenchMarkModel_Msg{ {Content: "1"}, {Content: "2"}, {Content: "3"}, }, V13: any, V14: durationpb.New(time.Hour * 24), V15: &emptypb.Empty{}, V16: wrapperspb.UInt64(math.MaxUint64), V17: timestamppb.Now(), } } func TestGo_Marshal(t *testing.T) { model := newModel(t) protoBufBody, err := proto.Marshal(model) if err != nil { t.Fatal(err) } newModel := &BenchMarkModel{} if err := proto.UnmarshalMerge(protoBufBody, newModel); err != nil { t.Fatal(err) } assertModel(t, model, newModel) } func assertModel(t testing.TB, model *BenchMarkModel, newModel *BenchMarkModel) { assert.Equal(t, model.V1, newModel.V1) assert.Equal(t, model.V2, newModel.V2) assert.Equal(t, model.V3, newModel.V3) assert.Equal(t, model.V4, newModel.V4) assert.Equal(t, model.V5, newModel.V5) assert.Equal(t, model.V6, newModel.V6) assert.Equal(t, model.V7, newModel.V7) assert.Equal(t, model.V8, newModel.V8) assert.Equal(t, model.TestOneof, newModel.TestOneof) assert.Equal(t, model.V11, newModel.V11) for index, _ := range model.V12 { assert.Equal(t, model.V12[index].Content, newModel.V12[index].Content) } assert.Equal(t, model.V13.Value, newModel.V13.Value) assert.Equal(t, model.V13.TypeUrl, newModel.V13.TypeUrl) assert.Equal(t, model.V14.Nanos, newModel.V14.Nanos) assert.Equal(t, model.V14.Seconds, newModel.V14.Seconds) assert.Equal(t, model.V15, newModel.V15) assert.Equal(t, model.V16.Value, newModel.V16.Value) assert.Equal(t, model.V17.Seconds, newModel.V17.Seconds) assert.Equal(t, model.V17.Nanos, newModel.V17.Nanos) }
1、使用protoc-gen-go
這里需要知道的是--go_out 需要找到protoc-gen-go 的位置,如果你的protoc-gen-go放在PATH目錄下就可以直接使用,但是我們一般不會放在PATH目錄下,所以需要指定--plugin=protoc-gen-go=bin/protoc-gen-go,意思就是告訴位置所在
bin/protoc --proto_path . --proto_path /Users/fanhaodong/go/grpc/include --plugin=protoc-gen-go=bin/protoc-gen-go --go_out=../ ./dto/*.proto
幫助
--plugin=EXECUTABLE Specifies a plugin executable to use. Normally, protoc searches the PATH for plugins, but you may specify additional executables not in the path using this flag. Additionally, EXECUTABLE may be of the form NAME=PATH, in which case the given plugin name is mapped to the given executable even if the executable's own name differs.
其實(shí)規(guī)則就是
--plugin=protoc-gen-go=bin/protoc-gen-go 對應(yīng)的必須是--go_out,它走的是拼前綴,比如你執(zhí)行這個也OK,因?yàn)槭强辞熬Y說話
bin/protoc --proto_path . --proto_path /Users/fanhaodong/go/grpc/include --plugin=protoc-gen-go1=bin/protoc-gen-go --go1_out=../ ./dto/*.proto
開始進(jìn)入話題進(jìn)行benchmark
func BenchmarkGo_Marshal(b *testing.B) { model := newModel(b) for i := 0; i < b.N; i++ { if _, err := proto.Marshal(model); err != nil { b.Fatal(err) } } } func BenchmarkGo_UnMarshal(b *testing.B) { model := newModel(b) result, err := proto.Marshal(model) if err != nil { b.Fatal(err) } newModel := &BenchMarkModel{} for i := 0; i < b.N; i++ { if err := proto.UnmarshalMerge(result, newModel); err != nil { b.Fatal(err) } } }
測試結(jié)果
? dto git:(master) ? go test -run=none -bench=BenchmarkGo_ -benchmem -count=4 . goos: darwin goarch: amd64 pkg: api/dto BenchmarkGo_Marshal-12 428138 2624 ns/op 600 B/op 17 allocs/op BenchmarkGo_Marshal-12 431756 2552 ns/op 600 B/op 17 allocs/op BenchmarkGo_Marshal-12 418332 2595 ns/op 600 B/op 17 allocs/op BenchmarkGo_Marshal-12 503637 2520 ns/op 600 B/op 17 allocs/op BenchmarkGo_UnMarshal-12 537661 2824 ns/op 555 B/op 19 allocs/op BenchmarkGo_UnMarshal-12 542142 2398 ns/op 554 B/op 19 allocs/op BenchmarkGo_UnMarshal-12 509076 2420 ns/op 563 B/op 19 allocs/op BenchmarkGo_UnMarshal-12 544599 2063 ns/op 553 B/op 19 allocs/op PASS ok api/dto 11.746s
2、使用protoc-gen-gofast
1、首先需要安裝
go install github.com/gogo/protobuf/protoc-gen-gofast ## protoc-gen-gofast 工具 go get github.com/gogo/protobuf ## 代碼依賴
2、這里使用了Any類型,所以需要我們做gofast的兼容,這點(diǎn)比較坑,官網(wǎng)也寫了如何解決:https://github.com/gogo/protobuf , 因此編譯命令需要添加一些參數(shù)!
bin/protoc --proto_path . --proto_path /Users/fanhaodong/go/src/github.com/gogo/protobuf/protobuf --plugin=protoc-gen-gofast=bin/protoc-gen-gofast --gofast_out= Mgoogle/protobuf/any.proto=github.com/gogo/protobuf/types, Mgoogle/protobuf/duration.proto=github.com/gogo/protobuf/types, Mgoogle/protobuf/field_mask.proto=github.com/gogo/protobuf/types, Mgoogle/protobuf/struct.proto=github.com/gogo/protobuf/types, Mgoogle/protobuf/type.proto=github.com/gogo/protobuf/types, Mgoogle/protobuf/api.proto=github.com/gogo/protobuf/types, Mgoogle/protobuf/descriptor.proto=github.com/gogo/protobuf/types, Mgoogle/protobuf/empty.proto=github.com/gogo/protobuf/types, Mgoogle/protobuf/source_context.proto=github.com/gogo/protobuf/types, Mgoogle/protobuf/timestamp.proto=github.com/gogo/protobuf/types, Mgoogle/protobuf/wrappers.proto=github.com/gogo/protobuf/types:../ ./dto/*.proto
3、最坑的來了,也就是它為啥不兼容的問題!
1)原來定義的any現(xiàn)在不能使用了,也就是說api的使用方式上變了!
import ( "encoding/json" "github.com/gogo/protobuf/types" "github.com/golang/protobuf/proto" "github.com/stretchr/testify/assert" "math" "testing" "time" ) func newModel(b testing.TB) *BenchMarkModel { any, err := types.MarshalAny(&types.UInt64Value{ Value: math.MaxUint64, }) if err != nil { b.Fatal(err) } return &BenchMarkModel{ V1: "hello 12345424234234", V2: math.MaxUint64, V3: math.MaxInt64, V4: math.MaxFloat64, V5: math.MaxFloat32, V6: true, V7: BenchMarkModel_PRODUCTS, V8: map[string]uint32{"1": 1, "2": 2, "3": 3}, TestOneof: &BenchMarkModel_V10{ V10: math.MaxUint32, }, V11: []byte("hello 1234567890"), V12: []*BenchMarkModel_Msg{{Content: "1"}, {Content: "2"}, {Content: "3"},}, V13: any, V14: types.DurationProto(time.Hour * 24), V15: &types.Empty{}, V16: &types.UInt64Value{ Value: math.MaxUint64, }, V17: types.TimestampNow(), } }
3、benchmark
? dto git:(master) ? go test -run=none -bench=BenchmarkGoFast_ -benchmem -count=4 . goos: darwin goarch: amd64 pkg: api/dto BenchmarkGoFast_Marshal-12 1579309 748 ns/op 240 B/op 2 allocs/op BenchmarkGoFast_Marshal-12 1487350 840 ns/op 240 B/op 2 allocs/op BenchmarkGoFast_Marshal-12 1389932 765 ns/op 240 B/op 2 allocs/op BenchmarkGoFast_Marshal-12 1532866 784 ns/op 240 B/op 2 allocs/op BenchmarkGoFast_UnMarshal-12 1000000 1173 ns/op 382 B/op 7 allocs/op BenchmarkGoFast_UnMarshal-12 1235286 1001 ns/op 384 B/op 7 allocs/op BenchmarkGoFast_UnMarshal-12 1083085 1191 ns/op 371 B/op 7 allocs/op BenchmarkGoFast_UnMarshal-12 1000000 1144 ns/op 382 B/op 7 allocs/op PASS ok api/dto 14.907s
3、總結(jié)
1、從性能上來看確實(shí)提升至少是一倍起步,基本帶來了翻倍的收益(官方給的數(shù)據(jù)是5-10倍的性能提升,它還提供了更快的!1),然后主要是內(nèi)存分配上可以看到內(nèi)存優(yōu)勢很大!
2、但從效率上來說其實(shí)對于業(yè)務(wù)開發(fā)其實(shí)是不關(guān)注太多這些的,開發(fā)效率和質(zhì)量決定一切!
3、關(guān)于選擇protoc-gen-gofast 還是選擇protoc-gen-go ,看你們業(yè)務(wù)已開始用的什么,如果開始就選擇protoc-gen-gofast 那么可以一直用,但是一開始就選擇protoc-gen-go 那就惡心了,基本上無法切換到protoc-gen-gofast,可以選擇使用protoc-gen-gogo
4、gRPC
1、介紹和文檔
? gRPC 是一個現(xiàn)代的開源高性能遠(yuǎn)程過程調(diào)用(Remote Procedure Call,RPC)框架,可以在任何環(huán)境中運(yùn)行。它可以高效地連接數(shù)據(jù)中心內(nèi)部和跨數(shù)據(jù)中心的服務(wù),并為負(fù)載平衡、跟蹤、健康檢查和身份驗(yàn)證提供可插拔的支持。它也適用于最后一英里的分布式計(jì)算連接設(shè)備,移動應(yīng)用程序和瀏覽器的后端服務(wù)。
2、寫rpc接口 (IDL)
第一個lbs接口是:
syntax="proto3"; package third; option java_multiple_files = true; option java_package = "com.grpc.api.third"; option go_package="api/third"; import "google/protobuf/wrappers.proto"; import "dto/city.proto"; service LbsService { rpc getCityInfo (google.protobuf.UInt64Value) returns (dto.City); }
第二個是user服務(wù)接口:
syntax="proto3"; package third; option java_multiple_files = true; option java_package = "com.grpc.api.third"; option go_package="api/third"; import "dto/user.proto"; import "google/protobuf/wrappers.proto"; service UserService { rpc GetUserInfo (google.protobuf.UInt64Value) returns (dto.User); }
3、gofast編譯rcp接口
如何編譯,我們使用的是 gofast進(jìn)行編譯,所以需要修改一些參數(shù),關(guān)于參數(shù)如何使用的,這些絕對百度不來,主要是看人家這個gofast項(xiàng)目的文檔和example
這里就是需要告訴一下編譯的時候使用 plugin=grpc, 然后還需要改變一下引用,最后就是指定一下輸出目錄
格式就是--{pugin}_out=k1=v1,k2=v2,k3=v3....,kn=vn:{輸出目錄}
bin/protoc --proto_path . --proto_path /Users/fanhaodong/go/src/github.com/gogo/protobuf/protobuf --plugin=protoc-gen-gofast=bin/protoc-gen-gofast --gofast_out=plugins=grpc, Mgoogle/protobuf/any.proto=github.com/gogo/protobuf/types, Mgoogle/protobuf/duration.proto=github.com/gogo/protobuf/types, Mgoogle/protobuf/field_mask.proto=github.com/gogo/protobuf/types, Mgoogle/protobuf/struct.proto=github.com/gogo/protobuf/types, Mgoogle/protobuf/type.proto=github.com/gogo/protobuf/types, Mgoogle/protobuf/api.proto=github.com/gogo/protobuf/types, Mgoogle/protobuf/descriptor.proto=github.com/gogo/protobuf/types, Mgoogle/protobuf/empty.proto=github.com/gogo/protobuf/types, Mgoogle/protobuf/source_context.proto=github.com/gogo/protobuf/types, Mgoogle/protobuf/timestamp.proto=github.com/gogo/protobuf/types, Mgoogle/protobuf/wrappers.proto=github.com/gogo/protobuf/types:../ ./third/*.proto
4、go寫接口服務(wù)調(diào)用
1、服務(wù)端代碼
package main import ( "api/dto" "api/third" "context" "fmt" "github.com/gogo/protobuf/types" "google.golang.org/grpc" "log" "net" "service/common" "time" ) type lbsServiceServer struct{} func (lbsServiceServer) GetCityInfo(ctx context.Context, cityId *types.UInt64Value) (*dto.City, error) { if cityId.Value == 0 { return nil, fmt.Errorf("not found city: %d", cityId.Value) } return &dto.City{ Id: cityId.Value, Name: fmt.Sprintf("beijing-%d", cityId.Value), Properties: map[string]string{"time": time.Now().Format(time.RFC3339)}, Msg: &dto.OneofMessage{ TestOneof: &dto.OneofMessage_Name{ Name: "demo", }, }, }, nil } func main() { // 創(chuàng)建一個tcp listener lis, err := net.Listen("tcp", common.LbsService) if err != nil { log.Fatalf("failed to listen: %v", err) } // 創(chuàng)建一個 grpc server ser := grpc.NewServer() // 注冊信息 third.RegisterLbsServiceServer(ser, lbsServiceServer{}) // 啟動服務(wù) if err := ser.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } }
2、客戶端代碼
package main import ( "api/third" "context" "github.com/gogo/protobuf/types" "google.golang.org/grpc" "log" "service/common" ) func main() { conn, err := grpc.Dial(common.LbsService, grpc.WithInsecure(), grpc.WithBlock()) if err != nil { log.Fatal(err) } client := third.NewLbsServiceClient(conn) cityInfo, err := client.GetCityInfo(context.Background(), &types.UInt64Value{Value: 1}) if err != nil { log.Fatal(err) } log.Printf("%s", cityInfo) }
請求一下可以看到完全可以調(diào)通
2021/04/16 20:10:48 id:1 name:"beijing-1" properties:"time" value:"2021-04-16T20:10:48+08:00" > msg:"demo" >
5、如何抓包
1、配置pb位置,比如我的就是在/Users/fanhaodong/project/programing/grpc-go/api目錄下
2、選擇抓取網(wǎng)卡
本地的話一般是就是本地回環(huán)網(wǎng)絡(luò),我的本地網(wǎng)卡就是lo0
然后選擇過濾的端口,比如我剛剛啟動的服務(wù)端口是8001, 然后記得客戶端調(diào)用一次服務(wù)端,就可以看到以下的流量包了
此時可以監(jiān)控到流量,但是是tcp,所以我們很難看懂,需要需要分析一下,因此需要decode一下包
所以大概你就可以看到所有包的情況了!
6、http2
7、grpc conn pool
1、如果發(fā)現(xiàn) [] conn
2、如果發(fā)現(xiàn) conn (http2 http3 )
3、
5、使用go-micro 搭建grpc服務(wù)
1、官方文檔
https://github.com/Anthony-Dong/go-micro
微解決了在云中構(gòu)建服務(wù)的關(guān)鍵需求。它利用微服務(wù)體系結(jié)構(gòu)模式,并提供一組作為平臺構(gòu)建塊的服務(wù)。微處理分布式系統(tǒng)的復(fù)雜性,并提供更簡單的可編程抽象。
其實(shí)看起來和那個現(xiàn)在比較火的 dapr 很像,抽象的級別很高,更加傻瓜式,但是你要是研究的話往往會增大學(xué)習(xí)成本
2、快速開始的話,你就根據(jù)官方提供的就行了
2、提供的能力
1、基本上人家?guī)痛a給你封裝好了,直接用
2、提供api-gateway 方便使用
3、提供有 一些內(nèi)部提供的服務(wù)治理能力,需要細(xì)細(xì)學(xué)習(xí)
4、有興趣可以學(xué)習(xí)一下
李熙
版權(quán)所有 未經(jīng)許可不得轉(zhuǎn)載
增值電信業(yè)務(wù)經(jīng)營許可證備案號:遼ICP備14006349號
網(wǎng)站介紹 商務(wù)合作 免責(zé)聲明 - html - txt - xml