Skip to content

Commit aeb1843

Browse files
committed
feat: init tl-ngrpc project
0 parents  commit aeb1843

17 files changed

+733
-0
lines changed

LICENSE

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
MIT License
2+
Copyright (c) <year> <copyright holders>
3+
4+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5+
6+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7+
8+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

+85
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# tl-ngrpc
2+
3+
极简node-grpc依赖包,无关业务,只做规约, 支持两种服务端,http服务, socket服务
4+
5+
### server
6+
7+
实体模型 Teacher 为例, 编写proto文件
8+
9+
```proto
10+
// teacher.proto
11+
syntax = "proto3";
12+
13+
package api;
14+
15+
service Teacher {
16+
rpc hello (Request) returns (Response) {}
17+
}
18+
message Request {
19+
string message = 1;
20+
}
21+
message Response {
22+
string message = 1;
23+
}
24+
```
25+
26+
**启动服务**
27+
28+
```js
29+
const TlApiRpcServer = require("xx/xx/TlApiRpcServer");
30+
const path = require("path")
31+
32+
const teacherApiRpcServer = new TlApiRpcServer({
33+
protoPath: path.resolve(__dirname, '../proto/api/teacher.proto'), //proto文件路径
34+
protoName : "teacher", //proto文件名称
35+
protoPackage : "api", //proto文件定义的package
36+
protoImpl : "Teacher", //proto定义的实体结构
37+
ip : "127.0.0.1", //服务监听ip
38+
port : 50051 //服务监听端口
39+
});
40+
41+
//启动服务端
42+
teacherApiRpcServer
43+
//监听接口事件,并定义处理逻辑
44+
.on("hello", (call, callback) => {
45+
console.log("服务端收到请求hello, 开始处理 : ", call.request.message);
46+
callback(null, {message: 'Hello ' + call.request.message});
47+
})
48+
//监听接口事件,并定义处理逻辑
49+
.on("hello1", (call, callback) => {
50+
console.log("服务端收到请求hello1, 开始处理 : ", call.request.message);
51+
callback(null, {message: 'Hello1 ' + call.request.message});
52+
})
53+
//启动服务
54+
.start();
55+
```
56+
57+
58+
### client
59+
60+
```js
61+
const TlApiRpcClient = require("../client/api/TlApiRpcClient");
62+
const path = require("path")
63+
64+
const teacherApiRpcClient = new TlApiRpcClient({
65+
protoPath: path.resolve(__dirname, '../proto/api/teacher.proto'), //proto文件路径
66+
protoName : "teacher", //proto文件名称
67+
protoPackage : "api", //proto文件定义的package
68+
protoImpl : "Teacher", //proto定义的实体结构
69+
ip : "127.0.0.1", //服务监听ip
70+
port : 50051 //服务监听端口
71+
});
72+
73+
//启动客户端
74+
(async ()=>{
75+
//调用服务接口
76+
let res = await teacherApiRpcClient.emit("hello", {message : "i am hello"})
77+
console.log("客户端收到hello结果 : ", res);
78+
79+
//调用服务接口
80+
res = await teacherApiRpcClient.emit("hello1", {message : "i am hello1"})
81+
console.log("客户端收到hello1结果 : ", res);
82+
})()
83+
```
84+
85+
#### socket服务同理,用法完全一致

TlRpc.js

