準備材料如下
// helloworld.proto 文件 用于前后端定義接口規(guī)范
// 安裝 npm i grpc-web
//安裝proto解析工具 protoc-gen-grpc-web.exe 插件
protoc-gen-grpc-web下載地址
proto下載地址
安裝完成后诀诊,配置系統(tǒng)環(huán)境變量 path 將 bin文件夾所在目錄配置進去即可
將 .proto protoc.exe protoc-gen-grpc-web.exe 三個文件放置于同一文件夾內(nèi)
執(zhí)行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.在需要引用的頁面內(nèi)引用相關(guān)文件,代碼如下
<a-button type="primary" @click="get1">
import {HelloRequest, RepeatHelloRequest,HelloReply} from "./proto/helloworld_pb";
import { GreeterClient } from "./proto/helloworld_grpc_web_pb";
created(){
// 創(chuàng)建grpc-web客戶端吭产,填入enovy的地址
this.client = new GreeterClient('http://localhost:8080',null, null)
}
get1(){
// 創(chuàng)建請求參數(shù)并賦值
var request = new HelloRequest();
request.setName('World');
// 調(diào)用客戶端相應的grpc方法闸翅,發(fā)送grpc請求琳拨,并接受后臺發(fā)送回來的返回值
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());
}
});
}
即可通過點擊按鈕寸齐,進行g(shù)rpc請求的發(fā)送
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內(nèi)的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協(xié)議不同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對應
執(zhí)行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寫法筹燕,其他的自己百度
一個賊坑的注意事項
關(guān)于想在grpc-web中發(fā)送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