2828< meta property ="og:description " content ="Remote Procedure Call 本地函数放到服务器运行,会出现若干问题: 我怎么知道是哪个函数?Call Id 本地函数调用,可以直接用指针找到函数;但是远程过程调用不行。 因此我们需要分别在Client和Server维护一个“函数 <-> Call Id”的映射来确定所调用的函数。 Client如何将参数传送到Server?序列化与反序列化 本地函数调用,参数会压入栈 ">
2929< meta property ="og:locale " content ="zh_CN ">
3030< meta property ="article:published_time " content ="2025-06-09T16:00:00.000Z ">
31- < meta property ="article:modified_time " content ="2025-06-18T07:12:51.799Z ">
31+ < meta property ="article:modified_time " content ="2025-06-20T09:07:55.548Z ">
3232< meta property ="article:author " content ="SIMULEITE ">
3333< meta property ="article:tag " content ="知识 ">
3434< meta name ="twitter:card " content ="summary ">
141141 < div class ="sidebar-panel-container ">
142142 <!--noindex-->
143143 < div class ="post-toc-wrap sidebar-panel ">
144- < div class ="post-toc animated "> < ol class ="nav "> < li class ="nav-item nav-level-1 "> < a class ="nav-link " href ="#remote-procedure-call "> < span class ="nav-text "> Remote Procedure Call</ span > </ a > < ol class ="nav-child "> < li class ="nav-item nav-level-2 "> < a class ="nav-link " href ="#%E4%B8%80%E4%B8%AAhttp%E8%AF%B7%E6%B1%82 "> < span class ="nav-text "> 一个HTTP请求</ span > </ a > </ li > </ ol > </ li > < li class ="nav-item nav-level-1 "> < a class ="nav-link " href ="#grpc "> < span class ="nav-text "> gRPC</ span > </ a > < ol class ="nav-child "> < li class ="nav-item nav-level-2 "> < a class ="nav-link " href ="#protobuf "> < span class ="nav-text "> Protobuf</ span > </ a > </ li > < li class ="nav-item nav-level-2 "> < a class ="nav-link " href ="#stream "> < span class ="nav-text "> Stream</ span > </ a > < ol class ="nav-child "> < li class ="nav-item nav-level-3 "> < a class ="nav-link " href ="#simple-rpc "> < span class ="nav-text "> Simple RPC</ span > </ a > </ li > < li class ="nav-item nav-level-3 "> < a class ="nav-link " href ="#server-side-streaming-rpc "> < span class ="nav-text "> Server-side streaming RPC</ span > </ a > </ li > < li class ="nav-item nav-level-3 "> < a class ="nav-link " href ="#client-side-streaming-rpc "> < span class ="nav-text "> Client-side streaming RPC</ span > </ a > </ li > < li class ="nav-item nav-level-3 "> < a class ="nav-link " href ="#bidirectional-streaming-rpc "> < span class ="nav-text "> Bidirectional streaming RPC</ span > </ a > </ li > </ ol > </ li > < li class ="nav-item nav-level-2 "> < a class ="nav-link " href ="#metadata "> < span class ="nav-text "> MetaData</ span > </ a > </ li > </ ol > </ li > < li class ="nav-item nav-level-1 "> < a class ="nav-link " href ="#interceptor "> < span class ="nav-text "> Interceptor</ span > </ a > </ li > </ ol > </ div >
144+ < div class ="post-toc animated "> < ol class ="nav "> < li class ="nav-item nav-level-1 "> < a class ="nav-link " href ="#remote-procedure-call "> < span class ="nav-text "> Remote Procedure Call</ span > </ a > < ol class ="nav-child "> < li class ="nav-item nav-level-2 "> < a class ="nav-link " href ="#%E4%B8%80%E4%B8%AAhttp%E8%AF%B7%E6%B1%82 "> < span class ="nav-text "> 一个HTTP请求</ span > </ a > </ li > </ ol > </ li > < li class ="nav-item nav-level-1 "> < a class ="nav-link " href ="#grpc "> < span class ="nav-text "> gRPC</ span > </ a > < ol class ="nav-child "> < li class ="nav-item nav-level-2 "> < a class ="nav-link " href ="#protobuf "> < span class ="nav-text "> Protobuf</ span > </ a > </ li > < li class ="nav-item nav-level-2 "> < a class ="nav-link " href ="#proto2go "> < span class ="nav-text "> proto2go</ span > </ a > </ li > < li class ="nav-item nav-level-2 "> < a class ="nav-link " href ="#stream "> < span class ="nav-text "> Stream</ span > </ a > < ol class ="nav-child "> < li class ="nav-item nav-level-3 "> < a class ="nav-link " href ="#simple-rpc "> < span class ="nav-text "> Simple RPC</ span > </ a > </ li > < li class ="nav-item nav-level-3 "> < a class ="nav-link " href ="#server-side-streaming-rpc "> < span class ="nav-text "> Server-side streaming RPC</ span > </ a > </ li > < li class ="nav-item nav-level-3 "> < a class ="nav-link " href ="#client-side-streaming-rpc "> < span class ="nav-text "> Client-side streaming RPC</ span > </ a > </ li > < li class ="nav-item nav-level-3 "> < a class ="nav-link " href ="#bidirectional-streaming-rpc "> < span class ="nav-text "> Bidirectional streaming RPC</ span > </ a > </ li > </ ol > </ li > < li class ="nav-item nav-level-2 "> < a class ="nav-link " href ="#metadata "> < span class ="nav-text "> MetaData</ span > </ a > </ li > </ ol > </ li > < li class ="nav-item nav-level-1 "> < a class ="nav-link " href ="#interceptor "> < span class ="nav-text "> Interceptor</ span > </ a > </ li > < li class ="nav-item nav-level-1 "> < a class ="nav-link " href ="#validation "> < span class ="nav-text "> Validation</ span > </ a > </ li > </ ol > </ div >
145145 </ div >
146146 <!--/noindex-->
147147
@@ -223,7 +223,7 @@ <h1 class="post-title" itemprop="name headline">
223223 < i class ="far fa-calendar-check "> </ i >
224224 </ span >
225225 < span class ="post-meta-item-text "> 更新于</ span >
226- < time title ="修改时间:2025-06-18 15:12:51 " itemprop ="dateModified " datetime ="2025-06-18T15:12:51 +08:00 "> 2025-06-18 </ time >
226+ < time title ="修改时间:2025-06-20 17:07:55 " itemprop ="dateModified " datetime ="2025-06-20T17:07:55 +08:00 "> 2025-06-20 </ time >
227227 </ span >
228228
229229
@@ -265,6 +265,12 @@ <h2 id="protobuf"><a class="markdownIt-Anchor" href="#protobuf"></a> Protobuf</h
265265</ ul >
266266< figure class ="highlight protobuf "> < table > < tr > < td class ="code "> < pre > < span class ="line "> < span class ="keyword "> message </ span > < span class ="title class_ "> HelloReq</ span > {</ span > < br > < span class ="line "> < span class ="keyword "> message </ span > < span class ="title class_ "> InnerReq</ span > {</ span > < br > < span class ="line "> < span class ="type "> string</ span > name = < span class ="number "> 1</ span > ;</ span > < br > < span class ="line "> < span class ="type "> string</ span > url = < span class ="number "> 2</ span > ;</ span > < br > < span class ="line "> }</ span > < br > < span class ="line "> Gender gender = < span class ="number "> 1</ span > ;</ span > < br > < span class ="line "> map<< span class ="type "> string</ span > , < span class ="type "> string</ span > > map = < span class ="number "> 2</ span > ;</ span > < br > < span class ="line "> google.protobuf.Timestamp createTime = < span class ="number "> 3</ span > ;</ span > < br > < span class ="line "> < span class ="keyword "> repeated</ span > InnerReq req = < span class ="number "> 4</ span > ;</ span > < br > < span class ="line "> }</ span > < br > </ pre > </ td > </ tr > </ table > </ figure >
267267< figure class ="highlight shell "> < table > < tr > < td class ="code "> < pre > < span class ="line "> // -I 路径; --go_out 生成go代码; plugins=grpc:. 使用grpc拓展,使用grpc拓展生成接口代码,放在当前目录下</ span > < br > < span class ="line "> protoc -I . <filename>.proto --go_out=plugins=grpc:.</ span > < br > </ pre > </ td > </ tr > </ table > </ figure >
268+ < h2 id ="proto2go "> < a class ="markdownIt-Anchor " href ="#proto2go "> </ a > proto2go</ h2 >
269+ < figure class ="highlight protobuf "> < table > < tr > < td class ="code "> < pre > < span class ="line "> < span class ="keyword "> service </ span > < span class ="title class_ "> Greeter</ span > {</ span > < br > < span class ="line "> < span class ="function "> < span class ="keyword "> rpc</ span > SayHello (HelloReq) < span class ="keyword "> returns</ span > (HelloResp)</ span > ;</ span > < br > < span class ="line "> }</ span > < br > </ pre > </ td > </ tr > </ table > </ figure >
270+ < p > Server</ p >
271+ < figure class ="highlight go "> < table > < tr > < td class ="code "> < pre > < span class ="line "> < span class ="keyword "> type</ span > GreeterServer < span class ="keyword "> interface</ span > {</ span > < br > < span class ="line "> SayHello(context.Context, *HelloReq) (*HelloReply, < span class ="type "> error</ span > )</ span > < br > < span class ="line "> }</ span > < br > < span class ="line "> </ span > < br > < span class ="line "> < span class ="function "> < span class ="keyword "> func</ span > < span class ="title "> RegisterGreeterServer</ span > < span class ="params "> (s *gprc.Server, srv GreeterServer)</ span > </ span > {</ span > < br > < span class ="line "> s.RegisterService(&_Greeter_serviceDesc, srv)</ span > < br > < span class ="line "> }</ span > < br > </ pre > </ td > </ tr > </ table > </ figure >
272+ < p > Client</ p >
273+ < figure class ="highlight go "> < table > < tr > < td class ="code "> < pre > < span class ="line "> < span class ="keyword "> type</ span > greeterClient < span class ="keyword "> struct</ span > {</ span > < br > < span class ="line "> cc grpc.ClientConnInterface</ span > < br > < span class ="line "> }</ span > < br > < span class ="line "> </ span > < br > < span class ="line "> < span class ="function "> < span class ="keyword "> func</ span > < span class ="title "> NewGreeterClient</ span > < span class ="params "> (cc grpc.ClientConnInterface)</ span > </ span > GreeterClient {</ span > < br > < span class ="line "> < span class ="comment "> // 返回一个实现了Interface所有方法的结构体</ span > </ span > < br > < span class ="line "> < span class ="keyword "> return</ span > &greeterClient{cc}</ span > < br > < span class ="line "> }</ span > < br > < span class ="line "> </ span > < br > < span class ="line "> < span class ="keyword "> type</ span > GreeterClient < span class ="keyword "> interface</ span > {</ span > < br > < span class ="line "> SayHello(ctx content.Context, in *HelloReq, opts ...grpc.CallOption) (*HelloReply, < span class ="type "> error</ span > )</ span > < br > < span class ="line "> }</ span > < br > < span class ="line "> </ span > < br > < span class ="line "> < span class ="function "> < span class ="keyword "> func</ span > < span class ="params "> (c *greeterClient)</ span > </ span > SayHello(ctx context.Context, in *HelloReq, opts ...grpc.CallOption) (*HelloReply, < span class ="type "> error</ span > ) {</ span > < br > < span class ="line "> out := < span class ="built_in "> new</ span > (HelloReply)</ span > < br > < span class ="line "> err := c.cc.Invoke(ctx, < span class ="string "> "/Greeter/SayHello"</ span > , in, out, opts...)</ span > < br > < span class ="line "> < span class ="keyword "> if</ span > err != < span class ="literal "> nil</ span > {</ span > < br > < span class ="line "> < span class ="keyword "> return</ span > out, < span class ="literal "> nil</ span > </ span > < br > < span class ="line "> }</ span > < br > < span class ="line "> }</ span > < br > </ pre > </ td > </ tr > </ table > </ figure >
268274< h2 id ="stream "> < a class ="markdownIt-Anchor " href ="#stream "> </ a > Stream</ h2 >
269275< h3 id ="simple-rpc "> < a class ="markdownIt-Anchor " href ="#simple-rpc "> </ a > Simple RPC</ h3 >
270276< p > Client和Server都建立短连接。</ p >
@@ -281,9 +287,17 @@ <h3 id="bidirectional-streaming-rpc"><a class="markdownIt-Anchor" href="#bidirec
281287< figure class ="highlight protobuf "> < table > < tr > < td class ="code "> < pre > < span class ="line "> < span class ="keyword "> service </ span > < span class ="title class_ "> Greeter</ span > {</ span > < br > < span class ="line "> < span class ="function "> < span class ="keyword "> rpc</ span > GetStream(stream StreamReq) < span class ="keyword "> returns</ span > (stream StreamResp)</ span > </ span > < br > < span class ="line "> < span class ="function "> }</ span > </ span > < br > </ pre > </ td > </ tr > </ table > </ figure >
282288< h2 id ="metadata "> < a class ="markdownIt-Anchor " href ="#metadata "> </ a > MetaData</ h2 >
283289< p > gRPC和HTTP一样,可以携带一些MetaData</ p >
284- < figure class ="highlight shell "> < table > < tr > < td class ="code "> < pre > < span class ="line "> :authority [localhost:port]</ span > < br > < span class ="line "> content-type [application/grpc]</ span > < br > < span class ="line "> user-agent [grpc-gp/version]</ span > < br > < span class ="line "> </ span > < br > < span class ="line "> data [your_data]</ span > < br > </ pre > </ td > </ tr > </ table > </ figure >
290+ < figure class ="highlight plaintext "> < table > < tr > < td class ="code "> < pre > < span class ="line "> :authority [localhost:port]</ span > < br > < span class ="line "> content-type [application/grpc]</ span > < br > < span class ="line "> user-agent [grpc-gp/version]</ span > < br > < span class ="line "> </ span > < br > < span class ="line "> data [your_data]</ span > < br > </ pre > </ td > </ tr > </ table > </ figure >
285291< h1 id ="interceptor "> < a class ="markdownIt-Anchor " href ="#interceptor "> </ a > Interceptor</ h1 >
286292< figure class ="highlight go "> < table > < tr > < td class ="code "> < pre > < span class ="line "> interceptorCust := < span class ="function "> < span class ="keyword "> func</ span > < span class ="params "> (ctx context.Context, req Interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler)</ span > </ span > (resp Interface{}, err < span class ="type "> error</ span > ) {</ span > < br > < span class ="line "> fmt.Println(< span class ="string "> "接收到新请求: "</ span > , req)</ span > < br > < span class ="line "> start := time.Now()</ span > < br > < span class ="line "> </ span > < br > < span class ="line "> res, err := handler(ctx, req)</ span > < br > < span class ="line "> </ span > < br > < span class ="line "> fmt.Println(< span class ="string "> "请求完成,耗时: "</ span > , time.Since(start))</ span > < br > < span class ="line "> < span class ="keyword "> return</ span > res, err</ span > < br > < span class ="line "> }</ span > < br > < span class ="line "> opt := grpc.UnaryInterceptor(interceptorCust)</ span > < br > < span class ="line "> g := grpc.NewServer(opt)</ span > < br > </ pre > </ td > </ tr > </ table > </ figure >
293+ < h1 id ="validation "> < a class ="markdownIt-Anchor " href ="#validation "> </ a > Validation</ h1 >
294+ < p > plugin: protoc-gen-validate</ p >
295+ < figure class ="highlight protobuf "> < table > < tr > < td class ="code "> < pre > < span class ="line "> < span class ="keyword "> message </ span > < span class ="title class_ "> Person</ span > {</ span > < br > < span class ="line "> < span class ="comment "> // id > 999</ span > </ span > < br > < span class ="line "> < span class ="type "> uint64</ span > id = < span class ="number "> 1</ span > [(validate.rules).< span class ="type "> uint64</ span > .gt = < span class ="number "> 999</ span > ];</ span > < br > < span class ="line "> < span class ="comment "> // email validation</ span > </ span > < br > < span class ="line "> < span class ="type "> string</ span > email = < span class ="number "> 2</ span > [(validate.rules).< span class ="type "> string</ span > .email = < span class ="literal "> true</ span > ];</ span > < br > < span class ="line "> < span class ="comment "> // custom validation</ span > </ span > < br > < span class ="line "> < span class ="type "> string</ span > name = < span class ="number "> 3</ span > [(validate.rules).< span class ="type "> string</ span > = {</ span > < br > < span class ="line "> pattern: < span class ="string "> "^[0-9]&"</ span > ,</ span > < br > < span class ="line "> max_bytes: < span class ="number "> 256</ span > ,</ span > < br > < span class ="line "> }];</ span > < br > < span class ="line "> < span class ="comment "> // not null</ span > </ span > < br > < span class ="line "> Location home = < span class ="number "> 4</ span > [(validate.rules).message.< span class ="keyword "> required</ span > = ture];</ span > < br > < span class ="line "> }</ span > < br > < span class ="line "> </ span > < br > < span class ="line "> < span class ="keyword "> message </ span > < span class ="title class_ "> Location</ span > {</ span > < br > < span class ="line "> < span class ="comment "> // multi-args validation</ span > </ span > < br > < span class ="line "> < span class ="type "> double</ span > lat = < span class ="number "> 1</ span > [(validate.rules).< span class ="type "> double</ span > = { gte: -< span class ="number "> 90</ span > , lte: < span class ="number "> 90</ span > }];</ span > < br > < span class ="line "> }</ span > < br > </ pre > </ td > </ tr > </ table > </ figure >
296+ < figure class ="highlight shell "> < table > < tr > < td class ="code "> < pre > < span class ="line "> protoc --validate_out="lang=go:."</ span > < br > </ pre > </ td > </ tr > </ table > </ figure >
297+ < figure class ="highlight go "> < table > < tr > < td class ="code "> < pre > < span class ="line "> p := < span class ="built_in "> new</ span > (Person)</ span > < br > < span class ="line "> < span class ="comment "> // throw error automatically</ span > </ span > < br > < span class ="line "> err := p.Validate()</ span > < br > < span class ="line "> < span class ="keyword "> if</ span > err != < span class ="literal "> nil</ span > {</ span > < br > < span class ="line "> < span class ="built_in "> panic</ span > (err)</ span > < br > < span class ="line "> }</ span > < br > </ pre > </ td > </ tr > </ table > </ figure >
298+ < p > 搭配拦截器</ p >
299+ < figure class ="highlight go "> < table > < tr > < td class ="code "> < pre > < span class ="line "> < span class ="function "> < span class ="keyword "> func</ span > < span class ="title "> interceptor</ span > < span class ="params "> (ctx, req, info, handler)</ span > </ span > (resp, err) {</ span > < br > < span class ="line "> < span class ="keyword "> if</ span > r, ok := req.(Validator); ok {</ span > < br > < span class ="line "> < span class ="keyword "> if</ span > err := r.Validate(); err != < span class ="literal "> nil</ span > {</ span > < br > < span class ="line "> < span class ="keyword "> return</ span > < span class ="literal "> nil</ span > , status.Error(codes.InvalidArgument, err.Error())</ span > < br > < span class ="line "> }</ span > < br > < span class ="line "> }</ span > < br > < span class ="line "> < span class ="keyword "> return</ span > handler(ctx, req)</ span > < br > < span class ="line "> }</ span > < br > </ pre > </ td > </ tr > </ table > </ figure >
300+
287301 </ div >
288302
289303
0 commit comments