From 9b1941f96171a17111027046bf346d022045cfb9 Mon Sep 17 00:00:00 2001 From: chai2010 Date: Fri, 29 Jun 2018 15:15:25 +0800 Subject: [PATCH] ch4-02: done --- SUMMARY.md | 2 +- ch4-rpc/ch4-02-pb-intro.md | 433 +++++++++++++++++- examples/ch4-02-proto/proto-v1/Makefile | 0 examples/ch4-02-proto/proto-v1/hello.pb.go | 105 +++++ examples/ch4-02-proto/proto-v1/hello.proto | 11 + examples/ch4-02-proto/proto-v1/main.go | 12 + .../ch4-02-proto/protoc-gen-go-netrpc/main.go | 98 ++++ .../protoc-gen-go-netrpc/netprpc.go | 155 +++++++ 8 files changed, 804 insertions(+), 12 deletions(-) create mode 100644 examples/ch4-02-proto/proto-v1/Makefile create mode 100644 examples/ch4-02-proto/proto-v1/hello.pb.go create mode 100644 examples/ch4-02-proto/proto-v1/hello.proto create mode 100644 examples/ch4-02-proto/proto-v1/main.go create mode 100644 examples/ch4-02-proto/protoc-gen-go-netrpc/main.go create mode 100644 examples/ch4-02-proto/protoc-gen-go-netrpc/netprpc.go diff --git a/SUMMARY.md b/SUMMARY.md index fc1c8b1..27f42c2 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -33,7 +33,7 @@ * [3.8. 补充说明](ch3-asm/ch3-08-faq.md) * [第四章 RPC和Protobuf](ch4-rpc/readme.md) * [4.1. RPC入门](ch4-rpc/ch4-01-rpc-intro.md) - * [4.2. Protobuf简介(TODO)](ch4-rpc/ch4-02-pb-intro.md) + * [4.2. Protobuf](ch4-rpc/ch4-02-pb-intro.md) * [4.3. protorpc(TODO)](ch4-rpc/ch4-03-protorpc.md) * [4.4. grpc(TODO)](ch4-rpc/ch4-04-grpc.md) * [4.5. 反向rpc(TODO)](ch4-rpc/ch4-05-reverse-rpc.md) diff --git a/ch4-rpc/ch4-02-pb-intro.md b/ch4-rpc/ch4-02-pb-intro.md index 084a84e..6878595 100644 --- a/ch4-rpc/ch4-02-pb-intro.md +++ b/ch4-rpc/ch4-02-pb-intro.md @@ -1,19 +1,430 @@ -# 4.2. Protobuf简介 +# 4.2. Protobuf -TODO +Protobuf是Protocol Buffers的简称,它是Google公司开发的一种数据描述语言,并于2008年对外开源。Protobuf刚开源时的定位类似于XML、JSON等数据描述语言,通过附带工具生成等代码提实现将结构化数据序列化的功能。但是我们更关注的是Protobuf作为接口规范的描述语言,可以作为设计安全的跨语言PRC接口的基础工具。 - diff --git a/examples/ch4-02-proto/proto-v1/Makefile b/examples/ch4-02-proto/proto-v1/Makefile new file mode 100644 index 0000000..e69de29 diff --git a/examples/ch4-02-proto/proto-v1/hello.pb.go b/examples/ch4-02-proto/proto-v1/hello.pb.go new file mode 100644 index 0000000..67a77ae --- /dev/null +++ b/examples/ch4-02-proto/proto-v1/hello.pb.go @@ -0,0 +1,105 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// source: hello.proto + +package main + +import proto "github.com/golang/protobuf/proto" +import fmt "fmt" +import math "math" + +import "net/rpc" + +// Reference imports to suppress errors if they are not otherwise used. +var _ = proto.Marshal +var _ = fmt.Errorf +var _ = math.Inf + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the proto package it is being compiled against. +// A compilation error at this line likely means your copy of the +// proto package needs to be updated. +const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package + +type String struct { + Value string `protobuf:"bytes,1,opt,name=value" json:"value,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *String) Reset() { *m = String{} } +func (m *String) String() string { return proto.CompactTextString(m) } +func (*String) ProtoMessage() {} +func (*String) Descriptor() ([]byte, []int) { + return fileDescriptor_hello_5dd9d59ecabc789f, []int{0} +} +func (m *String) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_String.Unmarshal(m, b) +} +func (m *String) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_String.Marshal(b, m, deterministic) +} +func (dst *String) XXX_Merge(src proto.Message) { + xxx_messageInfo_String.Merge(dst, src) +} +func (m *String) XXX_Size() int { + return xxx_messageInfo_String.Size(m) +} +func (m *String) XXX_DiscardUnknown() { + xxx_messageInfo_String.DiscardUnknown(m) +} + +var xxx_messageInfo_String proto.InternalMessageInfo + +func (m *String) GetValue() string { + if m != nil { + return m.Value + } + return "" +} + +func init() { + proto.RegisterType((*String)(nil), "main.String") +} + +type HelloServiceInterface interface { + Hello(in String, out *String) error +} + +func RegisterHelloService(srv *rpc.Server, x HelloService) error { + if err := srv.RegisterName("HelloService", x); err != nil { + return err + } + return nil +} + +type HelloServiceClient struct { + *rpc.Client +} + +var _ HelloServiceInterface = (*HelloServiceClient)(nil) + +func DialHelloService(network, address string) (*HelloServiceClient, error) { + c, err := rpc.Dial(network, address) + if err != nil { + return nil, err + } + return &HelloServiceClient{Client: c}, nil +} + +func (p *HelloServiceClient) Hello(in String, out *String) error { + return p.Client.Call("HelloService.Hello", in, out) +} + +func init() { proto.RegisterFile("hello.proto", fileDescriptor_hello_5dd9d59ecabc789f) } + +var fileDescriptor_hello_5dd9d59ecabc789f = []byte{ + // 107 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0xce, 0x48, 0xcd, 0xc9, + 0xc9, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0xc9, 0x4d, 0xcc, 0xcc, 0x53, 0x92, 0xe3, + 0x62, 0x0b, 0x2e, 0x29, 0xca, 0xcc, 0x4b, 0x17, 0x12, 0xe1, 0x62, 0x2d, 0x4b, 0xcc, 0x29, 0x4d, + 0x95, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0c, 0x82, 0x70, 0x8c, 0x8c, 0xb9, 0x78, 0x3c, 0x40, 0x9a, + 0x82, 0x53, 0x8b, 0xca, 0x32, 0x93, 0x53, 0x85, 0x94, 0xb9, 0x58, 0xc1, 0x7c, 0x21, 0x1e, 0x3d, + 0x90, 0x7e, 0x3d, 0x88, 0x66, 0x29, 0x14, 0x5e, 0x12, 0x1b, 0xd8, 0x06, 0x63, 0x40, 0x00, 0x00, + 0x00, 0xff, 0xff, 0xa1, 0x2a, 0xa2, 0x5a, 0x70, 0x00, 0x00, 0x00, +} diff --git a/examples/ch4-02-proto/proto-v1/hello.proto b/examples/ch4-02-proto/proto-v1/hello.proto new file mode 100644 index 0000000..d5542f9 --- /dev/null +++ b/examples/ch4-02-proto/proto-v1/hello.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +package main; + +message String { + string value = 1; +} + +service HelloService { + rpc Hello (String) returns (String); +} diff --git a/examples/ch4-02-proto/proto-v1/main.go b/examples/ch4-02-proto/proto-v1/main.go new file mode 100644 index 0000000..b25334d --- /dev/null +++ b/examples/ch4-02-proto/proto-v1/main.go @@ -0,0 +1,12 @@ +package main + +type HelloService struct{} + +func (p *HelloService) Hello(request String, reply *String) error { + reply.Value = "hello:" + request.GetValue() + return nil +} + +func main() { + +} diff --git a/examples/ch4-02-proto/protoc-gen-go-netrpc/main.go b/examples/ch4-02-proto/protoc-gen-go-netrpc/main.go new file mode 100644 index 0000000..8e2486d --- /dev/null +++ b/examples/ch4-02-proto/protoc-gen-go-netrpc/main.go @@ -0,0 +1,98 @@ +// Go support for Protocol Buffers - Google's data interchange format +// +// Copyright 2010 The Go Authors. All rights reserved. +// https://github.com/golang/protobuf +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// protoc-gen-go is a plugin for the Google protocol buffer compiler to generate +// Go code. Run it by building this program and putting it in your path with +// the name +// protoc-gen-go +// That word 'go' at the end becomes part of the option string set for the +// protocol compiler, so once the protocol compiler (protoc) is installed +// you can run +// protoc --go_out=output_directory input_directory/file.proto +// to generate Go bindings for the protocol defined by file.proto. +// With that input, the output will be written to +// output_directory/file.pb.go +// +// The generated code is documented in the package comment for +// the library. +// +// See the README and documentation for protocol buffers to learn more: +// https://developers.google.com/protocol-buffers/ +package main + +import ( + "io/ioutil" + "os" + + "github.com/golang/protobuf/proto" + "github.com/golang/protobuf/protoc-gen-go/generator" +) + +func main() { + // Begin by allocating a generator. The request and response structures are stored there + // so we can do error handling easily - the response structure contains the field to + // report failure. + g := generator.New() + + data, err := ioutil.ReadAll(os.Stdin) + if err != nil { + g.Error(err, "reading input") + } + + if err := proto.Unmarshal(data, g.Request); err != nil { + g.Error(err, "parsing input proto") + } + + if len(g.Request.FileToGenerate) == 0 { + g.Fail("no files to generate") + } + + g.CommandLineParameters(g.Request.GetParameter()) + + // Create a wrapped version of the Descriptors and EnumDescriptors that + // point to the file that defines them. + g.WrapTypes() + + g.SetPackageNames() + g.BuildTypeNameMap() + + g.GenerateAllFiles() + + // Send back the results. + data, err = proto.Marshal(g.Response) + if err != nil { + g.Error(err, "failed to marshal output proto") + } + _, err = os.Stdout.Write(data) + if err != nil { + g.Error(err, "failed to write output proto") + } +} diff --git a/examples/ch4-02-proto/protoc-gen-go-netrpc/netprpc.go b/examples/ch4-02-proto/protoc-gen-go-netrpc/netprpc.go new file mode 100644 index 0000000..0df1934 --- /dev/null +++ b/examples/ch4-02-proto/protoc-gen-go-netrpc/netprpc.go @@ -0,0 +1,155 @@ +// Copyright 2013 . All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import ( + "bytes" + "text/template" + + "github.com/golang/protobuf/protoc-gen-go/descriptor" + "github.com/golang/protobuf/protoc-gen-go/generator" +) + +type netrpcPlugin struct{ *generator.Generator } + +func (p *netrpcPlugin) Name() string { return "netrpc" } +func (p *netrpcPlugin) Init(g *generator.Generator) { p.Generator = g } + +func (p *netrpcPlugin) GenerateImports(file *generator.FileDescriptor) { + if len(file.Service) > 0 { + p.P(`import "net/rpc"`) + } +} + +func (p *netrpcPlugin) Generate(file *generator.FileDescriptor) { + for _, svc := range file.Service { + p.genServiceInterface(file, svc) + p.genServiceServer(file, svc) + p.genServiceClient(file, svc) + } +} + +func (p *netrpcPlugin) genServiceInterface( + file *generator.FileDescriptor, + svc *descriptor.ServiceDescriptorProto, +) { + const serviceInterfaceTmpl = ` +type {{.ServiceName}}Interface interface { + {{.CallMethodList}} +} +` + const callMethodTmpl = ` +{{.MethodName}}(in {{.ArgsType}}, out *{{.ReplyType}}) error` + + // gen call method list + var callMethodList string + for _, m := range svc.Method { + out := bytes.NewBuffer([]byte{}) + t := template.Must(template.New("").Parse(callMethodTmpl)) + t.Execute(out, &struct{ ServiceName, MethodName, ArgsType, ReplyType string }{ + ServiceName: generator.CamelCase(svc.GetName()), + MethodName: generator.CamelCase(m.GetName()), + ArgsType: p.TypeName(p.ObjectNamed(m.GetInputType())), + ReplyType: p.TypeName(p.ObjectNamed(m.GetOutputType())), + }) + callMethodList += out.String() + + p.RecordTypeUse(m.GetInputType()) + p.RecordTypeUse(m.GetOutputType()) + } + + // gen all interface code + { + out := bytes.NewBuffer([]byte{}) + t := template.Must(template.New("").Parse(serviceInterfaceTmpl)) + t.Execute(out, &struct{ ServiceName, CallMethodList string }{ + ServiceName: generator.CamelCase(svc.GetName()), + CallMethodList: callMethodList, + }) + p.P(out.String()) + } +} + +func (p *netrpcPlugin) genServiceServer( + file *generator.FileDescriptor, + svc *descriptor.ServiceDescriptorProto, +) { + const serviceHelperFunTmpl = ` +func Register{{.ServiceName}}(srv *rpc.Server, x {{.ServiceName}}) error { + if err := srv.RegisterName("{{.ServiceName}}", x); err != nil { + return err + } + return nil +} +` + { + out := bytes.NewBuffer([]byte{}) + t := template.Must(template.New("").Parse(serviceHelperFunTmpl)) + t.Execute(out, &struct{ PackageName, ServiceName, ServiceRegisterName string }{ + PackageName: file.GetPackage(), + ServiceName: generator.CamelCase(svc.GetName()), + }) + p.P(out.String()) + } +} + +func (p *netrpcPlugin) genServiceClient( + file *generator.FileDescriptor, + svc *descriptor.ServiceDescriptorProto, +) { + const clientHelperFuncTmpl = ` +type {{.ServiceName}}Client struct { + *rpc.Client +} + +var _ {{.ServiceName}}Interface = (*{{.ServiceName}}Client)(nil) + +func Dial{{.ServiceName}}(network, address string) (*{{.ServiceName}}Client, error) { + c, err := rpc.Dial(network, address) + if err != nil { + return nil, err + } + return &{{.ServiceName}}Client{Client: c}, nil +} + +{{.MethodList}} +` + const clientMethodTmpl = ` +func (p *{{.ServiceName}}Client) {{.MethodName}}(in {{.ArgsType}}, out *{{.ReplyType}}) error { + return p.Client.Call("{{.ServiceName}}.{{.MethodName}}", in, out) +} +` + + // gen client method list + var methodList string + for _, m := range svc.Method { + out := bytes.NewBuffer([]byte{}) + t := template.Must(template.New("").Parse(clientMethodTmpl)) + t.Execute(out, &struct{ ServiceName, ServiceRegisterName, MethodName, ArgsType, ReplyType string }{ + ServiceName: generator.CamelCase(svc.GetName()), + ServiceRegisterName: file.GetPackage() + "." + generator.CamelCase(svc.GetName()), + MethodName: generator.CamelCase(m.GetName()), + ArgsType: p.TypeName(p.ObjectNamed(m.GetInputType())), + ReplyType: p.TypeName(p.ObjectNamed(m.GetOutputType())), + }) + methodList += out.String() + } + + // gen all client code + { + out := bytes.NewBuffer([]byte{}) + t := template.Must(template.New("").Parse(clientHelperFuncTmpl)) + t.Execute(out, &struct{ PackageName, ServiceName, MethodList string }{ + PackageName: file.GetPackage(), + ServiceName: generator.CamelCase(svc.GetName()), + MethodList: methodList, + }) + p.P(out.String()) + } +} + +func init() { + generator.RegisterPlugin(new(netrpcPlugin)) +}