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)
}
相關文章
相關標籤/搜索