使用grpc编写一个服务分如下三步:
由“.proto”文件生成.pb.go文件,依赖protoc编译器、protoc编译器go插件及grpc-go第三方库文件
自定义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文件中主要包含以下内容:
注意:此步骤可能出现问题如下:
可能出现的问题:
问题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底层源码进行解读。