grpc 笔记

gRPC  是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHPC# 支持.

gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。

1.要编译proto文件生成go代码需要两个工具:

  • protoc :用于编译(其他语言只需要protoc足以)

  • protoc-gen-go : 用于生成go语言的文件(go语言专用插件)

  • 由于有墙,所以在这依赖或者工具都去 github 上去找

  • protoc-gen-go下载:go get github.com/golang/protobuf/protoc-gen-go

  • protoc :https://github.com/google/protobuf/release... 下载对应的 OS 要的安装包。

  • 配置环境变量:解压后找到对应的 bin 目录,file

  • 配置完,在命令行中输入 protoc,会有以下输出,即表示安装成功:

  • file

    2.安装 grpc

    • 命令:go get google.golang.org/grpc

    3.目录结构

    • file

      protos
      –protos.pb.go 编译proto之后生成go文件
      –protos.proto 要编写的proto文件
      service
      – service.go 服务端的业务
      service.go 服务端的启动入口
      client.go 客户端的入口

4. 编写 protos.proto 文件

syntax = "proto3";
package protos;
message User{
  int32 id =1 ;
  string name =2 ;
}

message UserReq{
    int32 id =1;
}

service IUserService{
    // 单一请求应答,一对一
    rpc Get (UserReq) returns (User);
    // 服务端流式应答,一对多,可用于下载
    rpc GetList (UserReq) returns (stream User);
    // 客户端流式请求,多对一,可用于上传
    rpc WaitGet(stream UserReq) returns (User);
    // 双向流式请求应答,支持HTTP/2.0
    rpc LoopGet(stream UserReq) returns (stream User);
}
  • cd 到 protos.proto 所在的目录:
    • 在命令行中输入:protoc --go_out=plugins=grpc:. ./protos.proto,将会生成 protos.pb.go 文件。
    • rpc Get (google.protobuf.Empty) returns (User) 方法里面的出参跟入参都不能不填,如果要为空 ,可以导入 import “google/protobuf/empty.proto” 里面有个 message Empty {} *

5.实现服务端 service/service.go

package service

import (
    "goweb/protos"
    "fmt"
    "strconv"
    "io"
    "golang.org/x/net/context"
)

//对外提供的工厂函数
func NewUserService() *UserService {
    return &UserService{}
}

//**************************************************************
// 接口实现,接口定义是在proto生成的.pb.go文件中
//**************************************************************

// 接口实现对象,属性成员根据而业务自定义
type UserService struct {
}

// Get接口方法实现
func (this *UserService) Get(ctx context.Context, req *protos.UserReq) (*protos.User, error) {
    return &protos.User{Id: 1, Name: "shuai"}, nil
}

// GetList接口方法实现
func (this *UserService) GetList(req *protos.UserReq, stream protos.IUserService_GetListServer) error {
    fmt.Println(*req)
    // 流式返回多条数据
    for i := 0; i < 5; i++ {
        stream.Send(&protos.User{Id: int32(i), Name: "我是" + strconv.Itoa(i)})
    }
    return nil
}

// WaitGet接口方法实现
func (this *UserService) WaitGet(reqStream protos.IUserService_WaitGetServer) error {
    for { // 接收流式请求并返回单一对象
        userReq, err := reqStream.Recv()
        if err != io.EOF {
            fmt.Println("流请求~", *userReq)
        } else {
            return reqStream.SendAndClose(&protos.User{Id: 100, Name: "shuai"})
        }
    }
}

//双向流:请求流和响应流异步
func (this *UserService) LoopGet(reqStream protos.IUserService_LoopGetServer) error {
    for {
        userReq, err := reqStream.Recv()
        if err == io.EOF { //请求结束
            return nil
        }
        if err != nil {
            return err
        }
        if err = reqStream.Send(&protos.User{Id: userReq.Id, Name: "shuai"}); err != nil {
            return err
        }
    }
}

6.启动服务端 goweb/service.go

package main
import (
    "flag"
    "fmt"
    "net"
    "goweb/service" // 实现了服务接口的包service
    "goweb/protos" // 此为自定义的protos包,存放的是.proto文件和对应的.pb.go文件

    "google.golang.org/grpc"
)

var (
    // 命令行参数-host,默认服务监听端口在9000
    addr = flag.String("host", "127.0.0.1:9000", "")
)

func main() {
    // 开启服务监听
    lis, err := net.Listen("tcp", *addr)
    if err != nil {
        fmt.Println(err)
        fmt.Println("listen error!")
        return
    }
    // 创建一个grpc服务
    grpcServer := grpc.NewServer()
    // 重点:向grpc服务中注册一个api服务,这里是UserService,处理相关请求
    protos.RegisterIUserServiceServer(grpcServer, service.NewUserService())
    // 可以添加多个api
    // TODO...

    // 启动grpc服务
    grpcServer.Serve(lis)
}
  • cd 到 goweb 目录,输入go run service.go 启动服务。

7.客户端的实现 goweb/client.go

package main
import (
    "flag"
    "fmt"
    "io"
    "log"
    "goweb/protos" // 此为自定义的protos包,存放的是.proto文件和对应的.pb.go文件
    "golang.org/x/net/context"
    "google.golang.org/grpc"
)

var (
    // 命令行参数-host,默认地址本机9000端口
    addr1 = flag.String("host", "127.0.0.1:9000", "")
    // UserService服务存根,可直接调用服务方法
    userCli protos.IUserServiceClient
)

func main() {
    // 创建grpc的服务连接
    conn, err := grpc.Dial(*addr1,grpc.WithInsecure())
    if err != nil {
        log.Fatal("failed to connect : ", err)
    }
    // 存根
    userCli = protos.NewIUserServiceClient(conn)

    // 测试前三种数据传递方式,第四种省略(二三结合)
    TestGet()
    TestGetList()
    TestWaitGet()
}

func TestGet() {
    // 测试调用方法Get,返回user对象
    user, err := userCli.Get(context.Background(), &protos.UserReq{Id: 1})
    if err != nil {
        fmt.Printf("Get connect failed :%v", err)
        return
    }
    log.Println("Get响应数据:", *user)
}

func TestGetList() {
    // 测试调用方法GetList,返回一个Stream流,循环获取多个user对象
    recvStream, err := userCli.GetList(context.Background(), &protos.UserReq{Id: 1})
    if err != nil {
        fmt.Printf("GetList connect failed :%v", err)
        return
    }
    for {
        user, err := recvStream.Recv()
        if err == io.EOF {
            break
        }
        log.Println("GetList获取的一条响应数据:", *user)
    }
}

func TestWaitGet() {
    // 测试调用方法WaitGet,传入多条请求数据,返回一个user对象
    sendStream, err := userCli.WaitGet(context.Background())
    if err != nil {
        fmt.Printf("WaitGet connect failed :%v", err)
        return
    }
    for i := 0; i < 5; i++ { // 一次传入5条请求数据
        if err = sendStream.Send(&protos.UserReq{Id: int32(i)}); err != nil {
            fmt.Printf("WaitGet send failed :%v", err)
            return
        }
    }
    // 服务端接受全部请求数据后,返回一个user对象
    user, err := sendStream.CloseAndRecv()
    if err != nil {
        fmt.Printf("WaitGet recv failed :%v", err)
        return
    }
    log.Println("WaitGet响应数据:", *user)
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
不卑不亢,不慌不忙,这才是生活的模样。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!