前端调用grpc

在 nodejs 中使用 grpc 编写接口

1. 创建一个 user.proto 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
syntax = "proto3";

package user; // 包名称

// 请求参数
message LoginRequest {
string username = 1;
string password = 2;
}

// 响应结果
message LoginResponse {
string access_token = 1;
int32 expires = 2;
}

// 用户相关接口
service User {
// 登录函数
rpc login(LoginRequest) returns (LoginResponse);
}

2. 安装相关依赖

1
npm i @grpc/grpc-js google-protobuf grpc-tools grpc_tools_node_protoc_ts

3. 生成 user_pb.js 和 user_grpc_pb.js 文件

1
grpc_tools_node_protoc --js_out=import_style=commonjs,binary:./ --grpc_out=./ --plugin=protoc-gen-grpc=`which grpc_tools_node_protoc_plugin` ./src/user.proto

4. 生成 user_pb.d.ts 和 user_grpc_pb.d.ts 文件

1
grpc_tools_node_protoc --plugin=protoc-gen-ts=`which protoc-gen-ts` --ts_out=./src --proto_path=./src ./src/user.proto

5.编写 server.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import * as grpc from "@grpc/grpc-js";
import { IUserServer, UserService } from "../proto/user_grpc_pb";
import messages from "../proto/user_pb";

// User Service 的实现
const userServiceImpl: IUserServer = {
// 实现登录接口
login(call: any, callback: any) {
const { request } = call;
const username = request.getUsername();
const password = request.getPassword();

const response = new messages.LoginResponse();
response.setAccessToken(`username = ${username}; password = ${password}`);
response.setExpires(7200);
callback(null, response);
}
}

function main() {
const server = new grpc.Server();

// UserService 是定义,UserImpl 是实现
server.addService(UserService as any, userServiceImpl as any);
server.bindAsync(
"0.0.0.0:8081",
grpc.ServerCredentials.createInsecure(),
() => {
server.start();
console.log("grpc server started");
}
);
}

main();

6.编写 client.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import * as grpc from "@grpc/grpc-js";
import { UserClient } from "../proto/user_grpc_pb";
import messages from "../proto/user_pb";

const client = new UserClient(
"localhost:8081",
grpc.credentials.createInsecure()
);

// 发起登录请求
const login = () => {
return new Promise((resolve, reject) => {
const request = new messages.LoginRequest();
request.setUsername('zhang');
request.setPassword("123456");

client.login(request, function (err: any, response: any) {
if (err) {
reject(err);
} else {
resolve(response.toObject());
}
});
})
}

async function main() {
const data = await login()
console.log(data)
}

main();

在 React 中调用 grpc 接口

1.在服务器端安装 envoy 开启反向代理

1.1 打开 envoy.yaml 配置文件

1
sudo nano /etc/envoy/envoy.yaml

1.2 编写 envoy.yaml 配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
static_resources:
listeners:
- name: listener_0
address:
socket_address: { address: 0.0.0.0, port_value: 9091}
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
load_assignment:
cluster_name: cluster_0
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 0.0.0.0
port_value: 8081

1.3 开启 envoy 服务

1
sudo envoy -c /etc/envoy/envoy.yaml

2.在 react 中引入 proto 文件并生成相关代码

2.1 生成 user_grpc_web_pb.js 和 user_grpc_web_pb.d.ts 文件

1
protoc --grpc-web_out=import_style=commonjs+dts,mode=grpcwebtext:. user.proto

或者可以直接生成 ts 文件

1
protoc --grpc-web_out=import_style=typescript,mode=grpcwebtext:. user.proto

2.2 生成 user_pb.js 和 user_pb.d.ts 安装相关依赖

具体代码在上方

3 编写 App.jsx

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import React, { memo } from "react";
import { UserClient } from "../proto/user_grpc_web_pb";
import { LoginRequest } from "../proto/user_pb";

const App = memo(() => {
const client = new UserClient("http://localhost:9091");

export const Login = () => {
return new Promise((resolve, reject) => {
const request = new LoginRequest();

request.setUsername("zhang");
request.setPassword("123456");

client.login(request, {}, function (err, response) {
if (err) {
reject(err);
} else {
resolve(response.toObject());
}
});
});
};
return <div>App</div>;
});
export default App;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
module.exports = {
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint/eslint-plugin", "eslint-plugin-import"],
extends: ["alloy", "alloy/typescript"],
settings: {
react: {
version: "detect",
},
},
rules: {
quotes: "error",
semi: "error",
"max-params": "off",
"guard-for-in": "off",
"spaced-comment": "off",
"object-curly-spacing": ["error", "always"],
"@typescript-eslint/no-parameter-properties": "off",
"@typescript-eslint/explicit-member-accessibility": "off",
"@typescript-eslint/no-invalid-void-type": "off",
},
};

其他

1
2
3
4
5
6
7
8
9
10
11
12
13
git submodule init
git submodule update --remote --forck
export NODE_OPTIONS='--max-old-space-size=4096'

ssh mimikko@192.168.2.22

/home/mimikko/Server/envoy/yaml

nohup envoy -c aggregation.yaml &

C:\Windows\System32\drivers\etc

npm install --legacy-peer-deps