gRPC使用样例(golang)

第一部分:grpc使用样例介绍

使用grpc编写一个服务分如下三步:

  1. 编写一个“.proto”文件,在该“.proto”文件中定义相关服务
  2. 用protoc生成服务端和客户端共享的.pb.go文件
  3. 实现一个简单的grpc客户端和grpc服务器

一:环境准备

由“.proto”文件生成.pb.go文件,依赖protoc编译器、protoc编译器go插件及grpc-go第三方库文件

  1. 安装protoc
    ▪️ https://github.com/protocolbuffers/protobuf/releases,官网下载最新版protocol Buffers压缩包,并解压
    ▪️ 命令cd跳转至解压缩,依次执行“./configure”命令、“make”命令、“make check”命令、“sudo make install”命令
    ▪️ 通过“protoc --version”命令检测是否安装成功
  2. 安装go插件
    ▪️ go get -u github.com/golang/protobuf/protoc-gen-go
  3. grpc-go 第三方库下载
    ▪️ go get -u google.golang.org/grpc

二:样例文件及代码

自定义grpc文件夹,文件目录如下:

--grpc
	--proto
		--hello.proto
		--hello.pb.go
	--grpc_server.go
	--grpc_client.go

1:构建hello.proto文件并生成hello.pb.go文件
hello.proto文件

syntax = "proto3";

package proto;

message Request{
    string message=1;
}

message Reply{
    string message=1;
}

service Hello{
    rpc SayHello(Request)returns (Reply){}
}

grpc/proto文件目录下执行“protoc --go_out=plugins=grpc:. hello.proto “生成hello.pb.go文件。hello.pb.go文件中主要包含以下内容:

  • 服务端(helloServer)相关数据结构及服务函数
  • 客户端(helloClient)相关数据结构及服务函数
  • 客户端和服务端进行数据交互相关的消息初始化、传递、接收的数据结构及函数

注意:此步骤可能出现问题如下:

可能出现的问题:
	问题1: undefined: proto.ProtoPackageIsVersion3
	解决方案(更新protoc go插件至最新版本):
	go get -u github.com/golang/protobuf/protoc-gen-go
	问题2: undefined: grpc.SupportPackageIsVersion6
	解决方案(更新grpc包至最新版本):
	go get -u google.golang.org/grpc

▪️ hello.pb.go文件

// Code generated by protoc-gen-go. DO NOT EDIT.
// source: hello.proto
package proto
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"
)
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf

const _ = proto.ProtoPackageIsVersion3 
/********客户端和服务端交互request相关*********/
type Request struct {
	Message              string   `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

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

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

var xxx_messageInfo_Request proto.InternalMessageInfo

func (m *Request) GetMessage() string {
	if m != nil {
		return m.Message
	}
	return ""
}

/********客户端和服务端交互reply相关*********/
type Reply struct {
	Message              string   `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

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

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

var xxx_messageInfo_Reply proto.InternalMessageInfo

func (m *Reply) GetMessage() string {
	if m != nil {
		return m.Message
	}
	return ""
}

/*省略proto初始化部分*/
/********客户端相关数据结构及服务函数数*********/
type HelloClient interface {
	SayHello(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Reply, error)
}

type helloClient struct {
	cc grpc.ClientConnInterface
}

func NewHelloClient(cc grpc.ClientConnInterface) HelloClient {
	return &helloClient{cc}
}

func (c *helloClient) SayHello(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Reply, error) {
	out := new(Reply)
	err := c.cc.Invoke(ctx, "/proto.Hello/SayHello", in, out, opts...)
	if err != nil {
		return nil, err
	}
	return out, nil
}
/********服务端相关数据结构及服务函数*********/
type HelloServer interface {
	SayHello(context.Context, *Request) (*Reply, error)
}
// UnimplementedHelloServer can be embedded to have forward compatible implementations.
type UnimplementedHelloServer struct {
}

func (*UnimplementedHelloServer) SayHello(ctx context.Context, req *Request) (*Reply, error) {
	return nil, status.Errorf(codes.Unimplemented, "method SayHello not implemented")
}

func RegisterHelloServer(s *grpc.Server, srv HelloServer) {
	s.RegisterService(&_Hello_serviceDesc, srv)
}
/********客户端和服务端交互handler处理函数*********/
func _Hello_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
	in := new(Request)
	if err := dec(in); err != nil {
		return nil, err
	}
	if interceptor == nil {
		return srv.(HelloServer).SayHello(ctx, in)
	}
	info := &grpc.UnaryServerInfo{
		Server:     srv,
		FullMethod: "/proto.Hello/SayHello",
	}
	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
		return srv.(HelloServer).SayHello(ctx, req.(*Request))
	}
	return interceptor(ctx, in, info, handler)
}
/********客户端和服务端交互函数声明定义*********/
var _Hello_serviceDesc = grpc.ServiceDesc{
	ServiceName: "proto.Hello",
	HandlerType: (*HelloServer)(nil),
	Methods: []grpc.MethodDesc{
		{
			MethodName: "SayHello",
			Handler:    _Hello_SayHello_Handler,
		},
	},
	Streams:  []grpc.StreamDesc{},
	Metadata: "hello.proto",
}

2:编写grpc_server.go及grpc_client.go文件
▪️ grpc_server.go文件

package main
import (
	"fmt"
	pb "./proto"
	"net"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
)

type helloService struct{}

func (h helloService) SayHello(ctx context.Context, in *pb.Request) (*pb.Reply, error) {
	resp := new(pb.Reply)
	resp.Message = in.Message
	return resp, nil
}

var HelloServer = helloService{}

func main() {
	//指定服务地址及端口
	listen, err := net.Listen("tcp", "127.0.0.1:10086")
	if err != nil {
		fmt.Printf("failed to listen:%v\n", err)
	}
	//创建GRPC服务器的一个实例
	s := grpc.NewServer()
	//在GRPC服务器上注册服务
	pb.RegisterHelloServer(s, HelloServer)
	//服务阻塞等待
	s.Serve(listen)
}

▪️ grpc_client.go文件

package main

import (
	"fmt"
	pb "./proto"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
)
func main() {
	conn, err := grpc.Dial("127.0.0.1:10086", grpc.WithInsecure())
	if err != nil {
		fmt.Println(err)
	}
	defer conn.Close()

	c := pb.NewHelloClient(conn)//创建helloClient实例

	reqBody := new(pb.Request)
	reqBody.Message = "hello world!"
	r, err := c.SayHello(context.Background(), reqBody)//调用Hello服务的SayHello()方法,并传入参数
	if err != nil {
		fmt.Println(err)
	}
	fmt.Println(r.Message)
}

3:测试
▪️ 服务端开启:go run grpc_server.go
▪️ 客户端开启:go run grpc_client.go
运行结果:hello world!

总结:服务端和客户端依赖相同的.pb.go文件,实现远程接口调用。

下一篇,将对服务端和客户端依赖grpc通信的golang底层源码进行解读。

文章同步公众号,欢迎扫码关注
gRPC使用样例(golang)_第1张图片

你可能感兴趣的:(grpc)