如何使用grpc

第一步,先把环境安装好

官网的安装命令:

go get -u google.golang.org/grpc

貌似用不了,连不上服务器,即便我挂上vpn也没有用,没办法只有迂回安装了, 反正代码在github上都有,就从github上clone下来, 需要的库包括grpc-go, golang/net, golang/text, protobuf/proto, protobuf/protoc-gen-go, google/go-genproto

# 如果已经安装了proto(编译proto文件的工具)和protoc-gen-go(proto将proto文件编译成go语言形式的插件)的话就不用安装了
go get -u github.com/golang/protobuf/{proto,protoc-gen-go}

# 下载grpc-go
git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc

# 下载golang/net
git clone https://github.com/golang/net.git $GOPATH/src/golang.org/x/net

# 下载golang/text
git clone https://github.com/golang/text.git $GOPATH/src/golang.org/x/text

# 下载go-genproto
git clone https://github.com/google/go-genproto.git $GOPATH/src/google.golang.org/genproto

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

第二步,编写proto文件

构造message,message是参数,一般有请求参数和响应参数

写一个接口,接口中的方法供客户端和服务端来调用

$GOPATH/src/helloworld/helloworld/helloworld.proto代码:

syntax = "proto3";

package helloworld;

//定义服务
service Greeter{ //定义了这么一个接口,protoc这个工具将会生成两个,一个是client一个是server,因为这是一个rpc接口,里面的方法SayHello,客户端会调用这个方法,调用的这个请求会发送给服务端,服务端那边也要实现,服务端那边实现,客户端这边用
    rpc SayHello(HelloRequest) returns(HelloReply){}; //接口中的方法
}
message HelloRequest{ //方法的请求参数
    string name = 1;
}
message HelloReply{ //方法的响应参数
    string message = 1;
}

通过protoc生成go代码:

$GOPATH/src/helloworld/helloworld/helloworld.pb.go代码

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

package helloworld