+117
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
const { loadProtoInstance, modeEnum } = require("./comm/comm");
2+
const TlRpcConfig = require("./config/TlRpcConfig");
3+
4+
/**
5+
* rpc-client, rpc-server通用类
6+
* 负责proto定义的加载和初始化
7+
* @version 1.0.0
8+
* @author iamtsm
9+
*/
10+
class TlRpc {
11+
/**
12+
* -- 私有属性
13+
* api proto实例
14+
*/
15+
static #apiProtoInstance = new Map();
16+
17+
/**
18+
* -- 私有属性
19+
* socket proto实例
20+
*/
21+
static #socketProtoInstance = new Map();
22+
23+
/**
24+
* -- 公共属性
25+
* rpc 配置
26+
*/
27+
static #configInstance = new TlRpcConfig({
28+
protoName : "", protoPackage : "", protoImpl : "", ip : "", port : ""
29+
});
30+
31+
/**
32+
* 初始化
33+
*/
34+
constructor(rpcConfig) {
35+
TlRpc.#configInstance = new TlRpcConfig(rpcConfig);
36+
}
37+
38+
/**
39+
* -- 私有方法
40+
* 获取proto文件对象
41+
* @returns proto instance
42+
*/
43+
#protoInstance() {
44+
return loadProtoInstance(TlRpc.#configInstance.protoPath)
45+
}
46+
47+
/**
48+
* 获取缓存proto instance key
49+
* @returns
50+
*/
51+
#getCacheProtoKey(){
52+
return TlRpc.#configInstance.protoName + "-" + TlRpc.#configInstance.protoPackage;
53+
}
54+
55+
/**
56+
* -- 私有方法
57+
* 从缓存获取proto对象
58+
* @returns proto cache instance
59+
*/
60+
#cacheProtoInstance() {
61+
let instance = null;
62+
const cacheKey = this.#getCacheProtoKey();
63+
64+
if (TlRpc.#configInstance.mode === modeEnum.API) {
65+
instance = TlRpc.#apiProtoInstance.get(cacheKey);
66+
if (!instance) {
67+
instance = this.#protoInstance();
68+
}
69+
TlRpc.#apiProtoInstance.set(cacheKey, instance)
70+
} else if (TlRpc.#configInstance.mode === modeEnum.SOCKET) {
71+
instance = TlRpc.#socketProtoInstance.get(cacheKey);
72+
if (!instance) {
73+
instance = this.#protoInstance();
74+
}
75+
TlRpc.#socketProtoInstance.set(cacheKey, instance)
76+
}
77+
78+
return instance;
79+
}
80+
81+
/**
82+
* -- 私有方法
83+
* 获取package对象
84+
* @returns package instance
85+
*/
86+
#packageInstance(){
87+
const instance = this.#cacheProtoInstance();
88+
if (!instance) {
89+
return null;
90+
}
91+
return instance[TlRpc.#configInstance.protoPackage];
92+
}
93+
94+
/**
95+
* -- 私有方法
96+
* 获取实例化impl对象
97+
* @returns protoImpl instance
98+
*/
99+
implInstance() {
100+
const instance = this.#packageInstance();
101+
if (!instance) {
102+
return null;
103+
}
104+
return instance[TlRpc.#configInstance.protoImpl];
105+
}
106+
107+
/**
108+
* -- 对外提供
109+
* 获取rpc配置
110+
* @returns
111+
*/
112+
getConfig(){
113+
return TlRpc.#configInstance;
114+
}
115+
}
116+
117+
module.exports = TlRpc;

TlRpcClient.js

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
const { credentials } = require('@grpc/grpc-js');
2+
const TlRpc = require("./TlRpc");
3+
4+
/**
5+
* rpc通用client
6+
* @version 1.0.0
7+
* @author iamtsm
8+
*/
9+
class TlRpcClient extends TlRpc{
10+
/**
11+
* -- 私有属性
12+
* client实例
13+
*/
14+
static #clientInstance = new Map();
15+
16+
/**
17+
* 初始化
18+
* @param {*} rpcConfig
19+
*/
20+
constructor( rpcConfig ) {
21+
super( rpcConfig )
22+
}
23+
24+
/**
25+
* 返回client缓存对象的key
26+
*/
27+
#clientInstanceKey() {
28+
const config = this.getConfig();
29+
return config.name + "-" + config.protoPackage + "-" + config.protoImpl + "-" + config.ip + "-" + config.port;
30+
}
31+
32+
/**
33+
* 获取client对象实例
34+
* @returns
35+
*/
36+
clientInstance(){
37+
const instance = this.implInstance();
38+
if (!instance) {
39+
return null;
40+
}
41+
42+
const clientKey = this.#clientInstanceKey();
43+
const client = TlRpcClient.#clientInstance.get(clientKey)
44+
if (client) {
45+
return client;
46+
}
47+
48+
const config = this.getConfig();
49+
const newClient = new instance(
50+
config.ip + ":" + config.port,
51+
credentials.createInsecure(),
52+
{ 'grpc.service_config': '{"loadBalancingConfig": [{"round_robin": {}}]}' },
53+
)
54+
55+
console.log("create client instance for " + clientKey + " ok!")
56+
57+
TlRpcClient.#clientInstance.set(clientKey, newClient);
58+
59+
return newClient;
60+
}
61+
}
62+
63+
module.exports = TlRpcClient;

