Skip to content

Commit

Permalink
feat: make cluster client configurable in egg (#1459)
Browse files Browse the repository at this point in the history
  • Loading branch information
gxcsoccer authored Sep 25, 2017
1 parent d0797b1 commit 21425e7
Show file tree
Hide file tree
Showing 10 changed files with 221 additions and 41 deletions.
7 changes: 7 additions & 0 deletions config/config.default.js
Original file line number Diff line number Diff line change
Expand Up @@ -302,5 +302,12 @@ module.exports = appInfo => {
},
};

/**
* @property {Number} responseTimeout - response timeout, default is 60000
*/
config.clusterClient = {
responseTimeout: 60000,
};

return config;
};
88 changes: 87 additions & 1 deletion docs/source/zh-cn/advanced/cluster-client.md
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
Expand All @@ -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];
}
}
```
总结一下:
Expand All @@ -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;
```
13 changes: 7 additions & 6 deletions lib/egg.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
53 changes: 27 additions & 26 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
18 changes: 14 additions & 4 deletions test/fixtures/apps/cluster_mod_app/agent.js
Original file line number Diff line number Diff line change
@@ -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();
});
};
15 changes: 11 additions & 4 deletions test/fixtures/apps/cluster_mod_app/app.js
Original file line number Diff line number Diff line change
@@ -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();
});
};
7 changes: 7 additions & 0 deletions test/fixtures/apps/cluster_mod_app/app/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
};
21 changes: 21 additions & 0 deletions test/fixtures/apps/cluster_mod_app/lib/api_client.js
Original file line number Diff line number Diff line change
@@ -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;
22 changes: 22 additions & 0 deletions test/fixtures/apps/cluster_mod_app/lib/api_client_2.js
Original file line number Diff line number Diff line change
@@ -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;
18 changes: 18 additions & 0 deletions test/lib/cluster/cluster-client.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});
});
});

0 comments on commit 21425e7

Please sign in to comment.