You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue.js+protobuf-ts调用Envoy代理的gRPC-web服务报错排查

问题:Vue3+TS中gRPC-Web调用出现503/连接重置错误

一、Proto文件定义

syntax = "proto3";
package extractor;

service Extract {
    rpc PutEntries (EntriesRequest) returns (EntriesResponse);
}

message EntriesRequest {
    repeated string value = 1;
}

message EntriesResponse {
    repeated string value = 1;
}

二、环境配置

  • gRPC服务运行在50051端口
  • Envoy代理运行在8080端口,已开启CORS
  • 外部客户端(Kreya/BloomRPC)可正常调用,但浏览器中调用报错
  • 前端、后端、代理均部署在同一机器

三、初始Vue3+TS组件代码

<script setup lang="ts">
import { ref } from "vue";
import { ExtractClient } from "./grpc/extractor.client";
import { GrpcWebFetchTransport } from "@protobuf-ts/grpcweb-transport";
import type { EntriesRequest, EntriesResponse } from "./grpc/extractor";
import type { GrpcWebOptions } from '@protobuf-ts/grpcweb-transport';

const HOST = "http://localhost:8080";
const TIMEOUT = Date.now() + 2000;

let extracted = ref(false);
const fileInput = ref<HTMLInputElement | null>(null);
let filename = ref("");
let data = ref("");

let options: GrpcWebOptions = {
  baseUrl: HOST,
  timeout: TIMEOUT,
  format: 'binary',
  meta: {}
};

const handleFileChange = (event: Event) => {
      const input = event.target as HTMLInputElement;
      fileInput.value = input;
    };

function extract() {
  if (!fileInput.value) {
    return;
  }
  const file = fileInput.value.files![0];
  filename.value = file.name;
  const reader = new FileReader();
  reader.readAsText(file);
  reader.onload = async () => {
    const transport = new GrpcWebFetchTransport(options);
    const client = new ExtractClient(transport);

    const content = reader.result as string;
    const entries = content.split("\n").filter((entry) => entry !== "");

    // Convert the entries to the EntriesRequest format
    let request: EntriesRequest = {
      value: entries
    }
    
    // Make the grpc-web call to the PutEntries endpoint
    let call = client.putEntries(request, options);
    let response: EntriesResponse = await call.response;
    data.value = response.value.join("\n");
    extracted.value = true;
  };
}
</script>

<template>
  <form>
    <input type="file" @change="handleFileChange" />
  </form>
  <button @click="extract">Extract</button>
  <br /><br />
  <span v-show="extracted">Should show data from {{ filename }}
    <br />{{ data }}
  </span>
</template>

四、更新后的情况

调整超时设置后,错误变为503 Uncaught (in promise) RpcError: Service Unavailable。测试文件小于1KB,外部客户端响应仅需2.1秒,更新后的extract方法代码如下:

function extract() {
  if (!fileInput.value) {
    return;
  }
  const file = fileInput.value.files![0];
  filename.value = file.name;
  const reader = new FileReader();
  reader.readAsText(file);
    reader.onload = async () => {

    const content = reader.result as string;
    const entries = content.split("\n").filter((entry) => entry !== "");
    // Convert the entries to the EntriesRequest format
    let request: EntriesRequest = {
      value: entries
    }

    // Configure Grpc-Web client
    let options: GrpcWebOptions = {
      baseUrl: HOST,
      timeout: Date.now() + 10000,
      format: 'binary',
      meta: {}
    };
    const transport = new GrpcWebFetchTransport(options);
    const client = new ExtractClient(transport);
    
    // Make the grpc-web call to the PutEntries endpoint
    let call = client.putEntries(request, options);
    let response: EntriesResponse = await call.response;
    data.value = response.value.join("\n");
    extracted.value = true;
  };
}

排查方向与解决方案

1. Envoy代理配置问题

  • 检查路由映射:确认Envoy路由的prefix是否匹配proto中的服务路径(如/extractor.Extract/),且cluster指向gRPC服务的50051端口。
  • 验证gRPC-Web过滤器:确保Envoy配置中启用了grpc_web过滤器,同时CORS的allow_origin覆盖前端本地开发域名(如http://localhost:5173)。
  • 查看Envoy日志:启动Envoy时开启调试日志,排查是否存在路由匹配失败、上游连接异常的具体信息。

2. gRPC-Web客户端配置错误

  • 修正超时参数:@protobuf-ts/grpcweb-transport的timeout参数是毫秒数,不是未来时间戳,应直接写10000(代表10秒),之前的Date.now() + 10000会导致参数无效。
  • 移除重复options传递:创建GrpcWebFetchTransport时已传入配置,调用client.putEntries时无需再次传递options,避免配置冲突。
  • 测试格式兼容性:尝试将format改为text,验证是否因格式不兼容导致调用失败(外部客户端能正常调用则大概率没问题,但可作为排查手段)。

3. 前端请求路径验证

通过浏览器开发者工具的Network面板,检查实际请求URL是否为http://localhost:8080/extractor.Extract/PutEntries,若路径错误,Envoy无法路由到后端服务。

4. 代码优化示例

修正上述问题后的extract方法:

function extract() {
  if (!fileInput.value) {
    return;
  }
  const file = fileInput.value.files![0];
  filename.value = file.name;
  const reader = new FileReader();
  reader.readAsText(file);
  reader.onload = async () => {
    const content = reader.result as string;
    const entries = content.split("\n").filter(entry => entry !== "");
    const request: EntriesRequest = { value: entries };

    // 修正超时参数,移除冗余配置
    const options: GrpcWebOptions = {
      baseUrl: HOST,
      timeout: 10000,
      format: 'binary'
    };
    const transport = new GrpcWebFetchTransport(options);
    const client = new ExtractClient(transport);
    
    try {
      // 调用时不再重复传递options
      const call = client.putEntries(request);
      const response: EntriesResponse = await call.response;
      data.value = response.value.join("\n");
      extracted.value = true;
    } catch (err) {
      // 增加错误捕获,方便调试
      console.error("gRPC调用失败:", err);
    }
  };
}

内容的提问来源于stack exchange,提问作者staticdev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 23:16:13