diff --git a/README.md b/README.md index e294903..c3f1869 100644 --- a/README.md +++ b/README.md @@ -41,19 +41,23 @@ exports.grpc = { ```js // {app_root}/config/config.default.js exports.grpc = { - endpoint: 'localhost:50051', - // dir: 'app/proto', // proto files dir, relative path - // property: 'grpc', // default attach to `ctx.grpc.**` - // loadOpts: { convertFieldsToCamelCase: true, }, // message field case: `string user_name` -> `userName` - // clientSsl: { - // enable: false, - // grpc.credentials.createSsl - // rootCerts: 'config/cert/server.crt', - // options: { - // "grpc.ssl_target_name_override": 'example.server', - // "grpc.default_authority": 'example.server' - // } - // } + clients: { + grpc1: { + property: 'grpc1', // default attach to `ctx.grpc.**` + endpoint: 'localhost:50051', + // dir: 'app/proto', // proto files dir, relative path + // loadOpts: { convertFieldsToCamelCase: true }, // message field case: `string user_name` -> `userName` + clientSsl: { + enable: false, + // grpc.credentials.createSsl + rootCerts: 'config/cert/server.crt', + options: { + 'grpc.ssl_target_name_override': 'example.server', + 'grpc.default_authority': 'example.server', + }, + }, + }, + }, }; ``` diff --git a/README.zh_CN.md b/README.zh_CN.md index dffb3a7..8d4840b 100644 --- a/README.zh_CN.md +++ b/README.zh_CN.md @@ -41,19 +41,23 @@ exports.grpc = { ```js // {app_root}/config/config.default.js exports.grpc = { - endpoint: 'localhost:50051', - // dir: 'app/proto', // proto 文件目录,相对路径 - // property: 'grpc', // 默认挂载到 `ctx.grpc.**` - // loadOpts: { convertFieldsToCamelCase: true, }, // message field case: `string user_name` -> `userName` - // clientSsl: { - // enable: false, - // grpc.credentials.createSsl - // rootCerts: 'config/cert/server.crt', - // options: { - // "grpc.ssl_target_name_override": 'example.server', - // "grpc.default_authority": 'example.server' - // } - // } + clients: { + grpc1: { + property: 'grpc1', // default attach to `ctx.grpc.**` + endpoint: 'localhost:50051', + // dir: 'app/proto', // proto files dir, relative path + // loadOpts: { convertFieldsToCamelCase: true }, // message field case: `string user_name` -> `userName` + clientSsl: { + enable: false, + // grpc.credentials.createSsl + rootCerts: 'config/cert/server.crt', + options: { + 'grpc.ssl_target_name_override': 'example.server', + 'grpc.default_authority': 'example.server', + }, + }, + }, + }, }; ``` diff --git a/app.js b/app.js index 51781f1..6537c14 100644 --- a/app.js +++ b/app.js @@ -1,33 +1,8 @@ 'use strict'; const loader = require('./lib/grpc_loader'); -const fs = require('fs'); -const path = require('path'); - module.exports = app => { // grpc.setLogger(app.coreLogger); - const GrpcLoader = app.loader.GrpcLoader = loader(app); - new GrpcLoader({}).load(); - // grpc ssl - if (app.config.grpc.clientSsl.enable) { - if (app.config.grpc.clientSsl.rootCerts.trim() !== '') { - const rootCerts = path.join(app.baseDir, app.config.grpc.clientSsl.rootCerts) - if (fs.existsSync(rootCerts)) { - app.grpc.clientSsl.rootCerts = fs.readFileSync(rootCerts); - } - } - if (app.config.grpc.clientSsl.privateKey.trim() != '') { - const privateKey = path.join(app.baseDir, app.config.grpc.clientSsl.privateKey) - if (fs.existsSync(privateKey)) { - app.grpc.clientSsl.privateKey = fs.readFileSync(privateKey); - } - } - if (app.config.grpc.clientSsl.certChain.trim() != '') { - const certChain = path.join(app.baseDir, app.config.grpc.clientSsl.certChain) - if (fs.existsSync(certChain)) { - app.grpc.clientSsl.certChain = fs.readFileSync(certChain); - } - } - } + loader(app); }; diff --git a/app/extend/application.js b/app/extend/application.js index 434038a..9116064 100644 --- a/app/extend/application.js +++ b/app/extend/application.js @@ -1,7 +1,6 @@ 'use strict'; const GrpcBaseClass = require('../../lib/base_grpc'); -const GRPC = Symbol('Application#grpc'); module.exports = { @@ -37,11 +36,4 @@ module.exports = { get GrpcBaseClass() { return GrpcBaseClass; }, - get grpc() { - // this 就是 app 对象,在其中可以调用 app 上的其他方法,或访问属性 - if (!this[GRPC]) { - this[GRPC] = { clientSsl: { rootCerts: null, privateKey: null, certChain: null } } - } - return this[GRPC]; - } }; diff --git a/config/config.default.js b/config/config.default.js index 192766e..8fde579 100644 --- a/config/config.default.js +++ b/config/config.default.js @@ -4,7 +4,7 @@ * grpc config * @member Config#grpc * @property {String} dir - proto files dir, relative path - * @property {String} property - default attach to `ctx.grpc.**` + * @property {String} property - default attach to `ctx.grpc.**`,与object.key同名,存在多个实例时需要配置该项 * @property {Object} loadOpts - options pass to `grpc.load(file, type, opts)` * @property {Boolean} loadOpts.convertFieldsToCamelCase - default to true, `string user_name` -> `userName` * @property {Object} clientOpts - options pass to `new Client(host, credentials, opts)` @@ -18,29 +18,49 @@ * @property {Object} clientSsl.options - 主要用于grpc.ssl_target_name_override和grpc.default_authority的配置 */ exports.grpc = { - dir: 'app/proto', - property: 'grpc', - loadOpts: { - convertFieldsToCamelCase: true, + default: { + dir: 'app/proto', + property: 'grpc', + loadOpts: { + convertFieldsToCamelCase: true, + }, + clientOpts: {}, + endpoint: 'localhost:50051', + timeout: 5000, + /** + * 2019-12-03 by 张晓东 + * 通过扩展原有配置的形式,使其支持tls/ssl安全 + */ + clientSsl: { + enable: false, + // grpc.credentials.createSsl + // config/grpc/cert.server.crt + rootCerts: '', // + privateKey: '', // 作为grpc client时无需填写 + certChain: '', // 作为grpc client时无需填写 + verifyOptions: {}, + options: { + 'grpc.ssl_target_name_override': 'example.server', + 'grpc.default_authority': 'example.server', + }, + }, }, - clientOpts: {}, - endpoint: 'localhost:50051', - timeout: 5000, - /** - * 2019-12-03 by 张晓东 - * 通过扩展原有配置的形式,使其支持tls/ssl安全 - */ - clientSsl: { - enable: false, - // grpc.credentials.createSsl - // config/grpc/cert.server.crt - rootCerts: '', // - privateKey: '', // 作为grpc client时无需填写 - certChain: '', // 作为grpc client时无需填写 - verifyOptions: {}, - options: { - "grpc.ssl_target_name_override": 'example.server', - "grpc.default_authority": 'example.server' - } - } + // clients: { + // // this.ctx.grpc1 + // grpc1: { + // property: 'grpc1', + // dir: 'grpc', + // // 服务端地址 + // endpoint: 'localhost:8090', + // clientSsl: { + // enable: true, + // // grpc.credentials.createSsl + // rootCerts: 'grpc/cert/server.crt', + // options: { + // "grpc.ssl_target_name_override": 'example.server', + // "grpc.default_authority": 'example.server' + // } + // } + // }, + // } }; diff --git a/lib/base_grpc.js b/lib/base_grpc.js index c1973ef..e0562df 100644 --- a/lib/base_grpc.js +++ b/lib/base_grpc.js @@ -18,7 +18,9 @@ module.exports = class BaseGrpc { constructor(ctx, ProtoClass) { this.ctx = ctx; this.app = ctx.app; - this.config = this.app.config.grpc; + // this.config = this.app.config.grpc; + const key = ProtoClass.grpcconfig; // 获取对应的config的key值 + this.config = this.app.config.grpc.clients[key]; // 获取对应的key值的config对象 this.ProtoClass = ProtoClass; // delegate client rpc to this @@ -43,12 +45,10 @@ module.exports = class BaseGrpc { if (!this.config.clientSsl.enable) { this[CLIENT] = new this.ProtoClass(this.config.endpoint, grpc.credentials.createInsecure(), Object.assign({}, this.config.clientOpts)); } else { - // TODO: 多个grpc实例 - const ssl_creds = grpc.credentials.createSsl(this.app.grpc.clientSsl.rootCerts, this.app.grpc.clientSsl.privateKey, this.app.grpc.clientSsl.certChain, this.config.clientSsl.verifyOptions) + const ssl_creds = grpc.credentials.createSsl(this.config.clientSsl.rootCertsData, this.config.clientSsl.privateKeyData, this.config.clientSsl.certChainData, this.config.clientSsl.verifyOptions); this[CLIENT] = new this.ProtoClass(this.config.endpoint, ssl_creds, Object.assign({}, this.config.clientOpts, this.config.clientSsl.options)); } } - // TODO: config.ssl return this[CLIENT]; } diff --git a/lib/grpc_loader.js b/lib/grpc_loader.js index 5cb37f7..1927fa1 100644 --- a/lib/grpc_loader.js +++ b/lib/grpc_loader.js @@ -5,9 +5,57 @@ const grpc = require('grpc'); const traverse = require('traverse'); const extend = require('extend2'); const debug = require('debug')('grpc'); +const fs = require('fs'); module.exports = app => { - const config = app.config.grpc; + // 获取多个配置 + const multiConfig = app.config.grpc.clients; + // 获取所有配置的对象键值 + const keys = Object.keys(multiConfig); + // 循环获取多个配置,进行加载,采用类官方插件多实例实现方案 + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + // 合并默认配置 + const config = Object.assign({}, app.config.grpc.default); + Object.assign(config, multiConfig[key]); + const config_clientSsl = Object.assign({}, config.clientSsl); + Object.assign(config.clientSsl, app.config.grpc.default.clientSsl); + Object.assign(config.clientSsl, config_clientSsl); + const GrpcLoader = createGrpc(config, app); + new GrpcLoader({}).load(); + } +}; + +/** + * @param {Object} config 框架处理之后的配置项,如果应用配置了多个实例,会将每一个配置项分别传入并调用多次该函数 + * @param {Application} app 当前的应用 + * @return {Object} 返回创建的实例 + */ +function createGrpc(config, app) { + // grpc ssl + if (config.clientSsl.enable) { + config.clientSsl.rootCertsData = undefined; + config.clientSsl.privateKeyData = undefined; + config.clientSsl.certChainData = undefined; + if (config.clientSsl.rootCerts !== '') { + const rootCerts = path.join(app.baseDir, config.clientSsl.rootCerts); + if (fs.existsSync(rootCerts)) { + config.clientSsl.rootCertsData = fs.readFileSync(rootCerts); + } + } + if (config.clientSsl.privateKey !== '') { + const privateKey = path.join(app.baseDir, config.clientSsl.privateKey); + if (fs.existsSync(privateKey)) { + config.clientSsl.privateKeyData = fs.readFileSync(privateKey); + } + } + if (config.clientSsl.certChain !== '') { + const certChain = path.join(app.baseDir, config.clientSsl.certChain); + if (fs.existsSync(certChain)) { + config.clientSsl.certChainData = fs.readFileSync(certChain); + } + } + } const defaults = { call: true, @@ -44,13 +92,14 @@ module.exports = app => { // traverse origin grpc proto to extract rpc service // `/example.Test/Echo` -> `app.grpcClasses.example.test` -> `yield ctx.grpc.example.test.echo()` - traverse(exports).forEach(function(proto) { + traverse(exports).forEach(function (proto) { /* istanbul ignore next */ if (this.circular) this.remove(); if (proto.name === 'Client' || proto.name === 'ServiceClient') { const properties = this.path.map(camelize); proto.paths = properties; + proto.grpcconfig = config.property; // key const item = { fullpath, properties, @@ -83,4 +132,5 @@ module.exports = app => { } return GrpcLoader; -}; +} + diff --git a/package.json b/package.json index 42a87a7..043c60e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "egg-grpc-ssl", - "version": "1.0.4", + "version": "1.0.6", "description": "grpc plugin for egg", "eggPlugin": { "name": "grpc"