生命不止,继续 go go go !!!
号外号外,插播一条广告,通过博客的uv可以看到周五,程序员是不怎么干活的:
本篇博客,使用gRPC和Protobuf,实现所谓的高性能api。
protobuf
golang中的protobuf大家应该不会很陌生,之前也有博客介绍过:
Go实战–go中使用google/protobuf(The way to go)
Protocol Buffers (a.k.a.,protobuf) are Google’s language-neutral,platform-neutral,extensible mechanism for serializing structured data. You can find protobuf’s documentation on the Google Developers site.
获取:
- go get -u github.com/golang/protobuf/proto
- go get -u github.com/golang/protobuf/protoc-gen-go
Protobuf语法
下面简要介绍Protobuf语法:
参考:http://www.jb51.cc/article/p-fqzbjyww-d.html
官方:
https://developers.google.com/protocol-buffers/
https://developers.google.com/protocol-buffers/docs/gotutorial
Message定义
一个message类型定义描述了一个请求或相应的消息格式,可以包含多种类型字段。例如定义一个搜索请求的消息格式,每个请求包含查询字符串、页码、每页数目。
- Syntax = "proto3";
- package tutorial;
首行声明使用的protobuf版本为proto3
- message Person {
- string name = 1;
- int32 id = 2; // Unique ID number for this person.
- string email = 3;
-
- enum PhoneType {
- MOBILE = 0;
- HOME = 1;
- WORK = 2;
- }
-
- message PhoneNumber {
- string number = 1;
- PhoneType type = 2;
- }
-
- repeated PhoneNumber phones = 4;
- }
-
- // Our address book file is just one of these.
- message AddressBook {
- repeated Person people = 1;
- }
- protoc -I=$SRC_DIR --go_out=$DST_DIR $SRC_DIR/addressbook.proto
写消息
读消息
grpc
golang中的rpc大家也不会陌生,之前也有介绍过奥:
Go实战–go中使用rpc(The way to go)
什么是rpc
RPC是Remote Procedure CallProtocol的缩写,即—远程过程调用协议。
RPC是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。如果涉及的软件采用面向对象编程,那么远程过程调用亦可称作远程调用或远程方法调用,信息数据。通过它可以使函数调用模式网络化。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
net/rpc
Go语言标准库能够自带一个rpc框架还是非常给力的,这可以很大程度的降低写后端网络通信服务的门槛,特别是在大规模的分布式系统中,rpc基本是跨机器通信的标配。rpc能够最大程度屏蔽网络细节,让开发者专注在服务功能的开发上面
什么是grpc
gRPC 是一个高性能、开源、通用的RPC框架,由Google推出,基于HTTP/2协议标准设计开发,默认采用Protocol Buffers数据序列化协议,支持多种开发语言。gRPC提供了一种简单的方法来精确的定义服务,并且为客户端和服务端自动生成可靠的功能库。
在gRPC客户端可以直接调用不同服务器上的远程程序,使用姿势看起来就像调用本地程序一样,很容易去构建分布式应用和服务。和很多RPC系统一样,服务端负责实现定义好的接口并处理客户端的请求,客户端根据接口描述直接调用需要的服务。客户端和服务端可以分别使用gRPC支持的不同语言实现。
grpc-go
github地址:
https://github.com/grpc/grpc-go
Star: 4402
文档地址:
https://godoc.org/google.golang.org/grpc
获取:
go get -u google.golang.org/grpc
文件结构
proto文件
customer.proto
- Syntax = "proto3";
- package customer;
-
-
- // The Customer service definition.
- service Customer {
- // Get all Customers with filter - A server-to-client streaming RPC.
- rpc GetCustomers(CustomerFilter) returns (stream CustomerRequest) {}
- // Create a new Customer - A simple RPC
- rpc CreateCustomer (CustomerRequest) returns (CustomerResponse) {}
- }
-
- // Request message for creating a new customer
- message CustomerRequest {
- int32 id = 1; // Unique ID number for a Customer.
- string name = 2;
- string email = 3;
- string phone= 4;
-
- message Address {
- string street = 1;
- string city = 2;
- string state = 3;
- string zip = 4;
- bool isShippingAddress = 5;
- }
-
- repeated Address addresses = 5;
- }
-
- message CustomerResponse {
- int32 id = 1;
- bool success = 2;
- }
- message CustomerFilter {
- string keyword = 1;
- }
生成customer.pb.go
- protoc --go_out=plugins=grpc:. customer.proto
- // Code generated by protoc-gen-go. DO NOT EDIT.
- // source: customer.proto
-
- /* Package customer is a generated protocol buffer package. It is generated from these files: customer.proto It has these top-level messages: CustomerRequest CustomerResponse CustomerFilter */
- package customer
-
- import proto "github.com/golang/protobuf/proto"
- import fmt "fmt"
- import math "math"
-
- import (
- context "golang.org/x/net/context"
- grpc "google.golang.org/grpc"
- )
-
- // 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
-
- // Request message for creating a new customer
- type CustomerRequest struct {
- Id int32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"`
- Name string `protobuf:"bytes,2,name=name" json:"name,omitempty"`
- Email string `protobuf:"bytes,3,name=email" json:"email,omitempty"`
- Phone string `protobuf:"bytes,4,name=phone" json:"phone,omitempty"`
- Addresses []*CustomerRequest_Address `protobuf:"bytes,5,rep,name=addresses" json:"addresses,omitempty"`
- }
-
- func (m *CustomerRequest) Reset() { *m = CustomerRequest{} }
- func (m *CustomerRequest) String() string { return proto.CompactTextString(m) }
- func (*CustomerRequest) ProtoMessage() {}
- func (*CustomerRequest) Descriptor() ([]byte,[]int) { return fileDescriptor0,[]int{0} }
-
- func (m *CustomerRequest) GetId() int32 {
- if m != nil {
- return m.Id
- }
- return 0
- }
-
- func (m *CustomerRequest) GetName() string {
- if m != nil {
- return m.Name
- }
- return ""
- }
-
- func (m *CustomerRequest) GetEmail() string {
- if m != nil {
- return m.Email
- }
- return ""
- }
-
- func (m *CustomerRequest) GetPhone() string {
- if m != nil {
- return m.Phone
- }
- return ""
- }
-
- func (m *CustomerRequest) GetAddresses() []*CustomerRequest_Address {
- if m != nil {
- return m.Addresses
- }
- return nil
- }
-
- type CustomerRequest_Address struct {
- Street string `protobuf:"bytes,name=street" json:"street,omitempty"`
- City string `protobuf:"bytes,name=city" json:"city,omitempty"`
- State string `protobuf:"bytes,name=state" json:"state,omitempty"`
- Zip string `protobuf:"bytes,name=zip" json:"zip,omitempty"`
- IsShippingAddress bool `protobuf:"varint,name=isShippingAddress" json:"isShippingAddress,omitempty"`
- }
-
- func (m *CustomerRequest_Address) Reset() { *m = CustomerRequest_Address{} }
- func (m *CustomerRequest_Address) String() string { return proto.CompactTextString(m) }
- func (*CustomerRequest_Address) ProtoMessage() {}
- func (*CustomerRequest_Address) Descriptor() ([]byte,[]int{0, 0} }
-
- func (m *CustomerRequest_Address) GetStreet() string {
- if m != nil {
- return m.Street
- }
- return ""
- }
-
- func (m *CustomerRequest_Address) GetCity() string {
- if m != nil {
- return m.City
- }
- return ""
- }
-
- func (m *CustomerRequest_Address) GetState() string {
- if m != nil {
- return m.State
- }
- return ""
- }
-
- func (m *CustomerRequest_Address) GetZip() string {
- if m != nil {
- return m.Zip
- }
- return ""
- }
-
- func (m *CustomerRequest_Address) GetIsShippingAddress() bool {
- if m != nil {
- return m.IsShippingAddress
- }
- return false
- }
-
- type CustomerResponse struct {
- Id int32 `protobuf:"varint,omitempty"`
- Success bool `protobuf:"varint,name=success" json:"success,omitempty"`
- }
-
- func (m *CustomerResponse) Reset() { *m = CustomerResponse{} }
- func (m *CustomerResponse) String() string { return proto.CompactTextString(m) }
- func (*CustomerResponse) ProtoMessage() {}
- func (*CustomerResponse) Descriptor() ([]byte,[]int{1} }
-
- func (m *CustomerResponse) GetId() int32 {
- if m != nil {
- return m.Id
- }
- return 0
- }
-
- func (m *CustomerResponse) GetSuccess() bool {
- if m != nil {
- return m.Success
- }
- return false
- }
-
- type CustomerFilter struct {
- Keyword string `protobuf:"bytes,name=keyword" json:"keyword,omitempty"`
- }
-
- func (m *CustomerFilter) Reset() { *m = CustomerFilter{} }
- func (m *CustomerFilter) String() string { return proto.CompactTextString(m) }
- func (*CustomerFilter) ProtoMessage() {}
- func (*CustomerFilter) Descriptor() ([]byte,[]int{2} }
-
- func (m *CustomerFilter) GetKeyword() string {
- if m != nil {
- return m.Keyword
- }
- return ""
- }
-
- func init() {
- proto.RegisterType((*CustomerRequest)(nil),"customer.CustomerRequest")
- proto.RegisterType((*CustomerRequest_Address)(nil),"customer.CustomerRequest.Address")
- proto.RegisterType((*CustomerResponse)(nil),"customer.CustomerResponse")
- proto.RegisterType((*CustomerFilter)(nil),"customer.CustomerFilter")
- }
-
- // Reference imports to suppress errors if they are not otherwise used.
- var _ context.Context
- var _ grpc.ClientConn
-
- // This is a compile-time assertion to ensure that this generated file
- // is compatible with the grpc package it is being compiled against.
- const _ = grpc.SupportPackageIsVersion4
-
- // Client API for Customer service
-
- type CustomerClient interface {
- // Get all Customers with filter - A server-to-client streaming RPC.
- GetCustomers(ctx context.Context,in *CustomerFilter,opts ...grpc.CallOption) (Customer_GetCustomersClient,error)
- // Create a new Customer - A simple RPC
- CreateCustomer(ctx context.Context,in *CustomerRequest,opts ...grpc.CallOption) (*CustomerResponse,error)
- }
-
- type customerClient struct {
- cc *grpc.ClientConn
- }
-
- func NewCustomerClient(cc *grpc.ClientConn) CustomerClient {
- return &customerClient{cc}
- }
-
- func (c *customerClient) GetCustomers(ctx context.Context,error) {
- stream,err := grpc.NewClientStream(ctx,&_Customer_serviceDesc.Streams[0],c.cc,"/customer.Customer/GetCustomers",opts...)
- if err != nil {
- return nil,err
- }
- x := &customerGetCustomersClient{stream}
- if err := x.ClientStream.SendMsg(in); err != nil {
- return nil,err
- }
- if err := x.ClientStream.CloseSend(); err != nil {
- return nil,err
- }
- return x,nil
- }
-
- type Customer_GetCustomersClient interface {
- Recv() (*CustomerRequest,error)
- grpc.ClientStream
- }
-
- type customerGetCustomersClient struct {
- grpc.ClientStream
- }
-
- func (x *customerGetCustomersClient) Recv() (*CustomerRequest,error) {
- m := new(CustomerRequest)
- if err := x.ClientStream.RecvMsg(m); err != nil {
- return nil,err
- }
- return m,nil
- }
-
- func (c *customerClient) CreateCustomer(ctx context.Context,error) {
- out := new(CustomerResponse)
- err := grpc.Invoke(ctx,"/customer.Customer/CreateCustomer",in,err
- }
- return out,nil
- }
-
- // Server API for Customer service
-
- type CustomerServer interface {
- // Get all Customers with filter - A server-to-client streaming RPC.
- GetCustomers(*CustomerFilter,Customer_GetCustomeRSServer) error
- // Create a new Customer - A simple RPC
- CreateCustomer(context.Context,*CustomerRequest) (*CustomerResponse,error)
- }
-
- func RegisterCustomerServer(s *grpc.Server,srv CustomerServer) {
- s.RegisterService(&_Customer_serviceDesc,srv)
- }
-
- func _Customer_GetCustomers_Handler(srv interface{},stream grpc.ServerStream) error {
- m := new(CustomerFilter)
- if err := stream.RecvMsg(m); err != nil {
- return err
- }
- return srv.(CustomerServer).GetCustomers(m,&customerGetCustomeRSServer{stream})
- }
-
- type Customer_GetCustomeRSServer interface {
- Send(*CustomerRequest) error
- grpc.ServerStream
- }
-
- type customerGetCustomeRSServer struct {
- grpc.ServerStream
- }
-
- func (x *customerGetCustomeRSServer) Send(m *CustomerRequest) error {
- return x.ServerStream.SendMsg(m)
- }
-
- func _Customer_CreateCustomer_Handler(srv interface{},ctx context.Context,dec func(interface{}) error,interceptor grpc.UnaryServerInterceptor) (interface{},error) {
- in := new(CustomerRequest)
- if err := dec(in); err != nil {
- return nil,err
- }
- if interceptor == nil {
- return srv.(CustomerServer).CreateCustomer(ctx,in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,FullMethod: "/customer.Customer/CreateCustomer",}
- handler := func(ctx context.Context,req interface{}) (interface{},error) {
- return srv.(CustomerServer).CreateCustomer(ctx,req.(*CustomerRequest))
- }
- return interceptor(ctx,info,handler)
- }
-
- var _Customer_serviceDesc = grpc.ServiceDesc{
- ServiceName: "customer.Customer",HandlerType: (*CustomerServer)(nil),Methods: []grpc.MethodDesc{
- {
- MethodName: "CreateCustomer",Handler: _Customer_CreateCustomer_Handler,},Streams: []grpc.StreamDesc{
- {
- StreamName: "GetCustomers",Handler: _Customer_GetCustomers_Handler,ServerStreams: true,Metadata: "customer.proto",}
-
- func init() { proto.RegisterFile("customer.proto",fileDescriptor0) }
-
- var fileDescriptor0 = []byte{
- // 326 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x02, 0xff, 0x74, 0x92, 0xef, 0x4a, 0xc3, 0x30, 0x10, 0xc0, 0x97, 0x6e, 0xdd, 0x9f, 0x53, 0xea, 0x0c, 0x22, 0xb1, 0x6a, 0x3f, 0x15, 0x91, 0x21, 0xf3, 0xab, 0x20, 0x32, 0x70, 0xf8, 0xb5, 0x3e, 0x41, 0x6d, 0x0f, 0x17, 0xdc, 0xda, 0x9a, 0xcb, 0x90, 0xf9, 0x0a, 0xbe, 0x83, 0xcf, 0xe0, 0x23, 0xd2, 0x66, 0x03, 0xe7, 0x72, 0x77, 0xe5, 0x04, 0x42, 0x35, 0xa9, 0x55, 0xa5, 0x2b, 0x1c, 0x78, 0x6b, 0xc5, 0xf7, 0xe6, 0x01, 0xb2, 0x2c, 0x62, 0x89, 0x7a, 0x65, 0xb6, 0xe1, 0x45, 0x19, 0x36, 0x67, 0xe3, 0x2a, 0x93, 0x4b, 0xd1, 0xc9, 0x06, 0x4c, 0x5e, 0x54, 0x25, 0x8a, 0x61, 0x94, 0x85, 0x24, 0x47, 0xe4, 0x68, 0x39, 0xd9, 0x1a, 0xfd, 0xb9, 0x7d, 0xf2, 0xd0, 0xa6, 0xbb, 0x9e, 0xf0, 0xc1, 0xa0, 0x4d, 0x73, 0xe8, 0x56, 0x88, 0x8e, 0x96, 0x8c, 0x64, 0x2e, 0xf5, 0xc6, 0x49, 0xd8, 0xce, 0x34, 0x3a, 0x0b, 0x7c, 0x4f, 0x59, 0xb7, 0x26, 0xd7, 0xe9, 0x79, 0xeb, 0x5a, 0xaf, 0xed, 0xc2, 0x8f, 0x58, 0xe2, 0x3b, 0x18, 0x9c, 0xae, 0xbd, 0x95, 0x09, 0xcd, 0x71, 0x5f, 0xba, 0x1f, 0x52, 0xa3, 0x6f, 0xb8, 0xa8, 0xf4, 0x9b, 0x51, 0xf6, 0x57, 0x2f, 0x0e, 0x37, 0xee, 0xfe, 0xfa, 0x43, 0xd4, 0x3c, 0xbc, 0x0d, 0xde, 0xd3, 0x60,}
server/main.go
- package main
-
- import (
- "log"
- "net"
- "strings"
-
- "golang.org/x/net/context"
- "google.golang.org/grpc"
-
- pb "go_grpc_protobuf/customer"
- )
-
- const (
- port = ":50051"
- )
-
- // server is used to implement customer.CustomerServer.
- type server struct {
- savedCustomers []*pb.CustomerRequest
- }
-
- // CreateCustomer creates a new Customer
- func (s *server) CreateCustomer(ctx context.Context,in *pb.CustomerRequest) (*pb.CustomerResponse,error) {
- s.savedCustomers = append(s.savedCustomers,in)
- return &pb.CustomerResponse{Id: in.Id,Success: true},nil
- }
-
- // GetCustomers returns all customers by given filter
- func (s *server) GetCustomers(filter *pb.CustomerFilter,stream pb.Customer_GetCustomeRSServer) error {
- for _,customer := range s.savedCustomers {
- if filter.Keyword != "" {
- if !strings.Contains(customer.Name,filter.Keyword) {
- continue
- }
- }
- if err := stream.Send(customer); err != nil {
- return err
- }
- }
- return nil
- }
-
- func main() {
- lis,err := net.Listen("tcp",port)
- if err != nil {
- log.Fatalf("Failed to listen: %v",err)
- }
- // Creates a new gRPC server
- s := grpc.NewServer()
- pb.RegisterCustomerServer(s,&server{})
- s.Serve(lis)
- }
client/main.go
- package main
-
- import (
- "io"
- "log"
-
- "golang.org/x/net/context"
- "google.golang.org/grpc"
-
- pb "go_grpc_protobuf/customer"
- )
-
- const (
- address = "localhost:50051"
- )
-
- // createCustomer calls the RPC method CreateCustomer of CustomerServer
- func createCustomer(client pb.CustomerClient,customer *pb.CustomerRequest) {
- resp,err := client.CreateCustomer(context.Background(),customer)
- if err != nil {
- log.Fatalf("Could not create Customer: %v",err)
- }
- if resp.Success {
- log.Printf("A new Customer has been added with id: %d",resp.Id)
- }
- }
-
- // getCustomers calls the RPC method GetCustomers of CustomerServer
- func getCustomers(client pb.CustomerClient,filter *pb.CustomerFilter) {
- // calling the streaming API
- stream,err := client.GetCustomers(context.Background(),filter)
- if err != nil {
- log.Fatalf("Error on get customers: %v",err)
- }
- for {
- customer,err := stream.Recv()
- if err == io.EOF {
- break
- }
- if err != nil {
- log.Fatalf("%v.GetCustomers(_) = _,%v",client,err)
- }
- log.Printf("Customer: %v",customer)
- }
- }
- func main() {
- // Set up a connection to the gRPC server.
- conn,err := grpc.Dial(address,grpc.WithInsecure())
- if err != nil {
- log.Fatalf("did not connect: %v",err)
- }
- defer conn.Close()
- // Creates a new CustomerClient
- client := pb.NewCustomerClient(conn)
-
- customer := &pb.CustomerRequest{
- Id: 101,Name: "Shiju Varghese",Email: "shiju@xyz.com",Phone: "732-757-2923",Addresses: []*pb.CustomerRequest_Address{
- &pb.CustomerRequest_Address{
- Street: "1 Mission Street",City: "San Francisco",State: "CA",Zip: "94105",IsShippingAddress: false,&pb.CustomerRequest_Address{
- Street: "Greenfield",City: "Kochi",State: "KL",Zip: "68356",IsShippingAddress: true,}
-
- // Create a new customer
- createCustomer(client,customer)
-
- customer = &pb.CustomerRequest{
- Id: 102,Name: "Irene Rose",Email: "irene@xyz.com",Phone: "732-757-2924",customer)
- // Filter with an empty Keyword
- filter := &pb.CustomerFilter{Keyword: ""}
- getCustomers(client,filter)
- }
运行server,运行client,输出:
- 2017/12/07 11:55:59 A new Customer has been added with id: 101
- 2017/12/07 11:55:59 A new Customer has been added with id: 102
- 2017/12/07 11:55:59 Customer: id:101 name:"Shiju Varghese" email:"shiju@xyz.com" phone:"732-757-2923" addresses:<street:"1 Mission Street" city:"San Francisco" state:"CA" zip:"94105" > addresses:<street:"Greenfield" city:"Kochi" state:"KL" zip:"68356" isShippingAddress:true >
- 2017/12/07 11:55:59 Customer: id:102 name:"Irene Rose" email:"irene@xyz.com" phone:"732-757-2924" addresses:<street:"1 Mission Street" city:"San Francisco" state:"CA" zip:"94105" isShippingAddress:true >