gRpc
1. gRpc 概述
gRpc是什麼 ?
gRPC是Google開源的可以在任何環境中運行的現代開源高性能RPC框架。它可以通過可插拔的支持來有效地連接數據中心內和跨數據中心的服務,以實現負載平衡,跟蹤,健康檢查和身份驗證。它也適用於分佈式計算的最後一英里,以將設備,移動應用程序和瀏覽器連接到後端服務。
主要使用場景
- 在微服務風格架構中有效連接多語種服務
- 將移動設備,瀏覽器客戶端連接到後端服務
- 生成高效的客戶端庫
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默認使用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框架使用的最基礎的流程:
編寫Protobuf 文件,在Protobuf文件中定義服務和接口
自動生成Go語言代碼.
這裏主要是指安裝的
protoc-gen-go
插件然後指定參數生成兼容gRpc框架的Go語言代碼服務接口的實現
gRpc服務端實現
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