日常记录一下项目中grpc的应用
一.准备工作
1、安装grpc-web和google-protobuf,如下
npm i grpc-web
npm i google-protobuf
2、安装 protoc ,选择适用于自己系统的版本安装
配置环境变量,以上下载安装/解压完后,在系统设置中配置环境变量,具体步骤如下:
右键我的电脑,选择属性,在弹出的页面中选择“高级系统设置”,
选择环境变量
在系统变量里找到“Path”,选择编辑,添加路径指向上述安装的文件夹bin文件
检验protoc是否安装成功:
3、安装 protoc-gen-grpc-web (用于生成web.js的工具),选择适用于自己系统的版本安装
或者直接执行
npm i -g protoc-gen-js protoc-gen-grpc-web
二.编写测试demo
我是在src文件夹下新建了proto文件夹,编写example.proto文件:
// 步骤 1. 基本配置
// ================================================ ====
// 第一行告诉编译器这个文件中使用了什么语法。
// 第二行属于命名空间,用来防止不同的消息类型有命名冲突
syntax = "proto3";
package example;
// 步骤 2. 定义消息结构
// ================================================ ====
// 这定义了请求负载。 此处进入消息的每个属性都与其类型一起定义。
// 需要为每个属性分配一个唯一的编号,称为标签。 协议缓冲区使用此标记来表示属性,而不是使用属性名称。
// 所以,不像 JSON 我们每次都会传递属性名称 name,protocol buffer 会使用数字 1 来表示 name。 响应负载定义类似于请求。
message ExampleRequest {
string name = 1;
}
message ExampleResponse {
string message = 1;
}
// 步骤 3. 定义服务契约
// ================================================ ====
// 最后,让我们定义服务契约。 对于我们的 HelloService,我们定义了一个 GetHelloReq() 操作:
service ExampleService {
rpc GetExampleMessage(ExampleRequest) returns (ExampleResponse);
}
进入当前文件夹下图执行命令生成文件,用于在 Vue 组件中调用 gRPC 服务。:
example_grpc_web_pb.js
example_pb.js
protoc --proto_path=. --js_out=import_style=commonjs,binary:. --grpc-web_out=import_style=commonjs,mode=grpcwebtext:. example.proto
--proto_path指proto文件所在的目录,如果是当前目录,用“.”表示
--js_out=import_style=commonjs指利用commonjs编译js文件
--grpc-web_out=import_style=commonjs,mode=grpcwebtext指利用commonjs编译后的grpc-web的js文件模式是grpcwebtext
如果想要把生成的js文件存储到其他文件夹,在当前example.proto目录下执行:
protoc --proto_path=. --js_out=import_style=commonjs,binary:./../../proto/ --grpc-web_out=import_style=commonjs,mode=grpcwebtext:./../../proto/ example.proto
指定生成的文件放在 ./../../proto/目录中。
三、 在 Vue 组件中调用 gRPC 服务:
在需要调用 gRPC 服务的 Vue 组件中导入生成的 gRPC-Web 客户端代码,并使用它来调用 gRPC 服务。例如,在 App.vue
组件中调用 GetExampleMessage
方法:
import { ExampleServiceClient } from './proto/example_grpc_web_pb';
import { ExampleRequest } from './proto/example_pb';
export default {
name: 'App',
methods: {
callGrpcService() {
const request = new ExampleRequest();
request.setName('John');
const client = new ExampleServiceClient('http://localhost:8080', null, null);
client.getExampleMessage(request, {}, (err, response) => {
if (err) {
console.error('Error:', err.message);
} else {
console.log('Response:', response.getMessage());
}
});
},
},
};
四、实际场景中的应用:
1、SwitchService.proto文件代码:
// Copyright 2015 The gRPC Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
syntax = "proto3";
//import "google/api/annotations.proto";
option java_multiple_files = true;
option java_package = "SwitchService";
option java_outer_classname = "SwitchServiceRpc";
package SwitchService;
service SwitchApi
{
rpc execRpcCommandSync(Request) returns (Reply);
rpc execRpcCommand(Request) returns (Reply);
rpc sendHeartbeat(HeartbeatRequest) returns (HeartbeatResponse);
}
message HeartbeatRequest {
string request = 1;
}
message HeartbeatResponse {
string rply = 1;
}
message Request
{
string request = 1;
}
message Reply
{
string rply = 1;
}
2、使用上面(二)中的方法生成js文件SwitchService_pb、SwitchService_grpc_web_pb
3、封装方法公共方法:
import {Request, Reply} from "@/proto/SwitchService_pb";
import { SwitchApiClient } from "@/proto/SwitchService_grpc_web_pb";
import { Message } from "element-ui";
import store from '@/store'
// 执行指令获取grpc数据
export function executeInstructions(url, Id, commandType, otherParameter) {
return new Promise(function(resolve){
// 创建grpc-web客户端,填入enovy的地址
var client = new SwitchApiClient(url,null, null)
// 创建请求参数并赋值
var request = new Request();
//固定指令传参
var data = {
Id:'',
commandType:commandType,//指令id
utcTime:getUtcTime(),//当前utc时间
timeStamp:getTimeStamp()//当前时间戳
}
//如果传入ID,取传入ID,否则生成guid
if(Id){
data.Id = Id
}else{
data.Id = getGUID()
}
var allParameter = Object.assign(data,otherParameter)
// 数据转换
var s = JSON.stringify(allParameter)
console.log('grpc传参+++++++++++++++++++++++++:'+s)
request.setRequest(s);
// 调用客户端相应的grpc方法,发送grpc请求,并接受后台发送回来的返回值
// TODO : 换方法
client.execRpcCommand(request, {}, (err, response) => {
console.log('grpc返回---------------------:'+response)
if (err) {//grpc链接异常
var errMessage = `Unexpected error for execRpcCommand: code = ${err.code}` +`, message = "${err.message}"`
response = {
code:'error',
data:errMessage
}
// handle(response)
// if (errMessage.indexOf('400') !== -1) {
// Message.warning(errMessage);
// }
resolve(response)
} else {//grpc链接正常
// 数据解析
var parsingData = JSON.parse(response.array[0])
if(parsingData.success){//请求正常
if(parsingData.content){//非空
try {//判断是否是json
parsingData.content = JSON.parse(parsingData.content)
response = {
code:'success',
data:parsingData
}
} catch (e) {//不是json
response = {
code:'success',
data:parsingData.content
}
}
}else{
response = {
code:'success',
data:'成功'
}
}
}else{//请求异常
response = {
code:'error',
data:parsingData.content
}
}
resolve(response)
// handle(response)
}
})
});
}
// 发送指令sendHeartbeat方法
export function sendHeartbeatInstructions(url,Id,commandType,otherParameter){
return new Promise(function(resolve){
// 创建grpc-web客户端,填入enovy的地址
var client = new SwitchApiClient(url,null,null)
// 创建请求参数并赋值
var request = new Request();
//固定指令传参
var data = {
Id:'',
commandType:commandType,//指令id
utcTime:getUtcTime(),//当前utc时间
timeStamp:getTimeStamp()//当前时间戳
}
//如果传入ID,取传入ID,否则生成guid
if(Id){
data.Id = Id
}else{
data.Id = getGUID()
}
// console.log(data)
var allParameter = Object.assign(data,otherParameter)
// 数据转换
var s = JSON.stringify(allParameter)
// console.log('grpc传参+++++++++++++++++++++++++:'+s)
request.setRequest(s);
// 调用客户端相应的grpc方法,发送grpc请求,并接受后台发送回来的返回值
// TODO : 换方法
let time1 =new Date().getTime()
client.sendHeartbeat(request, {}, (err, response) => {
let time2 =new Date().getTime()
// console.log('grpc返回---------------------:'+response)
if (err) {//grpc链接异常
var errMessage = `Unexpected error for sendHeartbeat: code = ${err.code}` +`, message = "${err.message}"`
response = {
code:'error',
data:errMessage,
time: time2-time1
}
// handle(response)
// Message.warning(errMessage);
response = {
code:'error',
data:{},
rowData:otherParameter,
time: time2-time1
}
resolve(response)
// reject(response)
} else {//grpc链接正常
// 数据解析
var parsingData = JSON.parse(response.array[0])
response = {
code:'success',
data:parsingData,
rowData:otherParameter,
time: time2-time1
}
resolve(response)
}
})
});
}
4、页面中使用:
this.$grpcFun.executeInstructions(this.$store.state._grpcurl, "", 46, {}).then((response) => {
if (response.code == "success") {
**********************
自己的方法
**********************
}else{
this.$message.error("错误!")
}
})