Vue集成gRPC-web/Protocol Buffers:实现高性能、二进制格式的客户端/服务端通信

Vue集成gRPC-web/Protocol Buffers:实现高性能、二进制格式的客户端/服务端通信

大家好,今天我们来聊聊如何在Vue项目中集成gRPC-web和Protocol Buffers,以实现客户端和服务端之间高性能、二进制格式的通信。

1. 什么是gRPC-web?为什么要用它?

gRPC 是一种高性能、开源和通用的 RPC 框架,由 Google 开发。它使用 Protocol Buffers 作为接口定义语言,并使用 HTTP/2 作为传输协议。然而,由于浏览器对 HTTP/2 的支持有限,以及跨域请求的限制,直接从浏览器中使用 gRPC 客户端存在一些挑战。

gRPC-web 旨在解决这些问题,它允许 Web 应用程序直接与 gRPC 服务通信,而无需中间层。它通过一个特殊的代理服务器(gRPC-web proxy,通常是 Envoy 或 grpcwebproxy)将浏览器发出的 HTTP/1.1 请求转换为 gRPC 请求,然后再转发到 gRPC 服务。

使用 gRPC-web 的优势:

  • 高性能: gRPC 使用 Protocol Buffers 进行序列化,相比 JSON 等文本格式,Protocol Buffers 更小、更快,减少了网络传输的开销。
  • 类型安全: Protocol Buffers 定义了明确的接口,可以确保客户端和服务端之间的数据类型一致,减少了错误。
  • 代码生成: 可以使用 Protocol Buffers 编译器生成客户端和服务端的代码,简化了开发过程。
  • 双向流: gRPC 支持双向流,允许客户端和服务端之间进行实时的双向通信。

简单来说,gRPC-web 让我们在浏览器端也能享受到 gRPC 的高性能和类型安全,同时避免了直接使用 gRPC 客户端的复杂性。

2. 准备工作

在开始之前,我们需要安装一些必要的工具:

  • Node.js 和 npm (或 yarn): 用于构建和运行 Vue 项目。
  • Protocol Buffers 编译器 (protoc): 用于编译 .proto 文件。
  • gRPC-web 插件 (protoc-gen-grpc-web): 用于生成 gRPC-web 客户端代码。
  • Envoy 或 grpcwebproxy: 作为 gRPC-web 代理服务器。

安装 Protocol Buffers 编译器 (protoc):

根据你的操作系统,从 https://github.com/protocolbuffers/protobuf/releases 下载相应的安装包,并将其添加到系统路径中。

安装 gRPC-web 插件 (protoc-gen-grpc-web):

npm install -g grpc-web

安装 Envoy 或 grpcwebproxy:

这里我们选择使用 grpcwebproxy,因为它配置更简单。

go install github.com/improbable-eng/grpc-web/go/grpcwebproxy@latest

确保你的 Go 环境已经正确配置,并且 $GOPATH/bin 目录已经添加到系统路径中。

3. 定义 Protocol Buffers 接口

首先,我们需要定义一个 .proto 文件来描述我们的服务接口和数据结构。

例如,我们定义一个简单的 Greeter 服务,包含一个 SayHello 方法:

syntax = "proto3";

package greeter;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply);
}

message HelloRequest {
  string name = 1;
}

message HelloReply {
  string message = 1;
}

将以上内容保存为 greeter.proto 文件。

4. 生成 gRPC-web 客户端代码

使用 Protocol Buffers 编译器和 gRPC-web 插件来生成客户端代码。

protoc -I. --grpc-web_out=import_style=typescript,mode=grpcwebtext:. greeter.proto

这个命令会生成两个文件:

  • GreeterServiceClientPb.ts: 包含 gRPC 客户端的 TypeScript 类。
  • greeter_pb.ts: 包含 Protocol Buffers 消息定义的 TypeScript 类。

参数解释:

  • -I.:指定 .proto 文件的搜索路径。
  • --grpc-web_out=...: 指定 gRPC-web 插件的输出目录和选项。
    • import_style=typescript: 使用 TypeScript 风格的导入方式。
    • mode=grpcwebtext: 指定传输模式为 grpcwebtext,这是 gRPC-web 的默认模式,使用 Base64 编码。 也可以选择 grpcweb,使用二进制格式,但需要配置 CORS。

5. 配置 gRPC-web 代理服务器

我们需要配置 grpcwebproxy 将浏览器发出的 HTTP/1.1 请求转换为 gRPC 请求。

创建一个配置文件 grpcwebproxy.conf

backend_addr: localhost:50051  # gRPC 服务的地址
bind_addr: 0.0.0.0:8080       # 代理服务器监听的地址
allow_all_origins: true          # 允许所有来源的跨域请求 (生产环境不建议)

启动 grpcwebproxy:

grpcwebproxy --config_file=grpcwebproxy.conf

6. 创建 Vue 项目并集成 gRPC-web

创建一个新的 Vue 项目:

vue create vue-grpc-web-example

选择你喜欢的选项,比如使用 TypeScript。

安装 gRPC-web 运行时库:

npm install google-protobuf grpc-web

将生成的 GreeterServiceClientPb.tsgreeter_pb.ts 文件复制到 Vue 项目的 src 目录下。

在 Vue 组件中使用 gRPC-web 客户端:

<template>
  <div>
    <input type="text" v-model="name" placeholder="Enter your name">
    <button @click="sayHello">Say Hello</button>
    <p v-if="message">{{ message }}</p>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';
import { GreeterClient } from '@/GreeterServiceClientPb';
import { HelloRequest } from '@/greeter_pb';

