golang 微服务之gRPC与Protobuf

RPC是什么?

所谓RPC(remote procedure call 远程过程调用)框架实际是提供了一套机制,使得应用程序之间能够进行通讯,并且也听从server/client模型。使用的时候客户端调用server端提供的接口就像是调用本地的函数同样。linux

gRPC是什么?

与许多RPC系统同样,gRPC基于定义服务的思想,指定可使用其参数和返回类型远程调用的方法。默认状况下,gRPC使用协议缓冲区做为接口定义语言(IDL)来描述服务接口和有效负载消息的结构。git

gRPC有什么好处以及在什么场景下须要用gRPC

既然是server/client模型,那么咱们直接用restful api不是也能够知足吗,为何还须要RPC呢?下面咱们就来看看RPC到底有哪些优点github

gRPC vs. Restful API

gRPC和restful API都提供了一套通讯机制,用于server/client模型通讯,并且它们都使用http做为底层的传输协议(严格地说, gRPC使用的http2.0,而restful api则不必定)。不过gRPC仍是有些特有的优点,以下:golang

  • gRPC能够经过protobuf来定义接口,从而能够有更加严格的接口约束条件。关于protobuf能够参见笔者以前的小文Google Protobuf简明教程
  • 另外,经过protobuf能够将数据序列化为二进制编码,这会大幅减小须要传输的数据量,从而大幅提升性能。
  • gRPC能够方便地支持流式通讯(理论上经过http2.0就可使用streaming模式, 可是一般web服务的restful api彷佛不多这么用,一般的流式数据应用如视频流,通常都会使用专门的协议如HLS,RTMP等,这些就不是咱们一般web服务了,而是有专门的服务器应用。)

使用场景

  • 须要对接口进行严格约束的状况,好比咱们提供了一个公共的服务,不少人,甚至公司外部的人也能够访问这个服务,这时对于接口咱们但愿有更加严格的约束,咱们不但愿客户端给咱们传递任意的数据,尤为是考虑到安全性的因素,咱们一般须要对接口进行更加严格的约束。这时gRPC就能够经过protobuf来提供严格的接口约束。
  • 对于性能有更高的要求时。有时咱们的服务须要传递大量的数据,而又但愿不影响咱们的性能,这个时候也能够考虑gRPC服务,由于经过protobuf咱们能够将数据压缩编码转化为二进制格式,一般传递的数据量要小得多,并且经过http2咱们能够实现异步的请求,从而大大提升了通讯效率。

可是,一般咱们不会去单独使用gRPC,而是将gRPC做为一个部件进行使用,这是由于在生产环境,咱们面对大并发的状况下,须要使用分布式系统来去处理,而gRPC并无提供分布式系统相关的一些必要组件。并且,真正的线上服务还须要提供包括负载均衡,限流熔断,监控报警,服务注册和发现等等必要的组件。不过,这就不属于本篇文章讨论的主题了,咱们仍是先继续看下如何使用gRPC。web

gRPC的使用一般包括以下几个步骤

  1. 经过protobuf来定义接口和数据类型
  2. 编写gRPC server端代码
  3. 编写gRPC client端代码

protobuf的安装

mac:brew install protobufwindows

windows:protoc 下载:官方地址,而后将 bin 路径添加到 path 环境变量下去api

linux:
安装须要的依赖包:
[root@localhost ~]# yum -y install autoconf automake libtool curl make g++ unzip
[root@localhost ~]# unzip protobuf-master.zip
[root@localhost ~]# cd protobuf-master
生成configure文件的脚本文件,若是不执行这步,如下操做将通不过
[root@localhost protobuf-master]#  ./autogen.sh 
[root@localhost protobuf-master]#  ./configure
能够修改安装目录经过 ./configure --prefix=命令,统一安装在/usr/local/protobuf下
[root@localhost protobuf-master]# ./configure --prefix=/usr/local/protobuf
[root@localhost protobuf-master]#  make
[root@localhost protobuf-master]#  make check
[root@localhost protobuf-master]#  make install
[root@localhost protobuf-master]#  ldconfig # refresh shared library cache.
安装成功
[root@localhost protobuf-master]#  protoc -I=./ --cpp_out=./ test.proto安全

安装grpc包

go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
go get -u google.golang.org/grpc
protoc --go_out=plugins=grpc:. *.proto

定义接口和数据类型

syntax = "proto3";

package rpc_package;

// define a service
service HelloWorldService {
    // define the interface and data type
    rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// define the data type of request
message HelloRequest {
    string name = 1;
}

// define the data type of response
message HelloReply {
    string message = 1;
}

使用protobuf生成工具生成对应语言的库函数

protoc --go_out=plugins=grpc:. helloworld.proto

server.go

// server.go
  
import (
    "log"
    "net"
  
    "golang.org/x/net/context"
    "google.golang.org/grpc"
    pb "helloworld/helloworld"
)
  
const (
    port = ":50051"
)
  
type server struct {}
  
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}
  
func main() {
    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatal("failed to listen: %v", err)
    }
    s := grpc.NewServer()
    pb.RegisterGreeterServer(s, &server{})
    s.Serve(lis)
}

client.go

package main
  
//client.go
  
import (
    "log"
    "os"
  
    "golang.org/x/net/context"
    "google.golang.org/grpc"
    pb "helloworld/helloworld"
)
  
const (
    address     = "localhost:50051"
    defaultName = "world"
)
  
func main() {
    conn, err := grpc.Dial(address, grpc.WithInsecure())
    if err != nil {
        log.Fatal("did not connect: %v", err)
    }
    defer conn.Close()
    c := pb.NewGreeterClient(conn)
  
    name := defaultName
    if len(os.Args) >1 {
        name = os.Args[1]
    }
    r, err := c.SayHello(context.Background(), &pb.HelloRequest{Name: name})
    if err != nil {
        log.Fatal("could not greet: %v", err)
    }
    log.Printf("Greeting: %s", r.Message)
}
相关文章
相关标签/搜索