>[success] # 客户端编写案例 实现一个简单 `web-grpc` 案例,使用 `webpack ` 构建项目 ~~~ . |-- dist | |-- bundle.js | |-- bundle.js.map | `-- index.html |-- package.json |-- pnpm-lock.yaml |-- protos | `-- greet.proto |-- src | |-- index.js | `-- proto | |-- greet_grpc_web_pb.js | `-- greet_pb.js |-- tsconfig.json `-- webpack.config.js ~~~ >[danger] ##### 准备 `proto` 文件 ~~~ syntax = "proto3"; // 指定使用proto3,如果不指定的话,编译器会使用proto2去编译 package greet; // 编译后的包名 message HelloRequest { // 定义请求对象类型 string name = 1; // string 类型name 字段1为 该成员编码时用1编号代替名字 } message HelloReply { // 定义响应对象类型 string message = 1; // string 类型name 字段1为 该成员编码时用1编号代替名字 } service Greeter { // 定义服务接口 //定义一个方法,SayHello 请求参数HelloRequest类型响应是HelloReply类型 rpc SayHello (HelloRequest) returns (HelloReply){}; } ~~~ >[danger] ##### 转换 `proto` 文件 * 进入`protos `文件夹 执行 ![](https://img.kancloud.cn/b4/30/b430d0728533742e1d8799ed40148bc2_1066x41.png) ~~~ protoc *.proto --js_out=import_style=commonjs:../src/proto --grpc-web_out=import_style=commonjs,mode=grpcwebtext:../src/proto ~~~ >[danger] ##### 安装 grpc-web ~~~ npm install google-protobuf @types/google-protobuf @improbable-eng/grpc-web --save ~~~ >[danger] ##### index.js 文件编写grpc 请求 ~~~ import { HelloRequest } from './proto/greet_pb.js' import { GreeterClient } from './proto/greet_grpc_web_pb.js' const url = '' const client = new GreeterClient(url) const helloRequest = new HelloRequest() console.log(helloRequest) helloRequest.setName('北京') const metadata = { 'Content-Type': 'application/grpc-web+proto' } // header头数据 client.sayHello(helloRequest, metadata, (err, response) => { console.log(err, response) }) ~~~ >[success] # 服务的搭建 ~~~ . |-- index.js |-- package.json `-- pnpm-lock.yaml ~~~ 使用 `node ` 作为客户端安装 ~~~ npm i @grpc/grpc-js @grpc/proto-loader ~~~ >[danger] ##### 客户端代码 ~~~ var PROTO_PATH = __dirname + '../../protos/greet.proto' var grpc = require('@grpc/grpc-js') var protoLoader = require('@grpc/proto-loader') var packageDefinition = protoLoader.loadSync(PROTO_PATH, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true, }) var hello_proto = grpc.loadPackageDefinition(packageDefinition).greet /** * Implements the SayHello RPC method. */ function sayHello(call, callback) { console.log(123) callback(null, { message: 'Hello ' + call.request.name }) } /** * Starts an RPC server that receives requests for the Greeter service at the * sample server port */ function main() { var server = new grpc.Server() server.addService(hello_proto.Greeter.service, { sayHello: sayHello }) server.bindAsync( '0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), () => { server.start() } ) } main() ~~~ >[success] # 配置ENYO 代理 * `enyoygrpcweb.yaml` ~~~ admin: access_log_path: /tmp/admin_access.log address: socket_address: { address: 0.0.0.0, port_value: 9901 } static_resources: listeners: - name: listener_0 address: socket_address: { address: 0.0.0.0, port_value: 10000 } filter_chains: - filters: - name: envoy.filters.network.http_connection_manager typed_config: '@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager codec_type: auto stat_prefix: ingress_http route_config: name: local_route virtual_hosts: - name: local_service domains: ['*'] routes: - match: { prefix: '/' } route: cluster: greeter_service max_stream_duration: grpc_timeout_header_max: 0s cors: allow_origin_string_match: - prefix: '*' allow_methods: GET, PUT, DELETE, POST, OPTIONS allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout max_age: '1728000' expose_headers: custom-header-1,grpc-status,grpc-message http_filters: - name: envoy.filters.http.grpc_web typed_config: '@type': type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb - name: envoy.filters.http.cors typed_config: '@type': type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors - name: envoy.filters.http.router typed_config: '@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router clusters: - name: greeter_service connect_timeout: 0.25s type: static http2_protocol_options: {} lb_policy: round_robin # win/mac hosts: Use address: host.docker.internal instead of address: localhost in the line below load_assignment: cluster_name: cluster_0 endpoints: - lb_endpoints: - endpoint: address: socket_address: address: xxxxx port_value: 50051 ~~~ node 服务启动的 `address`: 本机`ip` 和`port_value`: 端口 >[success] # 在docker 安装 envoy ~~~ docker pull envoyproxy/envoy-dev ~~~ 运行 ~~~ docker run --rm -it -p 9901:9901 -p 10000:10000 -v C:/xx/envoy/:/etc/envoy/ envoyproxy/envoy-dev -c /etc/envoy/enyoygrpcweb.yaml ~~~ >[info] ## 参考 [# gRPC四种模式、认证和授权实战演示,必赞](https://juejin.cn/post/6984210268871983118#heading-5) [gRPC Web 示例 ](https://www.vinsguru.com/grpc-web-example/) [前端 Web gRPC 实践和优化 文件较老1. google-protobuf + grpc-web-client 已经更改为了@improbable-eng/grpc-web](https://juejin.cn/post/6844903698351521805#heading-5)