export default defineComponent({
  name: 'HelloWorld',
  setup() {
    const name = ref('');
    const message = ref('');
    const client = new GreeterClient('http://localhost:8080'); // gRPC-web 代理服务器的地址

    const sayHello = () => {
      const request = new HelloRequest();
      request.setName(name.value);

      client.sayHello(request, {}, (err, response) => {
        if (err) {
          console.error(err);
          message.value = 'Error: ' + err.message;
        } else {
          message.value = response.getMessage();
        }
      });
    };

    return {
      name,
      message,
      sayHello,
    };
  },
});
</script>

代码解释:

  • 引入生成的 gRPC 客户端类 GreeterClient 和消息类 HelloRequest
  • 创建一个 GreeterClient 实例,指定 gRPC-web 代理服务器的地址。
  • 创建一个 HelloRequest 实例,并设置 name 字段。
  • 调用 client.sayHello 方法,传入请求对象、元数据(这里为空对象)和一个回调函数。
  • 在回调函数中处理响应或错误。

7. 服务端实现 (Go 语言示例)

这里提供一个简单的 Go 语言实现的 gRPC 服务端示例:

package main

import (
    "context"
    "fmt"
    "log"
    "net"

    pb "example.com/greeter" // 替换为你的 .proto 文件生成的 Go 包名

    "google.golang.org/grpc"
)

const (
    port = ":50051"
)

type server struct {
    pb.UnimplementedGreeterServer
}

func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
    log.Printf("Received: %v", in.GetName())
    return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}

func main() {
    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    s := grpc.NewServer()
    pb.RegisterGreeterServer(s, &server{})
    fmt.Printf("Server listening at %v", lis.Addr())
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

生成 Go 代码:

go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative greeter.proto

确保将 greeter.proto 文件中的 package 名称与 Go 代码中的包名匹配。 (在上面的例子中,需要修改pb "example.com/greeter"

8. 运行项目

  1. 启动 gRPC 服务端 (Go)。
  2. 启动 gRPC-web 代理服务器 (grpcwebproxy)。
  3. 运行 Vue 项目。

在浏览器中访问 Vue 项目,输入你的名字,点击 "Say Hello" 按钮,你应该能看到服务端返回的问候语。

9. 遇到的问题及解决方案

  • CORS 问题: 如果你没有配置 allow_all_origins: true,可能会遇到 CORS (跨域资源共享) 问题。 在生产环境中,你应该配置更严格的 CORS 策略,只允许特定的域名访问你的 gRPC-web 代理服务器。 另一种解决方案是使用 grpcweb 传输模式(二进制格式),并配置 Envoy 或 grpcwebproxy 来处理 CORS。

  • 错误信息: 如果遇到错误,请仔细检查以下几点:

    • gRPC 服务端是否正在运行。
    • gRPC-web 代理服务器是否正在运行,并且配置正确。
    • Vue 项目中 gRPC 客户端的地址是否正确。
    • .proto 文件是否正确,并且生成的代码与服务端代码是否匹配。
  • protobuf 版本问题 不同的protobuf版本可能导致生成代码不兼容。确保你使用的 protoc, protoc-gen-go, protoc-gen-go-grpc, grpc-web 版本一致。

10. 关于流式 gRPC-web

gRPC-web 支持服务端流、客户端流和双向流。 要在 Vue 项目中使用流式 gRPC-web,你需要使用相应的 gRPC-web 客户端 API。 例如,可以使用 client.serverStreamingMethod(request, metadata) 方法来调用服务端流方法。 你需要使用 Stream 对象来接收服务端发送的数据。

11. 替代方案:Twirp

除了 gRPC-web,还有一种替代方案叫做 Twirp。 Twirp 是一种简单的 RPC 框架,它使用 Protocol Buffers 作为接口定义语言,并使用 HTTP/1.1 和 JSON 作为传输协议。 Twirp 比 gRPC-web 更简单,更容易集成到 Web 应用程序中。 但是,Twirp 的性能不如 gRPC-web,因为它使用 JSON 而不是二进制格式。 选择 gRPC-web 还是 Twirp 取决于你的具体需求。 如果追求极致性能,并且对配置有一定的了解,选择 gRPC-web。如果对简单易用性有更高的要求,并且对性能要求不高,可以选择 Twirp。

12. 不同传输模式的比较

gRPC-web 提供了两种主要的传输模式:grpcwebtextgrpcweb

特性 grpcwebtext grpcweb
传输格式 Base64 编码的文本 二进制
浏览器支持 更好,几乎所有浏览器都支持 需要浏览器支持 CORS,配置更复杂
性能 稍差,因为需要进行 Base64 编码 更好,直接传输二进制数据
配置复杂度 较低,只需配置 gRPC-web 代理服务器 较高,需要配置 gRPC-web 代理服务器和 CORS

选择哪种传输模式取决于你的具体需求。 如果需要更好的浏览器兼容性,并且对性能要求不高,可以选择 grpcwebtext。 如果需要更好的性能,并且可以配置 CORS,可以选择 grpcweb

结尾

总而言之,通过集成 gRPC-web 和 Protocol Buffers,我们可以构建高性能、类型安全的 Web 应用程序,充分利用 gRPC 的优势。 希望本文能够帮助你更好地理解和使用 gRPC-web。

总结: 介绍了gRPC-web的优势,配置方法,以及与Vue项目的集成,并提供了服务端代码示例。同时讨论了可能遇到的问题以及替代方案和不同传输模式的比较。

更多IT精英技术系列讲座,到智猿学院

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注