科技改變生活 · 科技引領未來
1、wikipedia概念在分布式計算,遠程過程調用(英語:RemoteProcedureCall,縮寫為RPC)是一個計算機通信協議。該協議允許運行于一臺計算機的程序調用另一個地址空間(通常為一個開放網絡的一臺計算機)的子程序,而程序員就
1、wikipedia概念
在分布式計算,遠程過程調用(英語:Remote Procedure Call,縮寫為 RPC)是一個計算機通信協議。該協議允許運行于一臺計算機的程序調用另一個地址空間(通常為一個開放網絡的一臺計算機)的子程序,而程序員就像調用本地程序一樣,無需額外地為這個交互作用編程(無需關注細節)。RPC是一種服務器-客戶端(Client/Server)模式,經典實現是一個通過發送請求-接受回應進行信息交互的系統。
如果涉及的軟件采用面向對象編程,那么遠程過程調用亦可稱作遠程調用或遠程方法調用,例:Java RMI。
RPC是一種進程間通信的模式,程序分布在不同的地址空間里。如果在同一主機里,RPC可以通過不同的虛擬地址空間(即便使用相同的物理地址)進行通訊,而在不同的主機間,則通過不同的物理地址進行交互。許多技術(常常是不兼容)都是基于這種概念而實現的。
2、http和rpc的區別
1、http指的是一個應用層協議,它提供的只是一個傳輸協議
2、rpc講的是一個遠程過程調用,它是一個過程,一個rpc架構包含了多個層級,以dubbo的架構來
rpc其實架構很簡單,就是下面一圖,但是具體實現上差異還是有點,比如我們所了解的 http + json 只能說是dubbo 只能說是dubbo的最下層實現,所以 rpc相對來說偏向于服務治理這一塊
3、為什么我們需要rpc框架,http1.1 提供的rest api不行嗎
1、不支持長連接,keepalive
2、http1.1 ping-pang client - server http , 建立很多個連接 (http tcp 連接慢,傳輸/窗口)
3、rpc 多路復用能力 (http2/3) client - server (io) ,server 包 (二進制) -> go 順序 http2 奇偶
4、并發性
5、rpc tcp 傳輸 -> http1.1 純文本 頭部, rcp hello 行頭體
5、 json rpc
4、比較出名的rpc框架
大廠用的吧,各大廠都有自己的輪子,比如 thrift,gprc,Tars,brpc ,motan, dubbo 還有很多吧
其實論生態的話,絕對是開源項目的生態比較好,所以開源項目中生態比較好的就是 grpc,thrift,dubbo ,使用難度上來看 dubbo是最簡單的,如果你們全Java的話它是個不錯的選擇!
2、grpc介紹
gRPC 是一個現代的開源高性能遠程過程調用(Remote Procedure Call,RPC)框架,可以在任何環境中運行。它可以高效地連接數據中心內部和跨數據中心的服務,并為負載平衡、跟蹤、健康檢查和身份驗證提供可插拔的支持。它也適用于最后一英里的分布式計算連接設備,移動應用程序和瀏覽器的后端服務。
1、開源的grpc官方,主要提供的能力
2、grpc主要采用的技術
3、推薦學習文章,其實grpc是奔著一個規范去走了,所以在開源項目中所使用grpc的項目有很多,云原生中大量的項目使用grpc
關于序列化和反序列化的思考
關于HTTP2相關知識
XDS標準引入GRPC
關于xsd的學習
? grpc-go git:(master) ? tree -L 2 . ├── api ## pb 項目(不同業務組名稱是不一樣的,像我們組直接叫項目名字直接叫api) │ ├── Makefile ## 腳本 │ ├── bin ## protoc / protoc-gen-go/ protoc-gen-gofast 腳本用來生成pb文件 │ ├── dto ## 傳輸層數據 │ ├── go.mod │ ├── go.sum │ └── third ## rpc接口,我們這里叫做third └── service ## 業務項目 ├── client.go ├── common ##common ├── go.mods ├── go.sum ├── lbs-service.go ├── service ## 具體業務層 └── user-service.go
3、protobuf介紹
1、介紹和文檔
Protocol Buffers 是一種與語言無關、平臺無關、可擴展的序列化結構數據的方法,它可用于(數據)通信協議、數據存儲等。Protocol Buffers 是一種靈活,高效,自動化機制的結構數據序列化方法-可類比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更為簡單。你可以定義數據的結構,然后使用特殊生成的源代碼輕松地在各種數據流中使用各種語言進行編寫和讀取結構數據。你甚至可以更新數據結構,而不破壞由舊數據結構編譯的已部署程序。
2、基礎學習
1、基本格式
syntax = "proto3"; // 默認走的是 proto2,所以需要強制指定,pb3比pb2語法上簡單 package dto; //當前文件的package,我們項目不喜歡使用前綴類似于 project-name.dto,因為根本不會存在多個項目互相引用!根據你們需求去決定 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有關系,如果寫 dto,那么別人引入你就是 dto了,顯然是引入不到的,一定要寫全) message SearchRequest { // 每個字段都有四塊組成: 字段規則,類型,名稱,編號 string query = 1; int32 page_number = 2; int32 result_per_page = 3; repeated string hobby = 4; }
2、類型
3、編號
? 消息定義中的每個字段都有一個唯一的編號。這些字段編號用于以消息二進制格式標識字段,在使用消息類型后不應更改。注意,范圍1到15中的字段編號需要一個字節進行編碼,包括字段編號和字段類型。范圍16到2047的字段編號采用兩個字節。因此,應該為經常出現的消息元素保留數字1到15。記住為將來可能添加的頻繁出現的元素留出一些空間。
4、字段規則
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、支持任意的結構體嵌套
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、任意類型
? 這里代表的不是任意的數據類型,指的是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 路徑的路徑
比如我的項目中,引用就是,項目路徑是/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 作用
主要是區分 package 區分 import 可能引用了相同的 message ,所以就需要 package指定,一般package命令為比如我的目錄是api/dto,所以一般命名為api.dto ,一般來說跨業務組是不允許相互引用,只能引用一些common的結構
比如下面這種情況,我引用了兩個 文件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.
其實規則就是
--plugin=protoc-gen-go=bin/protoc-gen-go 對應的必須是--go_out,它走的是拼前綴,比如你執行這個也OK,因為是看前綴說話
bin/protoc --proto_path . --proto_path /Users/fanhaodong/go/grpc/include --plugin=protoc-gen-go1=bin/protoc-gen-go --go1_out=../ ./dto/*.proto
開始進入話題進行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) } } }
測試結果
? 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的兼容,這點比較坑,官網也寫了如何解決:https://github.com/gogo/protobuf , 因此編譯命令需要添加一些參數!
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現在不能使用了,也就是說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、總結
1、從性能上來看確實提升至少是一倍起步,基本帶來了翻倍的收益(官方給的數據是5-10倍的性能提升,它還提供了更快的!1),然后主要是內存分配上可以看到內存優勢很大!
2、但從效率上來說其實對于業務開發其實是不關注太多這些的,開發效率和質量決定一切!
3、關于選擇protoc-gen-gofast 還是選擇protoc-gen-go ,看你們業務已開始用的什么,如果開始就選擇protoc-gen-gofast 那么可以一直用,但是一開始就選擇protoc-gen-go 那就惡心了,基本上無法切換到protoc-gen-gofast,可以選擇使用protoc-gen-gogo
4、gRPC
1、介紹和文檔
? gRPC 是一個現代的開源高性能遠程過程調用(Remote Procedure Call,RPC)框架,可以在任何環境中運行。它可以高效地連接數據中心內部和跨數據中心的服務,并為負載平衡、跟蹤、健康檢查和身份驗證提供可插拔的支持。它也適用于最后一英里的分布式計算連接設備,移動應用程序和瀏覽器的后端服務。
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服務接口:
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進行編譯,所以需要修改一些參數,關于參數如何使用的,這些絕對百度不來,主要是看人家這個gofast項目的文檔和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寫接口服務調用
1、服務端代碼
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() { // 創建一個tcp listener lis, err := net.Listen("tcp", common.LbsService) if err != nil { log.Fatalf("failed to listen: %v", err) } // 創建一個 grpc server ser := grpc.NewServer() // 注冊信息 third.RegisterLbsServiceServer(ser, lbsServiceServer{}) // 啟動服務 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) }
請求一下可以看到完全可以調通
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、選擇抓取網卡
本地的話一般是就是本地回環網絡,我的本地網卡就是lo0
然后選擇過濾的端口,比如我剛剛啟動的服務端口是8001, 然后記得客戶端調用一次服務端,就可以看到以下的流量包了
此時可以監控到流量,但是是tcp,所以我們很難看懂,需要需要分析一下,因此需要decode一下包
所以大概你就可以看到所有包的情況了!
6、http2
7、grpc conn pool
1、如果發現 [] conn
2、如果發現 conn (http2 http3 )
3、
5、使用go-micro 搭建grpc服務
1、官方文檔
https://github.com/Anthony-Dong/go-micro
微解決了在云中構建服務的關鍵需求。它利用微服務體系結構模式,并提供一組作為平臺構建塊的服務。微處理分布式系統的復雜性,并提供更簡單的可編程抽象。
其實看起來和那個現在比較火的 dapr 很像,抽象的級別很高,更加傻瓜式,但是你要是研究的話往往會增大學習成本
2、快速開始的話,你就根據官方提供的就行了
2、提供的能力
1、基本上人家幫代碼給你封裝好了,直接用
2、提供api-gateway 方便使用
3、提供有 一些內部提供的服務治理能力,需要細細學習
4、有興趣可以學習一下
李熙