准备材料如下
// helloworld.proto 文件 用于前后端定义接口规范
// 安装 npm i grpc-web
//安装proto解析工具 protoc-gen-grpc-web.exe 插件
protoc-gen-grpc-web下载地址
proto下载地址
安装完成后,配置系统环境变量 path 将 bin文件夹所在目录配置进去即可
将 .proto protoc.exe protoc-gen-grpc-web.exe 三个文件放置于同一文件夹内
执行cmd命令
protoc helloworld.proto --js_out=import_style=commonjs:.\ --grpc-web_out=import_style=commonjs,mode=grpcwebtext:.\
可得到如下两个文件
---helloworld_pb.js
---helloworld_grpc_web_pb.js
Client
vue项目中如何使用这两个文件搭建client端
1.项目根目录中新建proto文件夹,并将文件粘贴进去
1.5.npm 安装 grpc-web
2.在需要引用的页面内引用相关文件,代码如下
<a-button type="primary" @click="get1">
import {HelloRequest, RepeatHelloRequest,HelloReply} from "./proto/helloworld_pb";
import { GreeterClient } from "./proto/helloworld_grpc_web_pb";
created(){
// 创建grpc-web客户端,填入enovy的地址
this.client = new GreeterClient('http://localhost:8080',null, null)
}
get1(){
// 创建请求参数并赋值
var request = new HelloRequest();
request.setName('World');
// 调用客户端相应的grpc方法,发送grpc请求,并接受后台发送回来的返回值
this.client.sayHello(request, {}, (err, response) => {
if (err) {
console.log(`Unexpected error for sayHello: code = ${err.code}` +
`, message = "${err.message}"`);
} else {
// 打印返回的信息
console.log(response.getMessage());
}
});
}
即可通过点击按钮,进行grpc请求的发送
server
var PROTO_PATH = __dirname + '/helloworld.proto';
var assert = require('assert');
var async = require('async');
var _ = require('lodash');
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 protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
var helloworld = protoDescriptor.helloworld;
// 这里的.helloworld其实是proto内的package名字
/**
* @param {!Object} call
* @param {function():?} callback
*/
function doSayHello(call, callback) {
callback(null, {message: 'Hello! '+ call.request.name});
}
/**
* @param {!Object} call
*/
function doSayRepeatHello(call) {
var senders = [];
function sender(name) {
return (callback) => {
call.write({
message: '123Hey! ' + name,
});
_.delay(callback, 500); // in ms
};
}
console.log(call.request)
for (var i = 0; i < call.request.count; i++) {
senders[i] = sender(call.request.name + i);
}
async.series(senders, () => {
call.end();
});
}
/**
* @return {!Object} gRPC server
*/
function getServer() {
var server = new grpc.Server();
console.log(helloworld)
server.addService(helloworld.Greeter.service, {
sayHello: doSayHello,
sayRepeatHello: doSayRepeatHello,
});
return server;
}
//这里的9090 是因为要通过代理从而访问
//grpc协议不同http2,所以需要配置代理才可进行访问,详情可以百度
if (require.main === module) { // 是否为node直接启动的服务
var server = getServer();
server.bindAsync(
'0.0.0.0:9090', grpc.ServerCredentials.createInsecure(), (err, port) => {
assert.ifError(err);
server.start();
console.log("服务器已启动")
});
}
exports.getServer = getServer;
代理
代理配置文件 envoy.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: 8080 }
//这里这个8080和客户端对应
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
- name: envoy.filters.http.cors
- name: envoy.filters.http.router
clusters:
- name: greeter_service
connect_timeout: 0.25s
type: logical_dns
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: host.docker.internal
//这个address为doctor启动代理所采用配置,如果不是请自己百度
port_value: 9090
//这里这个9090和server对应
执行doctor命令
docker run --rm -it -v "C:/Users/liujiaji/Downloads/grpc-web-master (1)/grpc-web-master/net/grpc/gateway/examples/helloworld/envoy.yaml:/envoy-custom.yaml" -p 9901:9901 -p 8080:8080 envoyproxy/envoy-dev:latest -c /envoy-custom.yaml
//这个 -p9901:9901 -p 8080:8080 为doctor写法,其他的自己百度
一个贼坑的注意事项
关于想在grpc-web中发送token于header里
需要修改代理文件中的配置,使其allow-headers中有想加入的值
cors:
allow_origin_string_match:
- prefix: "*"
allow_methods: GET, PUT, DELETE, POST, OPTIONS
allow_headers: Bearer,token,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
allow_headers: Bearer,token