blog icon indicating copy to clipboard operation
blog copied to clipboard

.proto 协议缓冲文件介绍

Open huangshuwei opened this issue 3 years ago • 0 comments

前言

介绍 .proto 协议缓冲文件的基本用途,我们下面以一个helloworld 示例介绍。 示例项目:https://github.com/huangshuwei/vue-grpc-demo

什么是 .proto 协议缓冲文件

协议缓冲(Protocol Buffers),官方有很详细的协议缓冲的介绍。总结下有这些功能点:

  • Defining A Message Type
  • Scalar Value Types
  • Optional And Default Values
  • Enumerations
  • Using Other Message Types
  • Nested Types
  • Updating A Message Type
  • Extensions
  • Oneof
  • Maps
  • Packages
  • Defining Services
  • Options
  • Generating Your Classes

helloword 示例

我们只看客户端实现部分,假设已经定义好了一个 helloworld.proto 文件,内容如下:

syntax = "proto3";

package helloworld;

service Greeter {
  // unary call
  rpc SayHello(HelloRequest) returns (HelloReply);
  // server streaming call
  rpc SayRepeatHello(RepeatHelloRequest) returns (stream HelloReply);
}

message HelloRequest {
  string name = 1;
}

message RepeatHelloRequest {
  string name = 1;
  int32 count = 2;
}

message HelloReply {
  string message = 1;
}

生成Protobuf消息和客户端服务存根

根据 helloworld.proto文件我们可以生成Protobuf消息和客户端服务存根,如何生成可以参考windows环境生成Protobuf消息和客户端服务存根

操作成功后,会生成 helloworld_pb.js 和 helloworld_grpc_web_pb.js 文件。

helloworld_pb.js 文件会包含协议信息,而 helloworld_grpc_web_pb.js 文件则包含客户端操作的信息

helloworld.proto 文件分析-消息类型

通过关键字 message 定义了3个消息类型,分别是 HelloRequest、RepeatHelloRequest以及HelloReply

HelloRequest 消息,包含 string 类型的参数 name

message HelloRequest {
  string name = 1;
}

RepeatHelloRequest 消息,包含 string 类型的参数 nameint32 类型的参数 count

message RepeatHelloRequest {
  string name = 1;
  int32 count = 2;
}

HelloReply 消息,包含 string 类型的参数 message

message HelloReply {
  string message = 1;
}

helloworld.proto 文件分析-服务

通过关键字 service 定义服务(接口):

service Greeter {
  // unary call(一元模式)
  rpc SayHello(HelloRequest) returns (HelloReply);
  // server streaming call(服务端流模式)
  rpc SayRepeatHello(RepeatHelloRequest) returns (stream HelloReply);
}

可以看到,Greeter 对象包含两个服务。一元模式服务SayHello 和 服务端流模式SayRepeatHello服务。一元模式可以理解为客户端单次请求的方式,而服务端流模式可以理解为服务端推送方式。

客户端编写

获取消息类型对象,以及请求客户端对象

通过 helloworld_pb.js 文件获取消息类型对象。通过 helloworld_grpc_web_pb.js 文件获取客户端请求对象

// client.js
const {
  HelloRequest,
  HelloReply,
} = require("./service/proto/helloworld_pb.js");
const { GreeterClient } = require("./service/proto/helloworld_grpc_web_pb.js");

var client = new GreeterClient("http://" + window.location.hostname + ":8080", null, null);

SayHello 一元模式

此时我们可以通过 helloworld_pb.js 文件获取到这些协议信息。

给参数赋值,是通过消息类型的示例设置的。如HelloRequest 消息类型,它有一个 name 参数,name 赋值是通过 new HelloRequest().setName("参数值");

// client.js

// simple unary call
var request = new HelloRequest();
request.setName("World");

client.sayHello(request, {}, (err, response) => {
  if (err) {
    console.log(
      `Unexpected error for sayHello: code = ${err.code}` + `, message = "${err.message}"`
    );
  } else {
    console.log(response.getMessage());
  }
});

SayRepeatHello 服务端流模式

和一元模式不同的是,对于请求的接收,是通过流的模式。如下:

// client.js

// simple unary call
var request = new HelloRequest();
request.setName("World");

client.sayHello(request, {}, (err, response) => {
  if (err) {
    console.log(
      `Unexpected error for sayHello: code = ${err.code}` + `, message = "${err.message}"`
    );
  } else {
    console.log(response.getMessage());
  }
});

客户端请求完整代码:

// client.js
const {
  HelloRequest,
  RepeatHelloRequest,
  HelloReply,
} = require("./service/proto/helloworld_pb.js");
const { GreeterClient } = require("./service/proto/helloworld_grpc_web_pb.js");

var client = new GreeterClient("http://" + window.location.hostname + ":8080", null, null);

// simple unary call
var request = new HelloRequest();
request.setName("World");

client.sayHello(request, {}, (err, response) => {
  if (err) {
    console.log(
      `Unexpected error for sayHello: code = ${err.code}` + `, message = "${err.message}"`
    );
  } else {
    console.log(response.getMessage());
  }
});

// server streaming call
var streamRequest = new RepeatHelloRequest();
streamRequest.setName("World");
streamRequest.setCount(5);

var stream = client.sayRepeatHello(streamRequest, {});
stream.on("data", (response) => {
  console.log(response.getMessage());
});
stream.on("error", (err) => {
  console.log(`Unexpected stream error: code = ${err.code}` + `, message = "${err.message}"`);
});

总结

此时我们距离代码可以运行还有一段距离,下一篇将介绍 windows10 基于grpc-web 的 envoy 代理配置

huangshuwei avatar May 08 '21 02:05 huangshuwei