golang與gRpc

gRpc

1. gRpc 概述

gRpc是什麼 ?

gRPC是Google開源的可以在任何環境中運行的現代開源高性能RPC框架。它可以通過可插拔的支持來有效地連接數據中心內和跨數據中心的服務,以實現負載平衡,跟蹤,健康檢查和身份驗證。它也適用於分佈式計算的最後一英里,以將設備,移動應用程序和瀏覽器連接到後端服務。

主要使用場景

  1. 在微服務風格架構中有效連接多語種服務
  2. 將移動設備,瀏覽器客戶端連接到後端服務
  3. 生成高效的客戶端庫

gRpc官方地址

https://grpc.io/

gRpc源碼託管地址

https://github.com/grpc/grpc

gRpc支持我們常見的編程語言(C++ java Python Go Ruby C# Node.js PHP Dart Objective-C ) ,這些編程語言基本都有對gRpc的實現

詳情可以參看 https://grpc.io/docs/

2. gRpc執行概述

在 gRPC 裏客戶端應用可以像調用本地對象一樣直接調用另一臺不同的機器上服務端應用的方法,使得您能夠更容易地創建分佈式應用和服務。與許多 RPC 系統類似,gRPC 也是基於以下理念:定義一個服務,指定其能夠被遠程調用的方法(包含參數和返回類型)。在服務端實現這個接口,並運行一個 gRPC 服務器來處理客戶端調用。在客戶端擁有一個存根能夠像服務端一樣的方法。

grpc

通過任意編程語言創建的gRpc服務端和客戶端可以運行在多種環境中,創建的gRpc服務端,可以通過任意編程語言編寫的客戶端調用

gRpc默認使用protocol buffers 對數據進行序列化

關於Protobuf的詳情,請參看 https://developers.google.com/protocol-buffers

3. gRpc-go 安裝

gRpc 有多種語言的實現

  • C++: follow the instructions under the src/cpp directory
  • C#: NuGet package Grpc
  • Dart: pub package grpc
  • Go: go get google.golang.org/grpc
  • Java: Use JARs from Maven Central Repository
  • Node: npm install grpc
  • Objective-C: Add gRPC-ProtoRPC dependency to podspec
  • PHP: pecl install grpc
  • Python: pip install grpcio
  • Ruby: gem install grpc
  • WebJS: follow the grpc-web instructions
Language Source
Shared C [core library] src/core
C++ src/cpp
Ruby src/ruby
Python src/python
PHP src/php
C# (core library based) src/csharp
Objective-C src/objective-c
Java grpc-java
Go grpc-go
NodeJS grpc-node
WebJS grpc-web
Dart grpc-dart
.NET (pure C# impl.) grpc-dotnet

grpc- go 是gRpc庫的Golang 實現版本,也是我們需要安裝的版本(根據自己的開發語言選擇安裝)

安裝細節

進入GOPATH目錄下執行如下命令

go get -u google.golang.org/grpc

不出意外的話會安裝失敗,報錯信息如下:

package google.golang.org/grpc: unrecognized import path "google.golang.org/grpc" (https fetch: Get https://google.golang.org/grpc?go-get=1: dial tcp 216.239.37.1:443: i/o timeout)

這怎麼辦呢?來來換個姿勢繼續操作

下載 grpc-go 的源碼包和依賴包

git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc
git clone https://github.com/golang/net.git $GOPATH/src/golang.org/x/net
git clone https://github.com/golang/text.git $GOPATH/src/golang.org/x/text
git clone https://github.com/google/go-genproto.git $GOPATH/src/google.golang.org/genproto

下載完成之後 ,執行安裝命令

cd $GOPATH/src
go install google.golang.org/grpc

因爲grpc默認使用Protocol buffers

所以必須安裝 Protocol Buffers 編譯器,安裝步驟省略

$ protoc --version
libprotoc 3.11.0

我們開發語言是Golang,所以也需要安裝protobuf文件的Go語言代碼生成插件

go get github.com/golang/protobuf/protoc-gen-go 

4. gRpc使用

上面的基礎環境具備之後,我們就可以快樂的使用gRpc開發服務了

我們梳理一下gRpc框架使用的最基礎的流程:

  1. 編寫Protobuf 文件,在Protobuf文件中定義服務和接口

  2. 自動生成Go語言代碼.

    這裏主要是指安裝的protoc-gen-go 插件然後指定參數生成兼容gRpc框架的Go語言代碼

  3. 服務接口的實現

  4. gRpc服務端實現

  5. gRpc客戶端實現

在gRpc安裝完成之後會有如下的一個目錄存在

$GOPATH/src/google.golang.org/grpc/examples

gRpc的很多功能的使用示例都在其中,我們學習gRpc可以多看看這個目錄中的文件

step1 : 編寫一個.proto 文件,文件名自己擬定

例如: demo5.proto

syntax = "proto3";
package example;
// 添加服務
service Demo5 {
    // 定義服務接口
    rpc GetInfo (Demo5Request) returns (Demo5Response) {
    }
    rpc SetName (Demo5Request) returns (Demo5Response) {
    }
}
message Demo5Request {
    string name = 1;
    int32 age = 2;
    enum Gender {
        MALE = 0;
        FEMALE = 1;
    }
    message Other {
        string addr = 1;
        string hobby = 2;
        Gender g = 3;
    }
    Other info = 3;
}
message Demo5Response {
    string info = 1;
}

step2 : 自動生成Go語言代碼

執行完下面的命令,將生成代碼文件 demo5.pb.go

 protoc --go_out=plugins=grpc:. example/demo5.proto

demo5.pb.go 具體內容如下(在開發中不太會去關注其中的內容)

// Code generated by protoc-gen-go. DO NOT EDIT.
// source: example/demo5.proto

package example

import (
	context "context"
	fmt "fmt"
	proto "github.com/golang/protobuf/proto"
	grpc "google.golang.org/grpc"
	codes "google.golang.org/grpc/codes"
	status "google.golang.org/grpc/status"
	math "math"
)

// 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.ProtoPackageIsVersion3 // please upgrade the proto package

type Demo5Request_Gender int32

const (
	Demo5Request_MALE   Demo5Request_Gender = 0
	Demo5Request_FEMALE Demo5Request_Gender = 1
)

var Demo5Request_Gender_name = map[int32]string{
	0: "MALE",
	1: "FEMALE",
}

var Demo5Request_Gender_value = map[string]int32{
	"MALE":   0,
	"FEMALE": 1,
}

func (x Demo5Request_Gender) String() string {
	return proto.EnumName(Demo5Request_Gender_name, int32(x))
}

func (Demo5Request_Gender) EnumDescriptor() ([]byte, []int) {
	return fileDescriptor_dc43dfb84d83bd6d, []int{0, 0}
}

type Demo5Request struct {
	Name                 string              `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
	Age                  int32               `protobuf:"varint,2,opt,name=age,proto3" json:"age,omitempty"`
	Info                 *Demo5Request_Other `protobuf:"bytes,3,opt,name=info,proto3" json:"info,omitempty"`
	XXX_NoUnkeyedLiteral struct{}            `json:"-"`
	XXX_unrecognized     []byte              `json:"-"`
	XXX_sizecache        int32               `json:"-"`
}

func (m *Demo5Request) Reset()         { *m = Demo5Request{} }
func (m *Demo5Request) String() string { return proto.CompactTextString(m) }
func (*Demo5Request) ProtoMessage()    {}
func (*Demo5Request) Descriptor() ([]byte, []int) {
	return fileDescriptor_dc43dfb84d83bd6d, []int{0}
}

func (m *Demo5Request) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_Demo5Request.Unmarshal(m, b)
}
func (m *Demo5Request) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_Demo5Request.Marshal(b, m, deterministic)
}
func (m *Demo5Request) XXX_Merge(src proto.Message) {
	xxx_messageInfo_Demo5Request.Merge(m, src)
}
func (m *Demo5Request) XXX_Size() int {
	return xxx_messageInfo_Demo5Request.Size(m)
}
func (m *Demo5Request) XXX_DiscardUnknown() {
	xxx_messageInfo_Demo5Request.DiscardUnknown(m)
}

var xxx_messageInfo_Demo5Request proto.InternalMessageInfo

func (m *Demo5Request) GetName() string {
	if m != nil {
		return m.Name
	}
	return ""
}

func (m *Demo5Request) GetAge() int32 {
	if m != nil {
		return m.Age
	}
	return 0
}

func (m *Demo5Request) GetInfo() *Demo5Request_Other {
	if m != nil {
		return m.Info
	}
	return nil
}

type Demo5Request_Other struct {
	Addr                 string              `protobuf:"bytes,1,opt,name=addr,proto3" json:"addr,omitempty"`
	Hobby                string              `protobuf:"bytes,2,opt,name=hobby,proto3" json:"hobby,omitempty"`
	G                    Demo5Request_Gender `protobuf:"varint,3,opt,name=g,proto3,enum=example.Demo5Request_Gender" json:"g,omitempty"`
	XXX_NoUnkeyedLiteral struct{}            `json:"-"`
	XXX_unrecognized     []byte              `json:"-"`
	XXX_sizecache        int32               `json:"-"`
}

func (m *Demo5Request_Other) Reset()         { *m = Demo5Request_Other{} }
func (m *Demo5Request_Other) String() string { return proto.CompactTextString(m) }
func (*Demo5Request_Other) ProtoMessage()    {}
func (*Demo5Request_Other) Descriptor() ([]byte, []int) {
	return fileDescriptor_dc43dfb84d83bd6d, []int{0, 0}
}

func (m *Demo5Request_Other) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_Demo5Request_Other.Unmarshal(m, b)
}
func (m *Demo5Request_Other) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_Demo5Request_Other.Marshal(b, m, deterministic)
}
func (m *Demo5Request_Other) XXX_Merge(src proto.Message) {
	xxx_messageInfo_Demo5Request_Other.Merge(m, src)
}
func (m *Demo5Request_Other) XXX_Size() int {
	return xxx_messageInfo_Demo5Request_Other.Size(m)
}
func (m *Demo5Request_Other) XXX_DiscardUnknown() {
	xxx_messageInfo_Demo5Request_Other.DiscardUnknown(m)
}

var xxx_messageInfo_Demo5Request_Other proto.InternalMessageInfo

func (m *Demo5Request_Other) GetAddr() string {
	if m != nil {
		return m.Addr
	}
	return ""
}

func (m *Demo5Request_Other) GetHobby() string {
	if m != nil {
		return m.Hobby
	}
	return ""
}

func (m *Demo5Request_Other) GetG() Demo5Request_Gender {
	if m != nil {
		return m.G
	}
	return Demo5Request_MALE
}

type Demo5Response struct {
	Info                 string   `protobuf:"bytes,1,opt,name=info,proto3" json:"info,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

func (m *Demo5Response) Reset()         { *m = Demo5Response{} }
func (m *Demo5Response) String() string { return proto.CompactTextString(m) }
func (*Demo5Response) ProtoMessage()    {}
func (*Demo5Response) Descriptor() ([]byte, []int) {
	return fileDescriptor_dc43dfb84d83bd6d, []int{1}
}

func (m *Demo5Response) XXX_Unmarshal(b []byte) error {
	return xxx_messageInfo_Demo5Response.Unmarshal(m, b)
}
func (m *Demo5Response) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
	return xxx_messageInfo_Demo5Response.Marshal(b, m, deterministic)
}
func (m *Demo5Response) XXX_Merge(src proto.Message) {
	xxx_messageInfo_Demo5Response.Merge(m, src)
}
func (m *Demo5Response) XXX_Size() int {
	return xxx_messageInfo_Demo5Response.Size(m)
}
func (m *Demo5Response) XXX_DiscardUnknown() {
	xxx_messageInfo_Demo5Response.DiscardUnknown(m)
}

var xxx_messageInfo_Demo5Response proto.InternalMessageInfo

func (m *Demo5Response) GetInfo() string {
	if m != nil {
		return m.Info
	}
	return ""
}

func init() {
	proto.RegisterEnum("example.Demo5Request_Gender", Demo5Request_Gender_name, Demo5Request_Gender_value)
	proto.RegisterType((*Demo5Request)(nil), "example.Demo5Request")
	proto.RegisterType((*Demo5Request_Other)(nil), "example.Demo5Request.Other")
	proto.RegisterType((*Demo5Response)(nil), "example.Demo5Response")
}

func init() { proto.RegisterFile("example/demo5.proto", fileDescriptor_dc43dfb84d83bd6d) }

var fileDescriptor_dc43dfb84d83bd6d = []byte{
	// 271 bytes of a gzipped FileDescriptorProto
	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4e, 0xad, 0x48, 0xcc,
	0x2d, 0xc8, 0x49, 0xd5, 0x4f, 0x49, 0xcd, 0xcd, 0x37, 0xd5, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17,
	0x62, 0x87, 0x0a, 0x2a, 0x3d, 0x61, 0xe4, 0xe2, 0x71, 0x01, 0x49, 0x04, 0xa5, 0x16, 0x96, 0xa6,
	0x16, 0x97, 0x08, 0x09, 0x71, 0xb1, 0xe4, 0x25, 0xe6, 0xa6, 0x4a, 0x30, 0x2a, 0x30, 0x6a, 0x70,
	0x06, 0x81, 0xd9, 0x42, 0x02, 0x5c, 0xcc, 0x89, 0xe9, 0xa9, 0x12, 0x4c, 0x0a, 0x8c, 0x1a, 0xac,
	0x41, 0x20, 0xa6, 0x90, 0x3e, 0x17, 0x4b, 0x66, 0x5e, 0x5a, 0xbe, 0x04, 0xb3, 0x02, 0xa3, 0x06,
	0xb7, 0x91, 0xb4, 0x1e, 0xd4, 0x38, 0x3d, 0x64, 0xa3, 0xf4, 0xfc, 0x4b, 0x32, 0x52, 0x8b, 0x82,
	0xc0, 0x0a, 0xa5, 0x62, 0xb9, 0x58, 0xc1, 0x5c, 0x90, 0xf9, 0x89, 0x29, 0x29, 0x45, 0x30, 0xf3,
	0x41, 0x6c, 0x21, 0x11, 0x2e, 0xd6, 0x8c, 0xfc, 0xa4, 0xa4, 0x4a, 0xb0, 0x0d, 0x9c, 0x41, 0x10,
	0x8e, 0x90, 0x16, 0x17, 0x63, 0x3a, 0xd8, 0x02, 0x3e, 0x23, 0x19, 0xec, 0x16, 0xb8, 0xa7, 0xe6,
	0xa5, 0xa4, 0x16, 0x05, 0x31, 0xa6, 0x2b, 0xc9, 0x71, 0xb1, 0x41, 0x38, 0x42, 0x1c, 0x5c, 0x2c,
	0xbe, 0x8e, 0x3e, 0xae, 0x02, 0x0c, 0x42, 0x5c, 0x5c, 0x6c, 0x6e, 0xae, 0x60, 0x36, 0xa3, 0x92,
	0x32, 0x17, 0x2f, 0x54, 0x67, 0x71, 0x41, 0x7e, 0x5e, 0x71, 0x2a, 0xc8, 0x19, 0x60, 0x0f, 0x40,
	0x9d, 0x01, 0x62, 0x1b, 0xd5, 0x73, 0xb1, 0x82, 0x15, 0x09, 0x59, 0x71, 0xb1, 0xbb, 0xa7, 0x96,
	0x78, 0xe6, 0xa5, 0xe5, 0x0b, 0x89, 0x62, 0xb5, 0x59, 0x4a, 0x0c, 0x5d, 0x18, 0x62, 0xac, 0x12,
	0x03, 0x48, 0x6f, 0x70, 0x6a, 0x89, 0x1f, 0x28, 0xd8, 0x48, 0xd5, 0x9b, 0xc4, 0x06, 0x8e, 0x1c,
	0x63, 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x16, 0x42, 0x94, 0xe0, 0xb3, 0x01, 0x00, 0x00,
}

// 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

// Demo5Client is the client API for Demo5 service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
type Demo5Client interface {
	GetInfo(ctx context.Context, in *Demo5Request, opts ...grpc.CallOption) (*Demo5Response, error)
	SetName(ctx context.Context, in *Demo5Request, opts ...grpc.CallOption) (*Demo5Response, error)
}

type demo5Client struct {
	cc *grpc.ClientConn
}

func NewDemo5Client(cc *grpc.ClientConn) Demo5Client {
	return &demo5Client{cc}
}

func (c *demo5Client) GetInfo(ctx context.Context, in *Demo5Request, opts ...grpc.CallOption) (*Demo5Response, error) {
	out := new(Demo5Response)
	err := c.cc.Invoke(ctx, "/example.Demo5/GetInfo", in, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

func (c *demo5Client) SetName(ctx context.Context, in *Demo5Request, opts ...grpc.CallOption) (*Demo5Response, error) {
	out := new(Demo5Response)
	err := c.cc.Invoke(ctx, "/example.Demo5/SetName", in, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}

// Demo5Server is the server API for Demo5 service.
type Demo5Server interface {
	GetInfo(context.Context, *Demo5Request) (*Demo5Response, error)
	SetName(context.Context, *Demo5Request) (*Demo5Response, error)
}

// UnimplementedDemo5Server can be embedded to have forward compatible implementations.
type UnimplementedDemo5Server struct {
}

func (*UnimplementedDemo5Server) GetInfo(ctx context.Context, req *Demo5Request) (*Demo5Response, error) {
	return nil, status.Errorf(codes.Unimplemented, "method GetInfo not implemented")
}
func (*UnimplementedDemo5Server) SetName(ctx context.Context, req *Demo5Request) (*Demo5Response, error) {
	return nil, status.Errorf(codes.Unimplemented, "method SetName not implemented")
}

func RegisterDemo5Server(s *grpc.Server, srv Demo5Server) {
	s.RegisterService(&_Demo5_serviceDesc, srv)
}

func _Demo5_GetInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
	in := new(Demo5Request)
	if err := dec(in); err != nil {
		return nil, err
	}
	if interceptor == nil {
		return srv.(Demo5Server).GetInfo(ctx, in)
	}
	info := &grpc.UnaryServerInfo{
		Server:     srv,
		FullMethod: "/example.Demo5/GetInfo",
	}
	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
		return srv.(Demo5Server).GetInfo(ctx, req.(*Demo5Request))
	}
	return interceptor(ctx, in, info, handler)
}

func _Demo5_SetName_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
	in := new(Demo5Request)
	if err := dec(in); err != nil {
		return nil, err
	}
	if interceptor == nil {
		return srv.(Demo5Server).SetName(ctx, in)
	}
	info := &grpc.UnaryServerInfo{
		Server:     srv,
		FullMethod: "/example.Demo5/SetName",
	}
	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
		return srv.(Demo5Server).SetName(ctx, req.(*Demo5Request))
	}
	return interceptor(ctx, in, info, handler)
}

var _Demo5_serviceDesc = grpc.ServiceDesc{
	ServiceName: "example.Demo5",
	HandlerType: (*Demo5Server)(nil),
	Methods: []grpc.MethodDesc{
		{
			MethodName: "GetInfo",
			Handler:    _Demo5_GetInfo_Handler,
		},
		{
			MethodName: "SetName",
			Handler:    _Demo5_SetName_Handler,
		},
	},
	Streams:  []grpc.StreamDesc{},
	Metadata: "example/demo5.proto",
}

step3 : gRpc服務端實現

具體服務的實現代碼可以單獨放在一個文件中,此處我們都放在了gRpc服務端代碼文件中

文件名: grpc_demo5_server.go

package main

import (
	pb "GoNote/chapter10/demo9/example"
	"fmt"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"log"
	"net"
)

// 實現服務
type Demo5Server struct {
	pb.UnimplementedDemo5Server
}
// 實現服務定義的接口
func (d *Demo5Server) GetInfo(ctx context.Context, in *pb.Demo5Request) (*pb.Demo5Response, error) {
	InfoS := fmt.Sprintf("my name is %s, i am %d, i live in %s", in.Name, in.Age, in.Info.Addr)
	return &pb.Demo5Response{Info: InfoS}, nil
}
func (d *Demo5Server) SetName(ctx context.Context, in *pb.Demo5Request) (*pb.Demo5Response, error) {
	return &pb.Demo5Response{Info: "getName"}, nil
}
func main() {
	// 監聽8080端口
	listen, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatal("failed to listen : ", err.Error())
	}
	// 創建一個沒有註冊服務的新的gRpc服務器
	s := grpc.NewServer()
	// 註冊服務
	pb.RegisterDemo5Server(s, &Demo5Server{})
	fmt.Println("gRpc 服務端開啓")
	// 接收gRpc請求
	if err := s.Serve(listen); err != nil {
		log.Fatal("this is error : ", err.Error())
	}
}

step4 : gRpc客戶端實現

文件名: grpc_demo5_client.go

package main

import (
	pb "GoNote/chapter10/demo9/example"
	"fmt"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"log"
	"time"
)

func main() {
	// 創建一個客戶端連接
	conn, err := grpc.Dial(":8080", grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		log.Fatal("connect failed : ", err.Error())
	}
	// 關閉客戶端連接
	defer conn.Close()
	// 客戶端服務api
	client := pb.NewDemo5Client(conn)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second*1)
	defer cancel()
	// 客戶端請求數據
	req := pb.Demo5Request{
		Name: "Tom",
		Age:  99,
		Info: &pb.Demo5Request_Other{
			Addr:  "Beijing",
			Hobby: "climbing",
			G:     0,
		},
	}
	// 調用服務接口
	r, err := client.GetInfo(ctx, &req)
	if err != nil {
		log.Fatal("called failed : ", err.Error())
	}
	// 打印結果
	fmt.Println(r.Info)
}

測試

$ go run grpc_demo5_server.go
gRpc 服務端開啓
$ go run grpc_demo5_client.go
my name is Tom, i am 99, i live in Beijing
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章