From 21425e7a9c451cfa07f3cb580d0b770eb5b0c890 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?z=C5=8Dng=20y=C7=94?= Date: Mon, 25 Sep 2017 09:42:01 +0800 Subject: [PATCH] feat: make cluster client configurable in egg (#1459) --- config/config.default.js | 7 ++ docs/source/zh-cn/advanced/cluster-client.md | 88 ++++++++++++++++++- lib/egg.js | 13 +-- package.json | 53 +++++------ test/fixtures/apps/cluster_mod_app/agent.js | 18 +++- test/fixtures/apps/cluster_mod_app/app.js | 15 +++- .../apps/cluster_mod_app/app/router.js | 7 ++ .../apps/cluster_mod_app/lib/api_client.js | 21 +++++ .../apps/cluster_mod_app/lib/api_client_2.js | 22 +++++ test/lib/cluster/cluster-client.test.js | 18 ++++ 10 files changed, 221 insertions(+), 41 deletions(-) create mode 100644 test/fixtures/apps/cluster_mod_app/lib/api_client.js create mode 100644 test/fixtures/apps/cluster_mod_app/lib/api_client_2.js diff --git a/config/config.default.js b/config/config.default.js index 0192d106e3..aebff6bc84 100644 --- a/config/config.default.js +++ b/config/config.default.js @@ -302,5 +302,12 @@ module.exports = appInfo => { }, }; + /** + * @property {Number} responseTimeout - response timeout, default is 60000 + */ + config.clusterClient = { + responseTimeout: 60000, + }; + return config; }; diff --git a/docs/source/zh-cn/advanced/cluster-client.md b/docs/source/zh-cn/advanced/cluster-client.md index f0dc304b25..367176ad67 100644 --- a/docs/source/zh-cn/advanced/cluster-client.md +++ b/docs/source/zh-cn/advanced/cluster-client.md @@ -435,7 +435,7 @@ module.exports = APIClient; const APIClient = require('some-client'); // 上面那个模块 module.exports = app => { const config = app.config.apiClient; - app.apiClient = new APIClient(Object.assign({}, config, { cluster: app.cluster.bind(app) }); + app.apiClient = new APIClient(Object.assign({}, config, { cluster: app.cluster }); app.beforeStart(function* () { yield app.apiClient.ready(); }); @@ -452,6 +452,38 @@ exports.apiClient = { }; ``` +为了方便你封装 `APIClient`,在 [cluster-client](https://www.npmjs.com/package/cluster-client) 模块中提供了一个 `APIClientBase` 基类,那么上面的 `APIClient` 可以改写为: + +```js +const APIClientBase = require('cluster-client').APIClientBase; +const RegistryClient = require('./registry_client'); + +class APIClient extends APIClientBase { + // 返回原始的客户端类 + get DataClient() { + return RegistryClient; + } + + // 用于设置 cluster-client 相关参数,等同于 cluster 方法的第二个参数 + get clusterOptions() { + return { + responseTimeout: 120 * 1000, + }; + } + + subscribe(reg, listener) { + this._client.subscribe(reg, listener); + } + + publish(reg) { + this._client.publish(reg); + } + + get(key) { + return this._cache[key]; + } +} +``` 总结一下: @@ -470,3 +502,57 @@ exports.apiClient = { - APIClient - 内部调用 ClusterClient 做数据同步,无需关心多进程模型,用户最终使用的模块。API 都通过此处暴露,支持同步和异步。 有兴趣的同学可以看一下[增强多进程研发模式](https://github.com/eggjs/egg/issues/322) 讨论过程。 + +## 在 Egg 里面 cluster-client 相关的配置项 + +```js +/** + * @property {Number} responseTimeout - response timeout, default is 60000 + * @property {Transcode} [transcode] + * - {Function} encode - custom serialize method + * - {Function} decode - custom deserialize method + */ +config.clusterClient = { + responseTimeout: 60000, +}; +``` + +配置项 | 类型 | 默认值 | 描述 +-----------------------|----------|-----------------|-------------------------------------------------------------------------- +responseTimeout | number | 60000 (一分钟) | 全局的进程间通讯的超时时长,不能设置的太短,因为代理的接口本身也有超时设置 +transcode | function | N/A | 进程间通讯的序列化方式,默认采用 [serialize-json](https://www.npmjs.com/package/serialize-json)(建议不要自行设置) + +上面是全局的配置方式。如果,你想对一个客户端单独做设置 + +- 可以通过 `app/agent.cluster(ClientClass, options)` 的第二个参数 `options` 进行覆盖 + +```js +app.registryClient = app.cluster(RegistryClient, { + responseTimeout: 120 * 1000, // 这里传入的是和 cluster-client 相关的参数 +}).create({ + // 这里传入的是 RegistryClient 需要的参数 +}); +``` + +- 也可以通过覆盖 `APIClientBase` 的 `clusterOptions` 这个 `getter` 属性 + +```js +const APIClientBase = require('cluster-client').APIClientBase; +const RegistryClient = require('./registry_client'); + +class APIClient extends APIClientBase { + get DataClient() { + return RegistryClient; + } + + get clusterOptions() { + return { + responseTimeout: 120 * 1000, + }; + } + + // ... +} + +module.exports = APIClient; +``` diff --git a/lib/egg.js b/lib/egg.js index fa86a42790..361d97c79d 100644 --- a/lib/egg.js +++ b/lib/egg.js @@ -87,12 +87,13 @@ class EggApplication extends EggCore { * @return {ClientWrapper} wrapper */ this.cluster = (clientClass, options) => { - options = options || {}; - // cluster need a port that can't conflict on the environment - options.port = this._options.clusterPort; - // agent worker is leader, app workers are follower - options.isLeader = this.type === 'agent'; - options.logger = this.coreLogger; + options = Object.assign({}, this.config.clusterClient, options, { + // cluster need a port that can't conflict on the environment + port: this._options.clusterPort, + // agent worker is leader, app workers are follower + isLeader: this.type === 'agent', + logger: this.coreLogger, + }); const client = cluster(clientClass, options); this._patchClusterClient(client); return client; diff --git a/package.json b/package.json index 81211a5808..70c16a5965 100644 --- a/package.json +++ b/package.json @@ -13,64 +13,65 @@ "egg" ], "dependencies": { - "@types/accepts": "^1.3.2", + "@types/accepts": "^1.3.3", "@types/koa": "^2.0.39", - "@types/koa-router": "^7.0.22", - "accepts": "^1.3.3", + "@types/koa-router": "^7.0.23", + "accepts": "^1.3.4", "agentkeepalive": "^3.3.0", - "cluster-client": "^1.6.5", + "cluster-client": "^1.7.1", "co": "^4.6.0", - "debug": "^2.6.8", + "debug": "^3.0.1", "delegates": "^1.0.0", - "egg-cluster": "^1.8.0", + "egg-cluster": "^1.12.1", "egg-cookies": "^2.2.1", - "egg-core": "^3.12.0", + "egg-core": "^3.13.1", "egg-development": "^1.3.1", - "egg-i18n": "^1.1.1", - "egg-jsonp": "^1.1.1", + "egg-i18n": "^1.2.0", + "egg-jsonp": "^1.1.2", "egg-logger": "^1.6.0", "egg-logrotator": "^2.2.3", "egg-multipart": "^1.5.0", - "egg-onerror": "^1.4.6", + "egg-onerror": "^1.5.0", "egg-schedule": "^2.4.1", - "egg-security": "^1.11.0", + "egg-security": "^1.12.1", "egg-session": "^2.1.1", "egg-static": "^1.4.1", - "egg-view": "^1.1.1", - "egg-watcher": "^2.1.3", + "egg-view": "^1.1.2", + "egg-watcher": "^2.2.0", "extend2": "^1.0.0", "graceful": "^1.0.1", "humanize-ms": "^1.2.1", - "is-type-of": "^1.0.0", + "is-type-of": "^1.2.0", "koa-bodyparser": "^2.5.0", "koa-is-json": "^1.0.0", "koa-override": "^2.0.0", - "mime-types": "^2.1.15", + "mime-types": "^2.1.17", "sendmessage": "^1.1.0", - "urllib": "^2.23.0", + "urllib": "^2.25.0", "utility": "^1.12.0", - "ylru": "^1.1.0" + "ylru": "^1.2.0" }, "devDependencies": { - "autod": "^2.8.0", + "address": "^1.0.3", + "autod": "^2.9.0", "autod-egg": "^1.0.0", "coffee": "^4.1.0", "egg-alinode": "^1.0.3", - "egg-bin": "^4.0.4", - "egg-doctools": "^2.0.1", - "egg-mock": "^3.8.0", + "egg-bin": "^4.3.3", + "egg-doctools": "^2.1.0", + "egg-mock": "^3.12.2", "egg-plugin-puml": "^2.4.0", "egg-tracer": "^1.1.0", - "egg-view-nunjucks": "^2.1.3", - "eslint": "^4.1.1", - "eslint-config-egg": "^5.0.0", + "egg-view-nunjucks": "^2.1.4", + "eslint": "^4.7.2", + "eslint-config-egg": "^5.1.1", "findlinks": "^1.1.0", "formstream": "^1.1.0", "glob": "^7.1.2", "koa-static": "^3.0.0", - "mz-modules": "^1.0.0", + "mz-modules": "^2.0.0", "pedding": "^1.1.0", - "runscript": "^1.2.1", + "runscript": "^1.3.0", "spy": "^1.0.0", "supertest": "^3.0.0", "ts-node": "^3.0.6", diff --git a/test/fixtures/apps/cluster_mod_app/agent.js b/test/fixtures/apps/cluster_mod_app/agent.js index b237d2e00a..c10b041524 100644 --- a/test/fixtures/apps/cluster_mod_app/agent.js +++ b/test/fixtures/apps/cluster_mod_app/agent.js @@ -1,11 +1,21 @@ 'use strict'; +const ApiClient = require('./lib/api_client'); +const ApiClient2 = require('./lib/api_client_2'); const RegistryClient = require('./lib/registry_client'); module.exports = function(agent) { - const done = agent.readyCallback('register_client', { - isWeakDep: agent.config.runMode === 0, - }); agent.registryClient = agent.cluster(RegistryClient).create(); - agent.registryClient.ready(done); + agent.apiClient = new ApiClient({ + cluster: agent.cluster, + }); + agent.apiClient2 = new ApiClient2({ + cluster: agent.cluster, + }); + + agent.beforeStart(function*() { + yield agent.registryClient.ready(); + yield agent.apiClient.ready(); + yield agent.apiClient2.ready(); + }); }; diff --git a/test/fixtures/apps/cluster_mod_app/app.js b/test/fixtures/apps/cluster_mod_app/app.js index ba222e7bb5..46b1461cb2 100644 --- a/test/fixtures/apps/cluster_mod_app/app.js +++ b/test/fixtures/apps/cluster_mod_app/app.js @@ -1,18 +1,25 @@ 'use strict'; +const ApiClient = require('./lib/api_client'); +const ApiClient2 = require('./lib/api_client_2'); const RegistryClient = require('./lib/registry_client'); module.exports = function(app) { - const done = app.readyCallback('register_client', { - isWeakDep: app.config.runMode === 0, - }); const cluster = app.cluster; app.registryClient = cluster(RegistryClient).create(); - app.registryClient.ready(done); app.registryClient.subscribe({ dataId: 'demo.DemoService', }, val => { app.val = val; }); + + app.apiClient = new ApiClient({ cluster }); + app.apiClient2 = new ApiClient2({ cluster }); + + app.beforeStart(function*() { + yield app.registryClient.ready(); + yield app.apiClient.ready(); + yield app.apiClient2.ready(); + }); }; diff --git a/test/fixtures/apps/cluster_mod_app/app/router.js b/test/fixtures/apps/cluster_mod_app/app/router.js index b07222415c..fc0095b5cd 100644 --- a/test/fixtures/apps/cluster_mod_app/app/router.js +++ b/test/fixtures/apps/cluster_mod_app/app/router.js @@ -5,4 +5,11 @@ module.exports = app => { app.get('/clusterPort', app.controller.home.getClusterPort); app.post('/publish', app.controller.home.publish); app.get('/getHosts', app.controller.home.getHosts); + + app.get('/getDefaultTimeout', function*() { + this.body = yield app.apiClient.getResponseTimeout(); + }); + app.get('/getOverwriteTimeout', function*() { + this.body = yield app.apiClient2.getResponseTimeout(); + }); }; diff --git a/test/fixtures/apps/cluster_mod_app/lib/api_client.js b/test/fixtures/apps/cluster_mod_app/lib/api_client.js new file mode 100644 index 0000000000..0bf9b6abb5 --- /dev/null +++ b/test/fixtures/apps/cluster_mod_app/lib/api_client.js @@ -0,0 +1,21 @@ +'use strict'; + +const APIClientBase = require('cluster-client').APIClientBase; + +class ApiClient extends APIClientBase { + get DataClient() { + return require('./registry_client'); + } + + get clusterOptions() { + return { + name: 'ApiClient', + }; + } + + * getResponseTimeout() { + return this._client.options.responseTimeout; + } +} + +module.exports = ApiClient; diff --git a/test/fixtures/apps/cluster_mod_app/lib/api_client_2.js b/test/fixtures/apps/cluster_mod_app/lib/api_client_2.js new file mode 100644 index 0000000000..066e024127 --- /dev/null +++ b/test/fixtures/apps/cluster_mod_app/lib/api_client_2.js @@ -0,0 +1,22 @@ +'use strict'; + +const APIClientBase = require('cluster-client').APIClientBase; + +class ApiClient2 extends APIClientBase { + get DataClient() { + return require('./registry_client'); + } + + get clusterOptions() { + return { + name: 'ApiClient2', + responseTimeout: 1000, + }; + } + + * getResponseTimeout() { + return this._client.options.responseTimeout; + } +} + +module.exports = ApiClient2; diff --git a/test/lib/cluster/cluster-client.test.js b/test/lib/cluster/cluster-client.test.js index 0dedb6c644..839ee55ad0 100644 --- a/test/lib/cluster/cluster-client.test.js +++ b/test/lib/cluster/cluster-client.test.js @@ -37,4 +37,22 @@ describe('test/lib/cluster/cluster-client.test.js', () => { .expect(200); }); }); + + it('should get default cluster response timeout', () => { + return app.httpRequest() + .get('/getDefaultTimeout') + .expect(200) + .then(res => { + assert(res.text === '60000'); + }); + }); + + it('should get overwrite cluster response timeout', () => { + return app.httpRequest() + .get('/getOverwriteTimeout') + .expect(200) + .then(res => { + assert(res.text === '1000'); + }); + }); });