go-grpc 基本使用


gRPC是什么?

gRPC是什么可以用官網的一句話來概括

A high-performance, open-source universal RPC framework

所謂RPC(remote procedure call 遠程過程調用)框架實際是提供了一套機制,使得應用程序之間可以進行通信,而且也遵從server/client模型。使用的時候客戶端調用server端提供的接口就像是調用本地的函數一樣。如下圖所示就是一個典型的RPC結構圖。

RPC通信

gRPC有什么好處以及在什么場景下需要用gRPC
既然是server/client模型,那么我們直接用restful api不是也可以滿足嗎,為什么還需要RPC呢?下面我們就來看看RPC到底有哪些優勢

gRPC vs. Restful API

gRPC和restful API都提供了一套通信機制,用於server/client模型通信,而且它們都使用http作為底層的傳輸協議(嚴格地說, gRPC使用的http2.0,而restful api則不一定)。不過gRPC還是有些特有的優勢,如下:

gRPC可以通過protobuf來定義接口,從而可以有更加嚴格的接口約束條件。

另外,通過protobuf可以將數據序列化為二進制編碼,這會大幅減少需要傳輸的數據量,從而大幅提高性能。
gRPC可以方便地支持流式通信(理論上通過http2.0就可以使用streaming模式, 但是通常web服務的restful api似乎很少這么用,通常的流式數據應用如視頻流,一般都會使用專門的協議如HLS,RTMP等,這些就不是我們通常web服務了,而是有專門的服務器應用。)

Protobuf是什么

官方文檔: https://developers.google.com/protocol-buffers

Protobuf實際是一套類似Json或者XML的數據傳輸格式和規范,用於不同應用或進程之間進行通信時使用。通信時所傳遞的信息是通過Protobuf定義的message數據結構進行打包,然后編譯成二進制的碼流再進行傳輸或者存儲。

Protobuf的優點

相比較而言,Protobuf有如下優點:

  • 足夠簡單
  • 序列化后體積很小:消息大小只需要XML的1/10 ~ 1/3
  • 解析速度快:解析速度比XML快20 ~ 100倍
  • 多語言支持
  • 更好的兼容性,Protobuf設計的一個原則就是要能夠很好的支持向下或向上兼容

安裝Protobuf

  1. 官網下載二進制文件
    https://github.com/protocolbuffers/protobuf/releases

根據自己的系統版本下載指定的穩定版本文件

我這邊是 MAC 就下載 protoc-3.11.4-osx-x86_64.zip文件

  1. 添加可執行文件到環境變量

  2. 為了基礎protobuf文件,使用protoc工具編譯出go相關文件,需要安裝一個插件

$ go get github.com/golang/protobuf/protoc-gen-go

GRPC示例

安裝grpc

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

GRPC服務端

目錄結構

.
├── main.go // 入口文件
├── pbfile  // protobuf 中間文件
│   └── Prob.proto
├── pbservices  // 根據中間文件生成的go文件
│   └── Prob.pb.go
└── services    // 根據生成的go文件的實現
    └── Prob.go

protobuf 中間文件

// proto 文件版本
syntax = "proto3";
// 生成文件的包名
package services;

// 請求結構體
message ProbRequest {
    int32 Pid = 1;
}

// 響應結構體
message ProbResponse {
    float Price = 1;
}

// 定義rpc接口
service ProbService {
    rpc GetProductPrice (ProbRequest) returns (ProbResponse)
}

使用中間文件生成go文件

$ cd pbfile/
$ protoc --go_out=plugins=grpc:../pbservices Prob.proto

實現生成的go文件的接口

// services/Prob.go
package services

import (
	"context"
	"pbservices"
)

type ProbService struct {
}

func (this *ProbService) GetProductPrice(context.Context, request *pbservices.ProbRequest) (*pbservices.ProbResponse, error) {
	return &pbservices.ProbResponse{Price: 10}, nil
}

創建rpc服務文件

package main

import (
	"pbservices"
	"services"
	"google.golang.org/grpc"
	"log"
	"net"
)

func main() {
	// 服務端啟用安全證書
	//cred, err := credentials.NewServerTLSFromFile("./keys/server.crt", "./keys/server.key")
	//if err != nil {
	//	log.Fatal(err)
	//}

	// 創建服務
	//rpcserver := grpc.NewServer(grpc.Creds(cred))
	rpcserver := grpc.NewServer()

	// 注冊
	pbservices.RegisterProbServicesServer(rpcserver, &services.ProbService{})

	// 啟動監聽
	lis, err := net.Listen("tcp", ":8081")
	if err != nil {
		log.Fatal(err)
	}
	err = rpcserver.Serve(lis)
	if err != nil {
		log.Fatal(err)
	}
}

GRPC客戶端

目錄結構

.
├── main.go     // 入口文件
└── pbservices  // 根據中間文件生成的go文件
    └── Prob.pb.go

客戶端實現

package main

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

func main() {
	// 啟用安全證書
	//cred, err := credentials.NewClientTLSFromFile("./keys/server.crt", "mazhichao.com")
	//if err != nil {
	//	log.Fatal(err)
	//}

	//conn, err := grpc.Dial(":8081", grpc.WithTransportCredentials(cred))
	// grpc.WithInsecure() 忽略安全
	conn, err := grpc.Dial(":8081", grpc.WithInsecure())
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()

	client := pbservices.NewProbServicesClient(conn)
	req := &pbservices.ProbRequest{Pid: 1}
	response, err := client.GetProductPrice(context.Background(), req)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(response.Price)
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM