1. gRpc 概述
gRpc是什么 ?
gRPC是Google開(kāi)源的可以在任何環(huán)境中運(yùn)行的現(xiàn)代開(kāi)源高性能RPC框架。它可以通過(guò)可插拔的支持來(lái)有效地連接數(shù)據(jù)中心內(nèi)和跨數(shù)據(jù)中心的服務(wù)觉痛,以實(shí)現(xiàn)負(fù)載平衡态秧,跟蹤昂验,健康檢查和身份驗(yàn)證缘滥。它也適用于分布式計(jì)算的最后一英里券躁,以將設(shè)備惩坑,移動(dòng)應(yīng)用程序和瀏覽器連接到后端服務(wù)。
主要使用場(chǎng)景
- 在微服務(wù)風(fēng)格架構(gòu)中有效連接多語(yǔ)種服務(wù)
- 將移動(dòng)設(shè)備也拜,瀏覽器客戶(hù)端連接到后端服務(wù)
- 生成高效的客戶(hù)端庫(kù)
gRpc官方地址
gRpc源碼托管地址
gRpc支持我們常見(jiàn)的編程語(yǔ)言(
C++
java
Python
Go
Ruby
C#
Node.js
PHP
Dart
Objective-C
) ,這些編程語(yǔ)言基本都有對(duì)gRpc的實(shí)現(xiàn)詳情可以參看 https://grpc.io/docs/
2. gRpc執(zhí)行概述
在 gRPC 里客戶(hù)端應(yīng)用可以像調(diào)用本地對(duì)象一樣直接調(diào)用另一臺(tái)不同的機(jī)器上服務(wù)端應(yīng)用的方法以舒,使得您能夠更容易地創(chuàng)建分布式應(yīng)用和服務(wù)。與許多 RPC 系統(tǒng)類(lèi)似慢哈,gRPC 也是基于以下理念:定義一個(gè)服務(wù)蔓钟,指定其能夠被遠(yuǎn)程調(diào)用的方法(包含參數(shù)和返回類(lèi)型)。在服務(wù)端實(shí)現(xiàn)這個(gè)接口卵贱,并運(yùn)行一個(gè) gRPC 服務(wù)器來(lái)處理客戶(hù)端調(diào)用滥沫。在客戶(hù)端擁有一個(gè)存根能夠像服務(wù)端一樣的方法。
通過(guò)任意編程語(yǔ)言創(chuàng)建的gRpc服務(wù)端和客戶(hù)端可以運(yùn)行在多種環(huán)境中,創(chuàng)建的gRpc服務(wù)端,可以通過(guò)任意編程語(yǔ)言編寫(xiě)的客戶(hù)端調(diào)用
gRpc默認(rèn)使用protocol buffers 對(duì)數(shù)據(jù)進(jìn)行序列化
關(guān)于Protobuf的詳情,請(qǐng)參看 https://developers.google.com/protocol-buffers
3. gRpc-go 安裝
gRpc 有多種語(yǔ)言的實(shí)現(xiàn)
- 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庫(kù)的Golang 實(shí)現(xiàn)版本,也是我們需要安裝的版本(根據(jù)自己的開(kāi)發(fā)語(yǔ)言選擇安裝)
安裝細(xì)節(jié)
進(jìn)入GOPATH目錄下執(zhí)行如下命令
go get -u google.golang.org/grpc
不出意外的話會(huì)安裝失敗,報(bào)錯(cuò)信息如下:
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)
這怎么辦呢?來(lái)來(lái)?yè)Q個(gè)姿勢(shì)繼續(xù)操作
下載 grpc-go 的源碼包和依賴(lài)包
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
下載完成之后 ,執(zhí)行安裝命令
cd $GOPATH/src
go install google.golang.org/grpc
因?yàn)間rpc默認(rèn)使用Protocol buffers
所以必須安裝 Protocol Buffers 編譯器,安裝步驟省略
$ protoc --version libprotoc 3.11.0
我們開(kāi)發(fā)語(yǔ)言是Golang,所以也需要安裝protobuf文件的Go語(yǔ)言代碼生成插件
go get github.com/golang/protobuf/protoc-gen-go
4. gRpc使用
上面的基礎(chǔ)環(huán)境具備之后,我們就可以快樂(lè)的使用gRpc開(kāi)發(fā)服務(wù)了
我們梳理一下gRpc框架使用的最基礎(chǔ)的流程:
編寫(xiě)Protobuf 文件,在Protobuf文件中定義服務(wù)和接口
自動(dòng)生成Go語(yǔ)言代碼.
這里主要是指安裝的
protoc-gen-go
插件然后指定參數(shù)生成兼容gRpc框架的Go語(yǔ)言代碼服務(wù)接口的實(shí)現(xiàn)
gRpc服務(wù)端實(shí)現(xiàn)
gRpc客戶(hù)端實(shí)現(xiàn)
在gRpc安裝完成之后會(huì)有如下的一個(gè)目錄存在
$GOPATH/src/google.golang.org/grpc/examples
gRpc的很多功能的使用示例都在其中,我們學(xué)習(xí)gRpc可以多看看這個(gè)目錄中的文件
step1 : 編寫(xiě)一個(gè)
.proto
文件,文件名自己擬定
例如:
demo5.proto
syntax = "proto3";
package example;
// 添加服務(wù)
service Demo5 {
// 定義服務(wù)接口
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 : 自動(dòng)生成Go語(yǔ)言代碼
執(zhí)行完下面的命令,將生成代碼文件 demo5.pb.go
protoc --go_out=plugins=grpc:. example/demo5.proto
demo5.pb.go 具體內(nèi)容如下(在開(kāi)發(fā)中不太會(huì)去關(guān)注其中的內(nèi)容)
// 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服務(wù)端實(shí)現(xiàn)
具體服務(wù)的實(shí)現(xiàn)代碼可以單獨(dú)放在一個(gè)文件中,此處我們都放在了gRpc服務(wù)端代碼文件中
文件名: grpc_demo5_server.go
package main
import (
pb "GoNote/chapter10/demo9/example"
"fmt"
"golang.org/x/net/context"
"google.golang.org/grpc"
"log"
"net"
)
// 實(shí)現(xiàn)服務(wù)
type Demo5Server struct {
pb.UnimplementedDemo5Server
}
// 實(shí)現(xiàn)服務(wù)定義的接口
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() {
// 監(jiān)聽(tīng)8080端口
listen, err := net.Listen("tcp", ":8080")
if err != nil {
log.Fatal("failed to listen : ", err.Error())
}
// 創(chuàng)建一個(gè)沒(méi)有注冊(cè)服務(wù)的新的gRpc服務(wù)器
s := grpc.NewServer()
// 注冊(cè)服務(wù)
pb.RegisterDemo5Server(s, &Demo5Server{})
fmt.Println("gRpc 服務(wù)端開(kāi)啟")
// 接收gRpc請(qǐng)求
if err := s.Serve(listen); err != nil {
log.Fatal("this is error : ", err.Error())
}
}
step4 : gRpc客戶(hù)端實(shí)現(xiàn)
文件名: 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() {
// 創(chuàng)建一個(gè)客戶(hù)端連接
conn, err := grpc.Dial(":8080", grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
log.Fatal("connect failed : ", err.Error())
}
// 關(guān)閉客戶(hù)端連接
defer conn.Close()
// 客戶(hù)端服務(wù)api
client := pb.NewDemo5Client(conn)
ctx, cancel := context.WithTimeout(context.Background(), time.Second*1)
defer cancel()
// 客戶(hù)端請(qǐng)求數(shù)據(jù)
req := pb.Demo5Request{
Name: "Tom",
Age: 99,
Info: &pb.Demo5Request_Other{
Addr: "Beijing",
Hobby: "climbing",
G: 0,
},
}
// 調(diào)用服務(wù)接口
r, err := client.GetInfo(ctx, &req)
if err != nil {
log.Fatal("called failed : ", err.Error())
}
// 打印結(jié)果
fmt.Println(r.Info)
}
測(cè)試
$ go run grpc_demo5_server.go
gRpc 服務(wù)端開(kāi)啟
$ go run grpc_demo5_client.go
my name is Tom, i am 99, i live in Beijing