import (
	"context"
	"fmt"
	"github.com/golang/protobuf/proto"
	"google.golang.org/grpc"
	"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 HelloRequest struct {
	Name                 string   `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
	XXX_NoUnkeyedLiteral struct{} `json:"-"`
	XXX_unrecognized     []byte   `json:"-"`
	XXX_sizecache        int32    `json:"-"`
}

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

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

var xxx_messageInfo_HelloRequest proto.InternalMessageInfo

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

type HelloReply 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 *HelloReply) Reset()         { *m = HelloReply{} }
func (m *HelloReply) String() string { return proto.CompactTextString(m) }
func (*HelloReply) ProtoMessage()    {}
func (*HelloReply) Descriptor() ([]byte, []int) {
	return fileDescriptor_73149fedf49f4319, []int{1}
}

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

var xxx_messageInfo_HelloReply proto.InternalMessageInfo

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

func init() {
	proto.RegisterType((*HelloRequest)(nil), "helloworld.HelloRequest")
	proto.RegisterType((*HelloReply)(nil), "helloworld.HelloReply")
}

func init() { proto.RegisterFile("helloworld/helloworld.proto", fileDescriptor_73149fedf49f4319) }

var fileDescriptor_73149fedf49f4319 = []byte{
	// 144 bytes of a gzipped FileDescriptorProto
	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0xce, 0x48, 0xcd, 0xc9,
	0xc9, 0x2f, 0xcf, 0x2f, 0xca, 0x49, 0xd1, 0x47, 0x30, 0xf5, 0x0a, 0x8a, 0xf2, 0x4b, 0xf2, 0x85,
	0xb8, 0x10, 0x22, 0x4a, 0x4a, 0x5c, 0x3c, 0x1e, 0x20, 0x5e, 0x50, 0x6a, 0x61, 0x69, 0x6a, 0x71,
	0x89, 0x90, 0x10, 0x17, 0x4b, 0x5e, 0x62, 0x6e, 0xaa, 0x04, 0xa3, 0x02, 0xa3, 0x06, 0x67, 0x10,
	0x98, 0xad, 0xa4, 0xc6, 0xc5, 0x05, 0x55, 0x53, 0x90, 0x53, 0x29, 0x24, 0xc1, 0xc5, 0x9e, 0x9b,
	0x5a, 0x5c, 0x9c, 0x98, 0x0e, 0x53, 0x04, 0xe3, 0x1a, 0x79, 0x72, 0xb1, 0xbb, 0x17, 0xa5, 0xa6,
	0x96, 0xa4, 0x16, 0x09, 0xd9, 0x71, 0x71, 0x04, 0x27, 0x56, 0x82, 0x75, 0x09, 0x49, 0xe8, 0x21,
	0xb9, 0x00, 0xd9, 0x32, 0x29, 0x31, 0x2c, 0x32, 0x05, 0x39, 0x95, 0x4a, 0x0c, 0x49, 0x6c, 0x60,
	0x97, 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0xea, 0x27, 0x0c, 0x07, 0xc8, 0x00, 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

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

// greeterClient已经实现了那个接口,到时候调用SayHello直接掉就行了
type greeterClient struct {
	cc *grpc.ClientConn
}

func NewGreeterClient(cc *grpc.ClientConn) GreeterClient { //客户端产生一个新的客户端,这个是给客户端使用的
	return &greeterClient{cc}
}

func (c *greeterClient) SayHello(ctx context.Context, in *HelloRequest, opts ...grpc.CallOption) (*HelloReply, error) {
	out := new(HelloReply)
	err := c.cc.Invoke(ctx, "/helloworld.Greeter/SayHello", in, out, opts...) //客户端调用完SayHello自动就会往grpc上发
	if err != nil {
		return nil, err
	}
	return out, nil
}

// GreeterServer is the server API for Greeter service.
type GreeterServer interface {
	//服务端怎么实现这个接口它根本就不知道怎么实现,服务端向让它实现什么它就实现什么,比如接收了SayHello了怎么处理,完了怎么回复,这个工具是不知道,这个是留给我们来实现的,这个是代码的核心逻辑
	SayHello(context.Context, *HelloRequest) (*HelloReply, error)
}

func RegisterGreeterServer(s *grpc.Server, srv GreeterServer) {
	s.RegisterService(&_Greeter_serviceDesc, srv)
}

func _Greeter_SayHello_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
	in := new(HelloRequest)
	if err := dec(in); err != nil {
		return nil, err
	}
	if interceptor == nil {
		return srv.(GreeterServer).SayHello(ctx, in)
	}
	info := &grpc.UnaryServerInfo{
		Server:     srv,
		FullMethod: "/helloworld.Greeter/SayHello",
	}
	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
		return srv.(GreeterServer).SayHello(ctx, req.(*HelloRequest))
	}
	return interceptor(ctx, in, info, handler)
}

var _Greeter_serviceDesc = grpc.ServiceDesc{
	ServiceName: "helloworld.Greeter",
	HandlerType: (*GreeterServer)(nil),
	Methods: []grpc.MethodDesc{
		{
			MethodName: "SayHello",
			Handler:    _Greeter_SayHello_Handler,
		},
	},
	Streams:  []grpc.StreamDesc{},
	Metadata: "helloworld/helloworld.proto",
}

第三步,实现服务端

首先定义一个结构体,这个结构体要实现上面的接口,实现了这个接口才能对外提供服务,参数请求

然后就可以启动主程序了

$GOPATH/src/helloworld/greeter_server/main.go代码:

package main

import (
	"golang.org/x/net/context"
	"google.golang.org/grpc/examples/helloworld/helloworld"
	"fmt"
	"net"
	"log"
	"google.golang.org/grpc"
)

const(
	port = ":5001"
)

type service struct{ //用来实现前面生成的接口

}

//对外提供一个供客户端调用的函数,只要客户端按照我这里要求传参,我就能对外提供一个我这里规定返回数据
func (s *service)SayHello(ctx context.Context, r *helloworld.HelloRequest) (*helloworld.HelloReply, error){ //通过Alt+Enter加入引入的包
	fmt.Println(r.Name)
	return &helloworld.HelloReply{Message:"hello," + r.Name},nil
}

func main(){
	lis,err := net.Listen("tcp", port) //监听请求

	if err != nil{
		log.Fatal("fail to listen")
	}

	s := grpc.NewServer() //NewServer一个grpc
	helloworld.RegisterGreeterServer(s, &service{}) //起到桥梁的作用 把GreaterServer注册到grpc里面去 把前面实现的service服务的结构体注册到grpc里面去,目的是实现的东西要让grpc系统知道
	if err := s.Serve(lis);err != nil{ //启动 这个服务由grpc来完成 死循环永远启动在这里
		log.Fatal("fail to server")
	}
}

第四步,实现客户端

先连接服务端,然后产生一个客户端的结构体

$GOPATH/src/helloworld/greeter_clinet/main.go代码:

package main

import (
	"google.golang.org/grpc"
	"log"
	"helloworld/helloworld"
	"golang.org/x/net/context"
	"fmt"
)

const(
	address = "127.0.0.1:5001"
)
//在客户端这里就不需要实现接口,客户端只管调用
func main(){
	conn,err := grpc.Dial(address, grpc.WithInsecure())
	if err != nil{
		log.Fatal("didnot connect server")
	}

	defer conn.Close()

	c := helloworld.NewGreeterClient(conn) //创建一个客户端 GreeterClient它内部维护了grpc链接 通过这个grpc链接就具有了远程调用能力

	r,err := c.SayHello(context.Background(), &helloworld.HelloRequest{Name:"xww"})
	if err != nil{
		log.Fatal("could not greet")
	}
	fmt.Println(r.Message)

}
发布了25 篇原创文章 · 获赞 2 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/longjuanfengzc/article/details/88183982