gPRC

gRPC介绍

gRPC是一个由Google开源的RPC框架

gRPC工作原理

在gRPC中客户端应用可以像调用本地对象的方法一样直接调用另一台不同机器上服务端应用的方法,使得我们能够更容易的创建分布式应用和服务。

gRPC基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个gRPC服务器来处理客户端的调用。在客户端拥有一个存根,它提供与服务器相同的方法或功能,存根由gRPC自动生成

golang grpc服务发现consul golang grpc原理_客户端

gRPC使用Protocol Buffer生成服务端&客户端存根

golang grpc服务发现consul golang grpc原理_golang_02

gRPC为什么高效
  • gRPC使用HTTP/2作为传输协议
gRPC & REST

golang grpc服务发现consul golang grpc原理_golang_03

protobuf编写

vscode下载vscode-proto3插件

安装Go protocol buffers插件 执行 go install github.com/golang/protobuf/protoc-gen-go@latest

编译生成go文件 protoc --go_out=./go ./proto/*

示例
// protobuf文件
syntax = "proto3";
option go_package="./;hello";

package hello;

// 字段编号 用于在消息二进制格式中标识你的字段并且在使用你的消息类型后不应更改
// 1 ~ 15 范围内的字段编号需要一个字节进行编码 16 ~ 2047 范围内的字段编码占用两个字节
message Person {
    string name = 1;
    int32 age = 2;
    string email = 3;
}
指定字段规则
  • 单数:符合语法规则的消息可以有零个或一个此字段(但不能超过一个),这是proto3语法的默认字段规则
  • repeated:该字段可以在符合语法规则的消息中重复任意次数(包括零次)。重复的值顺序将被保留
  • 在proto3中,repeated标量数值类型的字段packed默认使用编码
保留字段
  • 指定已删除字段的字段编号为reserved,如果将来有用户尝试使用这些字段标识符,protocol buffer 编译器会报错
message Foo {
	reserved 2, 15, 9 to 11;
	reserved "foo", "bar";
}
protobuf数据类型

int32 int64 double string …

protobuf枚举
  • 必须有一个零值,以便我们可以使用0作为数字默认值
  • 零值必须是第一个元素,以与第一个枚举值始终为默认值的proto2语义兼容
  • 可以将相同的值分配给不同的枚举常量来定义别名,需要设置allow_alias选项为true
syntax = "proto3";

message SearchRequest {
    string query = 1;
    int32 page_number = 2;
    int32 result_per_page = 3;
    enum Corpus {
        // 枚举中 0 1 2 ... 不是字段编号
        UNIVERSAL = 0;
        WEB = 1;
        IMAGES = 2;
        LOCAL = 3;
        NEWS = 4;
        PRODUCTS = 5;
        VIDEO = 6;
        /*
        	option allow_alias = true;
        	UNKONWN = 0;
        	STARTED = 1;
        	RUNNING = 1;
        */
    }
    Corpus corpus = 4;
}
序列化&反序列化
package main

import (
	"encoding/json"
	"fmt"
	"grpc/user"

	"google.golang.org/protobuf/proto"
)

func main() {
	article := &user.Article{
		Aid:   1,
		Title: "protobuf for golang",
		Views: 100,
	}
	// 序列化为二进制数据
	bytes, _ := proto.Marshal(article.ProtoReflect().Interface())
	fmt.Printf("bytes: %v\n", bytes)

	// 反序列化
	otherArticle := &user.Article{}
	proto.Unmarshal(bytes, otherArticle.ProtoReflect().Interface());
	fmt.Printf("otherArticle: %v\n", otherArticle)
}
protobuf与json转换

下载protojson go get google.golang.org/protobuf/encoding/protojson

package main

import (
	"fmt"
	"grpc/blog"

	"google.golang.org/protobuf/encoding/protojson"
)

func main() {
	article := &blog.Article {
		Aid: 1,
		Title: "protobuf for golang",
		Views: 100,
	}

	// message to json
	jsonString := protojson.Format(article.ProtoReflect().Interface())
	fmt.Printf("jsonString: %v\n", jsonString)

	// json to message
	m := article.ProtoReflect().Interface()
	protojson.Unmarshal([]byte(jsonString), m)
	fmt.Printf("m: %v\n", m)
}
protobuf定义服务

生成带有 service 的pb.go文件 protoc --go_out=plugins=grpc:. ./proto/*.proto

syntax = "proto3"

service UserService {
	rpc login(User) returns (User) {};
	rpc register(User) returns (User) {};
}
gRPC允许的四类服务方法
  • 单项RPC,即客户端发送一个请求给服务端,从服务端获取一个应答,就像一次普通的函数调用
rpc SayHello(HelloRequest) returns (HelloResponse) {}
  • 服务端流式RPC,即客户端发送一个请求给服务端,可获取一个数据流用来读取一系列消息。客户端从返回的数据流里一直读取直到没有更多消息为止
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse) {}
  • 客户端流式RPC,即客户端用提供的一个数据流写入并发送一系列消息给服务端,一旦客户端完成消息写入,就等待服务端读取消息并返回应答
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {}
  • 双向流式RPC,两边都可以通过一个读写数据流来发送一系列消息。这两个数据流操作是相互独立的,所以客户端和服务端能按其希望的任意顺序读写
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse)
gRPC示例

引入依赖 :

go get -u google.golang.org/grpc

go install github.com/golang/protobuf/protoc-gen-go@latest

grpc-example