TlRpcServer.js

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
const TlRpc = require("./TlRpc")
2+
const grpc = require('@grpc/grpc-js');
3+
4+
/**
5+
* rpc通用server
6+
* @version 1.0.0
7+
* @author iamtsm
8+
*/
9+
class TlRpcServer extends TlRpc{
10+
11+
/**
12+
* 初始化
13+
* @param {*} rpcConfig
14+
*/
15+
constructor(rpcConfig) {
16+
super(rpcConfig)
17+
}
18+
19+
/**
20+
* -- 对外提供
21+
* 获取实例化service接口列表对象
22+
* @returns service instance
23+
*/
24+
#serviceDefinition() {
25+
const instance = this.implInstance();
26+
if (!instance) {
27+
return null;
28+
}
29+
return instance.service;
30+
}
31+
32+
/**
33+
* 启动服务
34+
* 启动时需要添加相应的启动函数
35+
* @returns
36+
*/
37+
async startServer(handlerMap = {}){
38+
if(Object.keys(handlerMap).length === 0){
39+
return null;
40+
}
41+
console.log(handlerMap)
42+
43+
const server = new grpc.Server();
44+
const config = this.getConfig();
45+
return await new Promise(resolve => {
46+
server.addService(this.#serviceDefinition(), handlerMap);
47+
server.bindAsync(
48+
config.ip + ":" + config.port,
49+
grpc.ServerCredentials.createInsecure(), () => {
50+
server.start();
51+
console.log(`${JSON.stringify(config)} server start!`)
52+
resolve(handlerMap)
53+
}
54+
);
55+
})
56+
}
57+
}
58+
59+
module.exports = TlRpcServer;

client/api/TlApiRpcClient.js

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
const TlRpcClient = require("../../TlRpcClient");
2+
const { modeEnum, versionEnum } = require("../../comm/comm");
3+
4+
/**
5+
* rpc api模式client
6+
* @version 1.0.0
7+
* @author iamtsm
8+
*/
9+
class TlApiRpcClient extends TlRpcClient{
10+
/**
11+
* -- 公共属性
12+
* api调用版本
13+
*/
14+
version = versionEnum.V1;
15+
16+
/**
17+
* -- 私有属性
18+
* client实例
19+
*/
20+
#instanceClient = this.getClient();
21+
22+
/**
23+
* 初始化
24+
* @param {*} rpcConfig
25+
*/
26+
constructor(rpcConfig){
27+
super(Object.assign(rpcConfig, {mode : modeEnum.API}));
28+
}
29+
30+
/**
31+
* 获取client请求对象
32+
* @returns
33+
*/
34+
getClient(){
35+
return this.clientInstance();
36+
}
37+
38+
/**
39+
* -- 对外提供
40+
* 发送请求
41+
* @param {*} api
42+
* @param {*} requestArgs
43+
* @returns
44+
*/
45+
async emit(api, requestArgs){
46+
return await new Promise((resolve, reject) => {
47+
this.#instanceClient[api](requestArgs, function(err, response){
48+
if(err){ reject(err); } else { resolve(response) }
49+
});
50+
});
51+
}
52+
}
53+
54+
module.exports = TlApiRpcClient;

0 commit comments

Comments
 (0)