1.什么是gRPC

gRPC 是一个高性能、开源、通用的RPC框架,由Google推出,基于HTTP2协议标准设计开发,默认采用Protocol Buffers数据序列化协议,支持多种开发语言。gRPC提供了一种简单的方法来精确的定义服务,并且为客户端和服务端自动生成可靠的功能库。

2.gRPC技术栈

最底层为TCPUnix套接字协议,在此之上是HTTP/2协议的实现,然后在HTTP/2协议之上又构建了针对Go语言的gRPC核心库(gRPC内核+解释器)。应用程序通过gRPC插件生成的Stub代码和gRPC核心库通信,也可以直接和gRPC核心库通信。

3. 前置准备

3.1 安装protoc

下面示例是在mac环境中安装。

# 安装
➜ brew install protobuf
# 查看安装后版本
➜ protoc --version
libprotoc 3.17.3

3.2 安装插件

安装插件的目的是为了将protobuf文件,生成Go代码

$ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.26
$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1

3.3 设置插件环境变量

$ export PATH="$PATH:$(go env GOPATH)/bin"

3.4 验证插件是否成功

# 查看protoc-gen-go版本
$ protoc-gen-go --version
protoc-gen-go v1.26.0

# 查看protoc-gen-go-grpc版本
$ protoc-gen-go-grpc --version
protoc-gen-go-grpc 1.1.0

4. 快速使用

4.1 定义protobuf文件

文件名: hello.proto

syntax = "proto3";
package hello;

// 定义go生成后的包名
option go_package = "server/hello";

// 定义入参
message Request {
string name =1;
}
// 定义返回
message Response {
string result = 1;
}

// 定义接口
service UserService {
rpc Say(Request) returns (Response);
}

4.2 生成GO代码

# 同时生成hello.pb.go 和
$ protoc --go-grpc_out=. --go_out=. hello.proto

4.3 查看生成代码

1. hello.pb.go部分代码

// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.17.3
// source: grpc/hello.proto

package hello

import (
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)

const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)

// 定义入参
type Request struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields

Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
}

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

// 定义返回
type Response struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields

Result string `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"`
}
func (x *Response) GetResult() string {
if x != nil {
return x.Result
}
return ""
}

2. hello_grpc.pb.go部分代码

// Code generated by protoc-gen-go-grpc. DO NOT EDIT.

package hello

import (
context "context"
"fmt"
grpc "google.golang.org/grpc"
)

// ---------- 客户端相关代码 --------
// 客户端接口
type UserServiceClient interface {
Say(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error)
}
// 实现客户端接口
type userServiceClient struct {
cc grpc.ClientConnInterface
}
// 实例化客户端
func NewUserServiceClient(cc grpc.ClientConnInterface) UserServiceClient {
return &userServiceClient{cc}
}
// 客户端调用Say方法
func (c *userServiceClient) Say(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) {
out := new(Response)
err := c.cc.Invoke(ctx, "/hello.UserService/Say", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}

// ---------- 服务端相关代码 ------------------
// 定义服务端接口
type UserServiceServer interface {
Say(context.Context, *Request) (*Response, error)
mustEmbedUnimplementedUserServiceServer()
}

// 实现服务端接口
type UnimplementedUserServiceServer struct {
}

// 实现方法
func (UnimplementedUserServiceServer) Say(ctx context.Context, r *Request) (*Response, error) {
// 自己编写代码
reply := &Response{
Result: fmt.Sprintf("%s say hello word!", r.Name),
}
return reply, nil
}
// 注册服务
func RegisterUserServiceServer(s grpc.ServiceRegistrar, srv UserServiceServer) {
s.RegisterService(&UserService_ServiceDesc, srv)
}

4.4 下载grpc

$ go get -u google.golang.org/grpc

4.5 编写服务端代码

server.go

package main

import (
"52lu/go-study-example/grpc/server/hello"
"fmt"
"google.golang.org/grpc"
"net"
)

func main() {
// 创建grpc服务
grpcServer := grpc.NewServer()
// 注册服务
hello.RegisterUserServiceServer(grpcServer, new(hello.UnimplementedUserServiceServer))
// 监听端口
listen, err := net.Listen("tcp", ":1234")
if err != nil {
fmt.Println("服务启动失败", err)
return
}
grpcServer.Serve(listen)
}

4.6 编写客户端代码

client.go

package main

import (
"52lu/go-study-example/grpc/server/hello"
"fmt"
"golang.org/x/net/context"
"google.golang.org/grpc"
)

func main() {
// 建立链接
conn, err := grpc.Dial("127.0.0.1:1234", grpc.WithInsecure())
if err != nil {
fmt.Println("Dial Error ", err)
return
}
// 延迟关闭链接
defer conn.Close()
// 实例化客户端
client := hello.NewUserServiceClient(conn)
// 发起请求
reply, err := client.Say(context.TODO(), &hello.Request{Name: "张三"})
if err != nil {
return
}
fmt.Println("返回:", reply.Result)
}

4.7 启动 & 请求

# 启动服务端
$ go run server.go
# 启动客户端
$ go run client.go
返回: 张三 say hello word!