Grpc 2018

Posted by Bink的博客 on November 12, 2018

gRPC初识相关介绍以及简单模式

一、Grpc是什么

grpc 是 google 开源的一款网络框架,具有极好的性能,可能是目前性能最好的网络框架,支持流式 rpc。 可以很方便地构建消息订阅发布系统,支持几乎所有主流的语言,运行非常稳定。基于HTTP2.0传输数据,数据序列化框架为Google开源的Protocol Buffers

开始之前首先你要知道网络框架为你做了哪些事情:

  • 网络协议序列化与反序列化
  • 网络底层通信
  • 并发管理

以及需要你做哪些事情:

  • 定义通信的内容(通过协议文件)
  • 实现通信的方法(实现协议接口)

二、ProtoBuf

需要工具主要包括:

  1. 编译器:protoc,以及一些官方没有带的语言插件
  2. 运行环境:各种语言的 protobuf 库,不同语言有不同的安装来源

构建基于Golang的Grpc环境

这里我们以golang语言为实例,来使用grpc,安装环境步骤如下:

  1. 安装protoc
    $ git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc
    $ git clone https://github.com/golang/net.git $GOPATH/src/golang.org/x/net
    $ git clone https://github.com/golang/text.git $GOPATH/src/golang.org/x/text
    $ git clone https://github.com/google/go-genproto.git $GOPATH/src/google.golang.org/genproto
    $ cd $GOPATH/src/
    $ go install google.golang.org/grpc // 安装grpc
    $ protoc --version // 检查是否安装成功
    
  2. go protoc-gen-go插件
    $ go get -u github.com/golang/protobuf/{proto,protoc-gen-go} // go protoc-gen-go插件
    

三、简单Hello服务实例

1. 定义协议文件

首先要定义通信的协议,grpc 使用的是 proto3 序列化协议,这是一个高效的协议,具体细节可以看官网:ProtoBuf文档

syntax = "proto3";

package hello;

message HelloReq {
    string msg = 1;
}

message HelloRes {
    string msg = 1;
}

service Hello {
    rpc echo (HelloReq) returns (HelloRes);
}

其中HelloReq表示请求结构体,里面有一个参数msg为String类型,HelloRes表示相应结构体,和请求结构体类似, service是用来定义服务接口的,本协议文件定义了一个echo接口,请求参数为HelloReq,相应结果为HelloRes

执行如下命令会自动生成 hello.pb.go 文件,这个过程其实是把上面这个协议翻译成 golang:

$ protoc --go_out=plugins=grpc:. hello.proto

2. 实现协议接口

package services

import (
	"context"
	"go/grpc/protos"
	"fmt"
)

type HelloService struct {}

func (this *HelloService) Echo(ctx context.Context, req *hello.HelloReq) (*hello.HelloRes, error) {
	fmt.Printf("message from client: %s\n", req.GetMsg())
	res := &hello.HelloRes{
		Msg: "Server already receive your msg:" + req.GetMsg(),
	}

	return res, nil
}

这个协议接口的定义可以在hello.pb.go中找到,主要是在里面实现的具体的业务逻辑,我们这边做的比较简单,就是读取客户端 的请求信息,然后返回相响应结果给客户端,告诉它服务端已经收到请求了。可以把他理解为MVC里面的C(控制器)。里面的Echo就是一个 具体的接口方法。

3. 实现服务端

然后我们创建一个server.go文件来写我们的服务端代码,把我们刚刚时间的HelloService类注册到grpc服务里面,这样当我们 启动服务的时候,这个接口服务就可以被客户端访问了,这代码如下:

package main

import (
	pb "go/grpc/protos"
	"net"
	"log"
	"google.golang.org/grpc"
	"go/grpc/consts"
	"fmt"
	"go/grpc/services"
)

func main() {
	listener, err := net.Listen("tcp", consts.PORT)
	if err != nil {
		log.Fatalf("Failed to listen: %v", err)
	}

	s := grpc.NewServer()
	pb.RegisterHelloServer(s, &services.HelloService{}) // 注册hello service

	fmt.Println("Starting server on port ", consts.PORT)
	if err := s.Serve(listener); err != nil {
		panic(err)
	}
}

代码主要有三步:

  • 创建grpc服务
  • 注册HelloService服务到grpc中
  • 监听服务端口PORT,然后启动服务

启动服务命令如下:

$ go run server.go

4. 实现客户端

创建客户端文件client.go,写请求服务端Echo接口代码:

package main

import (
	"google.golang.org/grpc"
	"log"
	pb "go/grpc/protos"
	"os"
	"golang.org/x/net/context"
	"go/grpc/consts"
	"fmt"
	"strings"
)

func main() {
	conn, err := grpc.Dial(consts.HOST, grpc.WithInsecure())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()

	client := pb.NewHelloClient(conn)
	res, err := client.Echo(context.Background(), &pb.HelloReq{
		Msg: strings.Join(os.Args[1:], " "),
	})

	if err != nil {
		fmt.Errorf("client echo failed. err: [%v]", err)
		return
	}

	fmt.Printf("Msg from server: %s", res.GetMsg())
}

写完代码后,本地访问我们刚才启动的服务端接口:

$ go run client.go Hello World.

实例中代码URL

实例中代码链接:点我点我