Compare commits

...

60 Commits

Author SHA1 Message Date
Harry Chen 8a5568de77 v2.6.0 2023-12-14 10:43:15 +08:00
Bob 57cd21060b
chore: nacos-naming add more types (#105)
Co-authored-by: Harry Chen <czy88840616@gmail.com>
2023-12-14 10:41:52 +08:00
Nolan Zhang 7fa34ff6a6
chore: add Hosts typings (#97)
I have deployed NACOS version 2.1.1. Has the type changed here because of the version upgrade? I have fixed it
2023-12-13 19:56:09 +08:00
Marc SUN 22c0e57947
chore: add nacos auth supportt typings (#114)
* Update index.d.ts

add nacos auth support

* Update interface.ts

add nacos auth support
2023-12-13 19:54:50 +08:00
陈浩 Nineteen cdea9a26b1
chore: support nacos-sdk-nodejs(2.5.0) example and add auth example (#100) 2023-12-13 19:52:01 +08:00
唯然 50ac48e026
chore: update pkg.license => Apache-2.0 (#101)
rome reports "✖ Unknown license Apache".

refs: https://rome.tools/
2023-03-10 11:23:25 +08:00
Harry Chen 3c6840bdd7 v2.5.1 2022-12-08 16:14:07 +08:00
Jack Hu 4c9af68899
fix: no retry with dns.NOTFOUND (#102)
Co-authored-by: hujingkang <hujingkang@joyy.com>
2022-12-08 16:04:08 +08:00
Harry Chen cc6667c5b1 v2.5.0 2022-08-22 00:05:23 +08:00
Darcy d6ee163a36
fix: comparing the host information using an unstable JSON string (#95) 2022-08-22 00:03:21 +08:00
Harry Chen 41e3965870 v2.4.2 2022-08-09 19:35:21 +08:00
Harry Chen a3feb781a7 chore: update 2022-08-09 19:33:46 +08:00
恒遥 910f7e4bfe
fix: ignore empty hosts array (#94)
Co-authored-by: hengyao.zhy <hengyao.zhy@antgroup.com>
2022-08-09 19:30:03 +08:00
yanlinly 7f1870778e
Update README.md
update ding group info
2022-06-08 17:25:34 +08:00
Harry Chen d044e6cef4 v2.4.1 2022-05-09 14:40:09 +08:00
Evanhahaha 005134d725
feat: config add appName (#87)
* feat: clientOptions增加endPoint查询的url参数选项

* feat: add example of endPointQueryParam

* fixed query param example

* feat: 增加appName可配置

* fixed merge conflict

Co-authored-by: 黑糖 <heitang.zlw@alibaba-inc.com>
2022-05-09 14:38:56 +08:00
Harry Chen e3cb1d5955 v2.4.0 2022-05-05 20:15:14 +08:00
Evanhahaha 6df7ece0a8 feat: clientOptions add endPoint url query params (#84)
Co-authored-by: 黑糖 <heitang.zlw@alibaba-inc.com>
2022-05-05 20:15:14 +08:00
Harry Chen 27a8fa7651 v2.3.1 2022-05-05 20:15:14 +08:00
Harry Chen a3f33bd901 fix: missing typings 2022-05-05 20:15:14 +08:00
Harry Chen 194629a25b v2.3.0 2022-05-05 20:15:14 +08:00
ugrg 34055b4117 feat: add nacos-naming typings (#77)
Co-authored-by: ugrg <ugrg@163.com>
2022-05-05 20:13:49 +08:00
Vinlic科技 9ae37cef32
feat: add configuration type support (#82) 2022-04-25 19:33:54 +08:00
沈赟杰 a9a7fe64d8
fix: update interface.ts (#80) 2022-04-25 19:33:23 +08:00
zhanheng 3940268149
fix: window10,node use clutser (#57)
Co-authored-by: zhanheng <zhanheng@guahao.com>
2021-12-30 12:54:09 +08:00
Harry Chen 2b703c0621 v2.2.1 2021-11-25 12:10:05 +08:00
恒遥 01affdbb51
fix: listener add tenant (#71)
Co-authored-by: hengyao.zhy <hengyao.zhy@antgroup.com>
2021-11-25 12:05:35 +08:00
Harry Chen 643dd12ab8 v2.2.0 2021-09-29 18:37:15 +08:00
恒遥 c9f5808675
feat: support custom decode res data (#66)
Co-authored-by: hengyao.zhy <hengyao.zhy@antgroup.com>
2021-09-29 18:29:34 +08:00
恒遥 3e9670d9cf
feat: support custom httpAgent (#60)
Co-authored-by: hengyao.zhy <hengyao.zhy@antgroup.com>
2021-08-06 15:35:19 +08:00
gxcsoccer d5e2dd8ae0 v2.1.1 2021-05-11 13:39:15 +08:00
zōng yǔ 61a415ec6f
Merge pull request #49 from zhanghengyao/fix/delay-default
fix: the delay default value is too short
2021-05-11 13:33:00 +08:00
陈高原 17ea984a61
fix: close process after push_receiver (#30) 2021-05-09 11:33:49 +08:00
zhangchen 180dcf72fe
feat: identity header (#50) 2021-05-09 11:24:31 +08:00
hengyao.zhy 8c06bda57f fix: the delay default value is too short 2021-05-08 14:09:22 +08:00
Harry Chen b219a870bd v2.1.0 2021-02-19 15:30:52 +08:00
Harry Chen e5c1359509 chore: add optional 2021-02-19 15:29:51 +08:00
Harry Chen 744fc56f29 Merge branch 'master' of github.com:nacos-group/nacos-sdk-nodejs 2021-02-19 15:26:00 +08:00
silverera 1a63399df0
feat: support new authentication mode (#39) 2021-02-19 15:18:08 +08:00
Harry Chen f1bfe2d065 chore: update 2021-01-26 13:35:28 +08:00
Harry Chen 371f2dbc82 v2.0.1 2021-01-26 13:34:46 +08:00
Harry Chen 84f6032f44 chore: update 2021-01-26 13:34:03 +08:00
Harry Chen 133550f482 chore: update 2021-01-26 13:31:44 +08:00
Harry Chen edb996043e chore: ready for publish 2021-01-26 13:30:18 +08:00
Harry Chen 688ca8f38d chore: ready for publish 2021-01-26 13:18:48 +08:00
netfoxor 614623577b
fix: fix the request address is undefined after SSL is enabled (#42)
Co-authored-by: 许昆鹏 <kunpeng.xkp@alibaba-inc.com>
2021-01-26 13:06:58 +08:00
Harry Chen 2ea1abb9b0
doc: fix readme typo (#24)
doc: fix readme typo
2019-05-08 18:59:28 +08:00
gxcsoccer 4182e142bf doc: fix readme typo 2019-05-08 18:55:34 +08:00
zōng yǔ f6d5a7462a
Merge pull request #22 from nacos-group/doc-change
doc: update naming api & add version mapping
2019-05-07 18:52:15 +08:00
gxcsoccer 989eba2dc9 doc: update naming api & add version mapping 2019-05-07 16:13:07 +08:00
Harry Chen 34368536f9 v2.0.0 2019-05-07 14:41:36 +08:00
Harry Chen f7b9ce72ba chore: ready for publish 2019-05-07 14:41:24 +08:00
Harry Chen b06340b757 chore: ready for publish 2019-05-07 14:34:25 +08:00
Harry Chen 8caaf2504c
Merge pull request #21 from nacos-group/feat-nacos-1.x
[BREAKING] feat: support nacos server v1.0.0
2019-05-07 14:33:14 +08:00
gxcsoccer 7793dd5a73 feat: support nacos server v1.0.0 2019-05-07 12:35:03 +08:00
Harry Chen dfde567b25 v1.1.2 2019-03-31 22:40:15 +09:00
Harry Chen 06cad6fa4e chore: ready for publish 2019-03-31 22:40:07 +09:00
Harry Chen d81a4ab8e9
Merge pull request #16 from nacos-group/fix_option_direct
fix: fix options in direct mode
2019-03-31 22:39:07 +09:00
Harry Chen 93c3559a05 fix: fix gbk text 2019-03-31 12:02:11 +09:00
Harry Chen 2942e19b90 fix: fix options in direct mode 2019-03-31 11:25:13 +09:00
46 changed files with 1364 additions and 555 deletions

View File

@ -3,18 +3,20 @@ language: node_js
node_js:
- '8'
- '10'
- '12'
jdk: oraclejdk8
before_install:
- echo $JAVA_HOME
- java -version
- 'wget https://github.com/alibaba/nacos/releases/download/0.5.0/nacos-server-0.5.0.tar.gz'
- 'wget https://github.com/gxcsoccer/PDisk/releases/download/1.0.0/startup.sh'
- 'tar xf nacos-server-0.5.0.tar.gz'
- 'wget https://github.com/alibaba/nacos/releases/download/1.0.0/nacos-server-1.0.0.tar.gz'
- 'wget https://github.com/gxcsoccer/PDisk/releases/download/1.0.1/startup.sh'
- 'tar xf nacos-server-1.0.0.tar.gz'
- 'mv ./startup.sh ./nacos/bin/startup.sh'
- 'chmod 755 ./nacos/bin/startup.sh'
- 'nohup ./nacos/bin/startup.sh -m standalone 2>&1 &'
- 'sleep 30'
- 'curl "127.0.0.1:8848/nacos/v1/ns/api/hello"'
- 'cat nohup.out'
- 'curl "127.0.0.1:8848/nacos/v1/ns/operator/metrics"'
install:
- npm i npminstall && npminstall
- npm run bootstrap

View File

@ -1,7 +1,9 @@
# Ordered by date of first contribution.
# Auto-generated by 'contributors' on Wed, 12 Dec 2018 06:57:02 GMT.
# Auto-generated by 'contributors' on Tue, 26 Jan 2021 05:30:11 GMT.
# https://github.com/xingrz/node-contributors
yanlinly <yan.lin2009@163.com> (https://github.com/yanlinly)
yanlinly <yan.lin2009@163.com>
zōng yǔ <gxcsoccer@126.com> (https://github.com/gxcsoccer)
Harry Chen <czy88840616@gmail.com> (https://github.com/czy88840616)
zōng yǔ <gxcsoccer@users.noreply.github.com>
netfoxor <netfoxor@qq.com>

View File

@ -3,6 +3,29 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.0.1](https://github.com/nacos-group/nacos-sdk-nodejs/compare/v2.0.0...v2.0.1) (2021-01-26)
### Bug Fixes
* fix the request address is undefined after SSL is enabled ([#42](https://github.com/nacos-group/nacos-sdk-nodejs/issues/42)) ([6146235](https://github.com/nacos-group/nacos-sdk-nodejs/commit/614623577baa510fefc575f875e2ff3076f8a781))
## [1.1.2](https://github.com/nacos-group/nacos-sdk-nodejs/compare/v1.1.1...v1.1.2) (2019-03-31)
### Bug Fixes
* fix gbk text ([93c3559](https://github.com/nacos-group/nacos-sdk-nodejs/commit/93c3559))
* fix options in direct mode ([2942e19](https://github.com/nacos-group/nacos-sdk-nodejs/commit/2942e19))
## [1.1.1](https://github.com/nacos-group/nacos-sdk-nodejs/compare/v1.1.0...v1.1.1) (2019-01-17)

View File

@ -21,6 +21,13 @@
npm install nacos --save
```
## Version Mapping
Node.js SDK \ Nacos Server | 0.x.0 | 1.0.0 |
--- | --- | --- |
1.x | √ | |
2.x | | √ |
## Usage
### Service Discovery
@ -34,14 +41,21 @@ const logger = console;
const client = new NacosNamingClient({
logger,
serverList: '127.0.0.1:8848', // replace to real nacos serverList
namespace: 'public',
});
await client.ready();
const serviceName = 'nodejs.test.domain';
// registry instance
await client.registerInstance(serviceName, '1.1.1.1', 8080, 'NODEJS');
await client.registerInstance(serviceName, '2.2.2.2', 8080, 'NODEJS');
await client.registerInstance(serviceName, {
ip: '1.1.1.1',
port: 8080,
});
await client.registerInstance(serviceName, {
ip: '2.2.2.2',
port: 8080,
});
// subscribe instance
client.subscribe(serviceName, hosts => {
@ -49,7 +63,10 @@ client.subscribe(serviceName, hosts => {
});
// deregister instance
await client.deregisterInstance(serviceName, '1.1.1.1', 8080, 'NODEJS');
await client.deregisterInstance(serviceName, {
ip: '1.1.1.1',
port: 8080,
});
```
### Config Service
@ -100,27 +117,36 @@ default value: [ClientOptions default value](https://github.com/nacos-group/naco
### Service Discovery
- `registerInstance(serviceName, ip, port, [cluster])` Register an instance to service.
- serviceName <String> Service name
- ip <String> IP of instance
- port <Number> Port of instance
- cluster <String> Virtual cluster name
- `registerInstance(serviceName, instance, [groupName])` Register an instance to service.
- serviceName {String} Service name
- instance {Instance}
- ip {String} IP of instance
- port {Number} Port of instance
- [weight] {Number} weight of the instance, default is 1.0
- [ephemeral] {Boolean} active until the client is alive, default is true
- [clusterName] {String} Virtual cluster name
- [groupName] {String} group name, default is `DEFAULT_GROUP`
- `deregisterInstance(serviceName, ip, port, [cluster])` Delete instance from service.
- serviceName <String> Service name
- ip <String> IP of instance
- port <Number> Port of instance
- cluster <String> Virtual cluster name
- `getAllInstances(serviceName, [clusters])` Query instance list of service.
- serviceName <String> Service name
- clusters <Array> Cluster names
- serviceName {String} Service name
- instance {Instance}
- ip {String} IP of instance
- port {Number} Port of instance
- [weight] {Number} weight of the instance, default is 1.0
- [ephemeral] {Boolean} active until the client is alive, default is true
- [clusterName] {String} Virtual cluster name
- [groupName] {String} group name, default is `DEFAULT_GROUP`
- `getAllInstances(serviceName, [groupName], [clusters], [subscribe])` Query instance list of service.
- serviceName {String} Service name
- [groupName] {String} group name, default is `DEFAULT_GROUP`
- [clusters] {String} Cluster names
- [subscribe] {Boolean} whether subscribe the service, default is true
- `getServerStatus()` Get the status of nacos server, 'UP' or 'DOWN'.
- `subscribe(info, listener)` Subscribe the instances of the service
- info <Object | String> service info, if type is string, it's the serviceName
- listener <Function> the listener function
- unSubscribe(info, [listener]) Unsubscribe the instances of the service
- info <Object | String> service info, if type is string, it's the serviceName
- listener <Function> the listener function, if not provide, will unSubscribe all listeners under this service
- info {Object}|{String} service info, if type is string, it's the serviceName
- listener {Function} the listener function
- `unSubscribe(info, [listener])` Unsubscribe the instances of the service
- info {Object}|{String} service info, if type is string, it's the serviceName
- listener {Function} the listener function, if not provide, will unSubscribe all listeners under this service
### Config Service
@ -151,6 +177,10 @@ Please let us know how can we help. Do check out [issues](https://github.com/nac
PR is welcome.
nacos-sdk-nodejs ding group 44654232
![image](https://user-images.githubusercontent.com/17695352/172582005-c661e2a0-49fa-425c-bf99-785bb7cd4dc1.png)
## License
[Apache License V2](LICENSE)

View File

@ -0,0 +1,50 @@
'use strict';
const NacosConfigClient = require('nacos').NacosConfigClient;
const configClient = new NacosConfigClient({
serverAddr: 'aliyun.nacos.net:80',
namespace: '',
// 如果nacos开启了认证鉴权需要在此处填写用户名密码
// username: 'xxx',
// password: 'xxx'
});
function sleep(time){
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, time);
})
}
(async () => {
await configClient.ready();
const dataId = 'nacos.test.22';
const group = 'DEFAULT_GROUP';
const str = `example_test_${Math.random()}_${Date.now()}`;
console.log('---------ready to publish------------');
await configClient.publishSingle(dataId, group, str);
await sleep(2000);
console.log('---------publish complete------------');
await sleep(2000);
console.log('---------ready to getConfig----------');
await sleep(2000);
let content = await configClient.getConfig(dataId, group);
console.log('---------getConfig complete----------');
console.log('current content => ' + content);
await sleep(2000);
console.log('---------ready to remove config------');
await configClient.remove(dataId, group);
console.log('---------remove config complete------');
await sleep(2000);
content = await configClient.getConfig(dataId, group);
console.log('---------getConfig complete----------');
console.log('current content => ' + content);
await sleep(2000);
console.log('---------remove config success-------');
configClient.close();
process.exit(0);
})();

View File

@ -2,6 +2,6 @@
"name": "nacos-example",
"version": "1.0.0",
"dependencies": {
"nacos": "^1.0.0"
"nacos": "^2.5.0"
}
}

View File

@ -2,5 +2,19 @@
"packages": [
"packages/*"
],
"version": "1.1.1"
"command": {
"bootstrap": {
"hoist": true,
"noCi": true,
"npmClientArgs": [
"--no-package-lock"
]
},
"publish": {
"ignoreChanges": [
"*.md"
]
}
},
"version": "2.6.0"
}

View File

@ -6,14 +6,14 @@
"contributors": "contributors -f plain -o AUTHORS",
"clean": "lerna clean --yes; rm -rf ./packages/**/package-lock.json",
"bootstrap": "rm -f ./packages/.DS*; lerna bootstrap --no-ci",
"publish": "rm -f ./packages/.DS*; sh scripts/publish.sh",
"release": "rm -f ./packages/.DS*; sh scripts/publish.sh",
"next": "sh scripts/publish.sh --npm-tag next",
"test": "lerna run test",
"cov": "sh scripts/cov.sh",
"ci": "npm run cov",
"build": "lerna run build && cp ./README.md ./packages/nacos/README.md"
},
"license": "Apache",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/nacos-group/nacos-sdk-nodejs/issues"
},

View File

@ -3,6 +3,29 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.0.1](https://github.com/nacos-group/nacos-sdk-nodejs/compare/v2.0.0...v2.0.1) (2021-01-26)
### Bug Fixes
* fix the request address is undefined after SSL is enabled ([#42](https://github.com/nacos-group/nacos-sdk-nodejs/issues/42)) ([6146235](https://github.com/nacos-group/nacos-sdk-nodejs/commit/614623577baa510fefc575f875e2ff3076f8a781))
## [1.1.2](https://github.com/nacos-group/nacos-sdk-nodejs/compare/v1.1.1...v1.1.2) (2019-03-31)
### Bug Fixes
* fix gbk text ([93c3559](https://github.com/nacos-group/nacos-sdk-nodejs/commit/93c3559))
* fix options in direct mode ([2942e19](https://github.com/nacos-group/nacos-sdk-nodejs/commit/2942e19))
# [1.1.0](https://github.com/nacos-group/nacos-sdk-nodejs/compare/v1.0.2...v1.1.0) (2019-01-15)

View File

@ -1,6 +1,6 @@
{
"name": "nacos-config",
"version": "1.1.0",
"version": "2.6.0",
"description": "nacos config client",
"keywords": [
"nacos-config",
@ -26,7 +26,7 @@
"@types/mocha": "^5.2.5",
"@types/node": "^10.9.4",
"contributors": "^0.5.1",
"midway-bin": "^0.3.2",
"midway-bin": "1",
"mm": "^2.4.1",
"pedding": "^1.1.0",
"tslint": "^5.11.0",

View File

@ -44,7 +44,9 @@ export class DataClient extends Base implements BaseClient {
protected httpAgent;
constructor(options: ClientOptions) {
assert(options.endpoint, '[Client] options.endpoint is required');
if(!options.endpoint && !options.serverAddr) {
assert(options.endpoint, '[Client] options.endpoint or options.serverAddr is required');
}
options = Object.assign({}, DEFAULT_OPTIONS, options);
super(options);
@ -52,7 +54,8 @@ export class DataClient extends Base implements BaseClient {
this.snapshot = this.getSnapshot();
this.serverMgr = this.getServerListManager();
this.httpAgent = new HttpAgent({ configuration: this.configuration });
const CustomHttpAgent = this.configuration.get(ClientOptionKeys.HTTP_AGENT);
this.httpAgent = CustomHttpAgent ? new CustomHttpAgent({ configuration: this.configuration }) : new HttpAgent({ configuration: this.configuration });
this.configuration.merge({
snapshot: this.snapshot,
@ -160,7 +163,7 @@ export class DataClient extends Base implements BaseClient {
async publishSingle(dataId, group, content, options) {
checkParameters(dataId, group);
const client = this.getClient(options);
return await client.publishSingle(dataId, group, content);
return await client.publishSingle(dataId, group, content, options && options.type);
}
/**

View File

@ -217,6 +217,10 @@ export class ClientWorker extends Base implements IClientWorker {
}
const postData = {};
// 开启权限验证后需要携带租户,否则没有权限
if (tenant) {
Object.assign(postData, { tenant })
}
postData[ this.listenerDataKey ] = probeUpdate.join('');
const content = await this.httpAgent.request(this.apiRoutePath.LISTENER, {
method: 'POST',
@ -345,9 +349,10 @@ export class ClientWorker extends Base implements IClientWorker {
* @param {String} dataId - id of the data
* @param {String} group - group name of the data
* @param {String} content - config value
* @param {String} type - type of the data
* @return {Boolean} success
*/
async publishSingle(dataId, group, content) {
async publishSingle(dataId, group, content, type) {
await this.httpAgent.request(this.apiRoutePath.PUBLISH, {
method: 'POST',
encode: true,
@ -356,6 +361,8 @@ export class ClientWorker extends Base implements IClientWorker {
group,
content,
tenant: this.namespace,
type,
appName: this.appName
},
});
return true;

View File

@ -18,12 +18,12 @@ import { HTTP_CONFLICT, HTTP_NOT_FOUND, HTTP_OK, VERSION } from './const';
import { ClientOptionKeys, IConfiguration, IServerListManager } from './interface';
import * as urllib from 'urllib';
import * as crypto from 'crypto';
import { encodingParams } from './utils';
import { encodingParams, transformGBKToUTF8 } from './utils';
import * as dns from 'dns';
export class HttpAgent {
options;
currentServer: string;
protected loggerDomain = 'Nacos';
private debugPrefix = this.loggerDomain.toLowerCase();
private debug = require('debug')(`${this.debugPrefix}:${process.pid}:http_agent`);
@ -83,6 +83,22 @@ export class HttpAgent {
return this.configuration.get(ClientOptionKeys.DEFAULT_ENCODING) || 'utf8';
}
get identityKey() {
return this.configuration.get(ClientOptionKeys.IDENTITY_KEY);
}
get identityValue() {
return this.configuration.get(ClientOptionKeys.IDENTITY_VALUE);
}
get endpointQueryParams() {
return this.configuration.get(ClientOptionKeys.ENDPOINT_QUERY_PARAMS)
}
get decodeRes() {
return this.configuration.get(ClientOptionKeys.DECODE_RES);
}
/**
*
@ -107,6 +123,11 @@ export class HttpAgent {
const endTime = Date.now() + timeout;
let lastErr;
if (this.options?.configuration?.innerConfig?.username &&
this.options?.configuration?.innerConfig?.password) {
data.username = this.options.configuration.innerConfig.username;
data.password = this.options.configuration.innerConfig.password;
}
let signStr = data.tenant;
if (data.group && data.tenant) {
signStr = data.tenant + '+' + data.group;
@ -126,6 +147,7 @@ export class HttpAgent {
timeStamp: ts,
exConfigInfo: 'true',
'Spas-Signature': signature,
...this.identityKey ? {[this.identityKey]: this.identityValue} : {}
});
let requestData = data;
@ -153,7 +175,10 @@ export class HttpAgent {
this.debug('%s %s, got %s, body: %j', method, url, res.status, res.data);
switch (res.status) {
case HTTP_OK:
return res.data;
if (this.decodeRes) {
return this.decodeRes(res, method, this.defaultEncoding)
}
return this.decodeResData(res, method);
case HTTP_NOT_FOUND:
return null;
case HTTP_CONFLICT:
@ -171,6 +196,9 @@ export class HttpAgent {
break;
}
} catch (err) {
if (err.code === dns.NOTFOUND) {
throw err;
}
err.url = `${method} ${url}`;
err.data = data;
err.headers = headers;
@ -188,15 +216,28 @@ export class HttpAgent {
if (/:/.test(currentServer)) {
url = `http://${currentServer}`;
if (this.ssl) {
url = `https://${this.currentServer}`;
url = `https://${currentServer}`;
}
} else {
url = `http://${currentServer}:${this.serverPort}`;
if (this.ssl) {
url = `https://${this.currentServer}:${this.serverPort}`;
url = `https://${currentServer}:${this.serverPort}`;
}
}
return `${url}/${this.contextPath}`;
}
decodeResData(res, method = 'GET') {
if (method === 'GET' && /charset=GBK/.test(res.headers[ 'content-type' ]) && this.defaultEncoding === 'utf8') {
try {
return transformGBKToUTF8(res.data);
} catch (err) {
console.error(`transform gbk data to utf8 error, msg=${err.messager}`);
return res.data;
}
} else {
return res.data;
}
}
}

View File

@ -24,6 +24,7 @@ export { DataClient } from './client';
export { ClientWorker } from './client_worker';
export { ServerListManager } from './server_list_mgr';
export { Snapshot } from './snapshot';
export { HttpAgent } from './http_agent'
const APIClientBase = require('cluster-client').APIClientBase;

View File

@ -251,9 +251,10 @@ export interface ClientOptions {
accessKey?: string; // 阿里云的 accessKey
secretKey?: string; // 阿里云的 secretKey
httpclient?: any; // http 请求客户端,默认为 urllib
httpAgent?: any; // httpAgent
appName?: string; // 应用名,可选
ssl?: boolean; // 是否为 https 请求
refreshInterval?: number; // 重新拉地址列表的间隔时间
refreshInterval?: number; // 重新拉地址列表的间隔时间
contextPath?: string; // 请求的 contextPath
clusterName?: string; // 请求的 path
requestTimeout?: number; // 请求超时时间
@ -261,7 +262,13 @@ export interface ClientOptions {
serverAddr?: string; // 用于直连,包含端口
unit?: string; // 内部单元化用
nameServerAddr?: string; // 老的兼容参数,逐步废弃,同 endpoint
username?: string; // 认证的用户名
password?: string; // 认证的密码
cacheDir?: string; // 缓存文件的路径
identityKey?: string; // Identity Key
identityValue?: string; // Identity Value
endpointQueryParams?: string; // endPoint 查询参数 e.g: param_1=1&param_2=2
decodeRes?: (res: any, method?: string, encoding?: string) => any
}
export enum ClientOptionKeys {
@ -285,6 +292,10 @@ export enum ClientOptionKeys {
HTTP_AGENT = 'httpAgent',
SERVER_MGR = 'serverMgr',
DEFAULT_ENCODING = 'defaultEncoding',
IDENTITY_KEY = 'identityKey',
IDENTITY_VALUE = 'identityValue',
DECODE_RES = 'decodeRes',
ENDPOINT_QUERY_PARAMS = 'endpointQueryParams'
}
export interface IConfiguration {

View File

@ -108,6 +108,10 @@ export class ServerListManager extends Base implements IServerListManager {
return this.configuration.get(ClientOptionKeys.CLUSTER_NAME) || 'serverlist';
}
get endpointQueryParams() {
return this.configuration.get(ClientOptionKeys.ENDPOINT_QUERY_PARAMS)
}
get requestTimeout(): number {
return this.configuration.get(ClientOptionKeys.REQUEST_TIMEOUT);
}
@ -226,7 +230,8 @@ export class ServerListManager extends Base implements IServerListManager {
// 获取请求 url
protected getRequestUrl(unit) {
return `http://${this.nameServerAddr}/${this.contextPath}/${this.clusterName}`;
const endpointQueryParams = !!this.endpointQueryParams ? `?${this.endpointQueryParams}` : '';
return `http://${this.nameServerAddr}/${this.contextPath}/${this.clusterName}${endpointQueryParams}`;
}
/**

View File

@ -74,3 +74,8 @@ export function checkParameters(dataIds, group, datumId?) {
assert(exports.isValid(datumId), `[datumId] only allow digital, letter and symbols in [ "_", "-", ".", ":" ], but got ${datumId}`);
}
}
export function transformGBKToUTF8(text) {
return iconv.decode(iconv.encode(text, 'gbk'), 'utf8');
}

View File

@ -15,6 +15,7 @@
* limitations under the License.
*/
import { DataClient } from '../src/client';
import { HttpAgent } from '../src/http_agent'
const mm = require('mm');
const assert = require('assert');
@ -117,4 +118,20 @@ describe('test/client.test.ts', () => {
});
});
it('support custom httpAgent', () => {
class CustomHttpAgent {};
assert(client.httpAgent instanceof HttpAgent);
client = new DataClient({
appName: 'test',
endpoint: 'acm.aliyun.com',
namespace: '81597370-5076-4216-9df5-538a2b55bac3',
accessKey: '4c796a4dcd0d4f5895d4ba83a296b489',
secretKey: 'UjLemP8inirhjMg1NZyY0faOk1E=',
httpclient,
httpAgent: CustomHttpAgent,
ssl: false
});
assert(client.httpAgent instanceof CustomHttpAgent);
});
});

View File

@ -497,5 +497,32 @@ describe('test/client_worker.test.ts', () => {
assert(error && error.name === 'NacosServerConflictError');
});
});
describe('custom decode res data', () => {
before(async () => {
configuration.merge({ decodeRes(res) {
if (/^\d+\.\d+\.\d+\.\d+\:\d+$/.test(res.data)) {
return 'customDecode' + res.data
}
return res.data;
}});
client = getClient(configuration);
await client.publishSingle('com.taobao.hsf.redis', 'DEFAULT_GROUP', '10.123.32.1:8080');
await sleep(1000);
await client.ready();
});
afterEach(mm.restore);
after(async () => {
client.close();
await client.remove('com.taobao.hsf.redis', 'DEFAULT_GROUP');
await rimraf(cacheDir);
});
it('should be handled by decodeRes', async () => {
const content = await client.getConfig('com.taobao.hsf.redis', 'DEFAULT_GROUP');
assert(/^customDecode\d+\.\d+\.\d+\.\d+\:\d+$/.test(content));
});
})
});
});

View File

@ -18,14 +18,6 @@
import { Configuration } from '../src/configuration';
import { DEFAULT_OPTIONS } from '../src/const';
export function delay(timeout) {
return new Promise(resolve => {
setTimeout(() => {
resolve();
}, timeout);
});
}
export function createDefaultConfiguration(config: any) {
return new Configuration(Object.assign({}, DEFAULT_OPTIONS)).merge(config);
}

View File

@ -0,0 +1,14 @@
language: node_js
node_js:
- '8'
- '10'
- '12'
before_install:
- npm i npminstall -g
install:
- npminstall
script:
- npm run ci
after_script:
- npminstall codecov && codecov

View File

@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.0.1](https://github.com/nacos-group/nacos-sdk-nodejs/compare/v2.0.0...v2.0.1) (2021-01-26)
**Note:** Version bump only for package nacos-naming
## [1.1.2](https://github.com/nacos-group/nacos-sdk-nodejs/compare/v1.1.1...v1.1.2) (2019-03-31)
**Note:** Version bump only for package nacos-naming
## [1.1.1](https://github.com/nacos-group/nacos-sdk-nodejs/compare/v1.1.0...v1.1.1) (2019-01-17)

View File

@ -34,14 +34,21 @@ const logger = console;
const client = new NacosNamingClient({
logger,
serverList: '127.0.0.1:8848', // replace to real nacos serverList
namespace: 'public',
});
await client.ready();
const serviceName = 'nodejs.test.domain';
// registry instance
await client.registerInstance(serviceName, '1.1.1.1', 8080, 'NODEJS');
await client.registerInstance(serviceName, '2.2.2.2', 8080, 'NODEJS');
await client.registerInstance(serviceName, {
ip: '1.1.1.1',
port: 8080,
});
await client.registerInstance(serviceName, {
ip: '2.2.2.2',
port: 8080,
});
// subscribe instance
client.subscribe(serviceName, hosts => {
@ -49,33 +56,46 @@ client.subscribe(serviceName, hosts => {
});
// deregister instance
await client.deregisterInstance(serviceName, '1.1.1.1', 8080, 'NODEJS');
await client.deregisterInstance(serviceName, {
ip: '1.1.1.1',
port: 8080,
});
```
## APIs
### Service Discovery
- `registerInstance(serviceName, ip, port, [cluster])` Register an instance to service.
- serviceName <String> Service name
- ip <String> IP of instance
- port <Number> Port of instance
- cluster <String> Virtual cluster name
- `registerInstance(serviceName, instance, [groupName])` Register an instance to service.
- serviceName {String} Service name
- instance {Instance}
- ip {String} IP of instance
- port {Number} Port of instance
- [weight] {Number} weight of the instance, default is 1.0
- [ephemeral] {Boolean} active until the client is alive, default is true
- [clusterName] {String} Virtual cluster name
- [groupName] {String} group name, default is `DEFAULT_GROUP`
- `deregisterInstance(serviceName, ip, port, [cluster])` Delete instance from service.
- serviceName <String> Service name
- ip <String> IP of instance
- port <Number> Port of instance
- cluster <String> Virtual cluster name
- `getAllInstances(serviceName, [clusters])` Query instance list of service.
- serviceName <String> Service name
- clusters <Array> Cluster names
- serviceName {String} Service name
- instance {Instance}
- ip {String} IP of instance
- port {Number} Port of instance
- [weight] {Number} weight of the instance, default is 1.0
- [ephemeral] {Boolean} active until the client is alive, default is true
- [clusterName] {String} Virtual cluster name
- [groupName] {String} group name, default is `DEFAULT_GROUP`
- `getAllInstances(serviceName, [groupName], [clusters], [subscribe])` Query instance list of service.
- serviceName {String} Service name
- [groupName] {String} group name, default is `DEFAULT_GROUP`
- [clusters] {String} Cluster names
- [subscribe] {Boolean} whether subscribe the service, default is true
- `getServerStatus()` Get the status of nacos server, 'UP' or 'DOWN'.
- `subscribe(info, listener)` Subscribe the instances of the service
- info <Object>|<String> service info, if type is string, it's the serviceName
- listener <Function> the listener function
- info {Object}|{String} service info, if type is string, it's the serviceName
- listener {Function} the listener function
- `unSubscribe(info, [listener])` Unsubscribe the instances of the service
- info <Object>|<String> service info, if type is string, it's the serviceName
- listener <Function> the listener function, if not provide, will unSubscribe all listeners under this service
- info {Object}|{String} service info, if type is string, it's the serviceName
- listener {Function} the listener function, if not provide, will unSubscribe all listeners under this service
## Questions & Suggestions

View File

@ -25,6 +25,7 @@ async function test() {
const client = new NacosNamingClient({
logger,
serverList: '127.0.0.1:8848',
namespace: 'public',
});
await client.ready();
@ -38,8 +39,14 @@ async function test() {
console.log(hosts);
});
await client.registerInstance(serviceName, '1.1.1.1', 8080, 'NODEJS');
await client.registerInstance(serviceName, '2.2.2.2', 8080, 'NODEJS');
await client.registerInstance(serviceName, {
ip: '1.1.1.1',
port: 8080,
});
await client.registerInstance(serviceName, {
ip: '2.2.2.2',
port: 8080,
});
// const hosts = await client.getAllInstances(serviceName);
// console.log();
@ -48,7 +55,10 @@ async function test() {
await sleep(5000);
await client.deregisterInstance(serviceName, '1.1.1.1', 8080, 'NODEJS');
await client.deregisterInstance(serviceName, {
ip: '1.1.1.1',
port: 8080,
});
}
test().catch(err => {

View File

@ -1,64 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
const net = require('net');
const NameProxy = require('../lib/naming/proxy');
const HostReactor = require('../lib/naming/host_reactor');
const logger = console;
const server = net.createServer();
server.listen(8080);
const serverProxy = new NameProxy({
logger,
serverList: '127.0.0.1:8848',
});
const hostReactor = new HostReactor({
logger,
serverProxy,
});
async function test() {
await hostReactor.ready();
const serviceName = 'nodejs.test.nodejs.1';
let result = await serverProxy.registerService(serviceName, {
ip: '30.23.176.112',
port: 8080,
cluster: {
name: 'NODEJS',
serviceName,
},
weight: 1.0,
healthy: true,
});
console.log(result);
const doms = await hostReactor.getServiceInfo({
serviceName,
cluster: [ 'NODEJS' ],
allIPs: true,
});
console.log(doms);
result = await serverProxy.deregisterService(serviceName, '30.23.176.112', 8080, 'NODEJS');
console.log(result);
}
test();

97
packages/nacos-naming/index.d.ts vendored Normal file
View File

@ -0,0 +1,97 @@
/*
* Author: ugrg
* Create Time: 2021/8/20 9:12
*/
interface Instance {
instanceId: string,
ip: string, //IP of instance
port: number, //Port of instance
healthy: boolean,
enabled: boolean,
serviceName?: string,
weight?: number,
ephemeral?: boolean,
clusterName?: string
}
export interface Host {
instanceId: string;
ip: string;
port: number;
weight: number;
healthy: boolean;
enabled: boolean;
ephemeral: boolean;
clusterName: string;
serviceName: string;
metadata: any;
instanceHeartBeatInterval: number;
instanceIdGenerator: string;
instanceHeartBeatTimeOut: number;
ipDeleteTimeout: number;
}
type Hosts = Host[];
interface SubscribeInfo {
serviceName: string,
groupName?: string,
clusters?: string
}
interface NacosNamingClientConfig {
logger: typeof console,
serverList: string | string[],
namespace?: string,
username?: string,
password?: string,
endpoint?: string,
vipSrvRefInterMillis?: number,
ssl?: boolean
}
/**
* Nacos服务发现组件
*/
export class NacosNamingClient {
constructor (config: NacosNamingClientConfig);
ready: () => Promise<void>;
// Register an instance to service
registerInstance: (
serviceName: string, //Service name
instance: Instance, //Instance
groupName?: string // group name, default is DEFAULT_GROUP
) => Promise<void>;
// Delete instance from service.
deregisterInstance: (
serviceName: string, //Service name
instance: Instance, //Instance
groupName?: string // group name, default is DEFAULT_GROUP
) => Promise<void>;
// Query instance list of service.
getAllInstances: (
serviceName: string, //Service name
groupName?: string, //group name, default is DEFAULT_GROUP
clusters?: string, //Cluster names
subscribe?: boolean //whether subscribe the service, default is true
) => Promise<Hosts>;
// Select instance list of service.
selectInstances: (
serviceName: string,
groupName?: string,
clusters?: string,
healthy?: boolean,
subscribe?: boolean
) => Promise<Hosts>;
// Get the status of nacos server, 'UP' or 'DOWN'.
getServerStatus: () => 'UP' | 'DOWN';
subscribe: (
info: SubscribeInfo | string, //service info, if type is string, it's the serviceName
listener: (host: Hosts) => void //the listener function
) => void;
unSubscribe: (
info: SubscribeInfo | string, //service info, if type is string, it's the serviceName
listener: (host: Hosts) => void //the listener function
) => void;
}

View File

@ -17,7 +17,7 @@
'use strict';
exports.VERSION = 'Nacos-Java-Client:v0.1.0';
exports.VERSION = 'Nacos-Java-Client:v1.0.0';
exports.ENCODING = 'UTF-8';
@ -46,3 +46,9 @@ exports.SERVER_ADDR_IP_SPLITER = ':';
exports.NAMING_INSTANCE_ID_SPLITTER = '#';
exports.NAMING_DEFAULT_CLUSTER_NAME = 'DEFAULT';
exports.SERVICE_INFO_SPLITER = '@@';
exports.DEFAULT_GROUP = 'DEFAULT_GROUP';
exports.DEFAULT_DELAY = 5000;

View File

@ -24,7 +24,6 @@ const sleep = require('mz-modules/sleep');
class BeatReactor extends Base {
constructor(options = {}) {
assert(options.logger, '[BeatReactor] options.logger is required');
assert(options.serverProxy, '[BeatReactor] options.serverProxy is required');
super(options);
@ -36,10 +35,6 @@ class BeatReactor extends Base {
this.ready(true);
}
get logger() {
return this.options.logger;
}
get serverProxy() {
return this.options.serverProxy;
}
@ -57,20 +52,11 @@ class BeatReactor extends Base {
}
async _beat(beatInfo) {
const params = {
beat: JSON.stringify(beatInfo),
dom: beatInfo.dom,
};
try {
const result = await this.serverProxy.reqAPI(Constants.NACOS_URL_BASE + '/api/clientBeat', params);
const jsonObject = JSON.parse(result);
if (jsonObject) {
this._clientBeatInterval = jsonObject.clientBeatInterval;
}
} catch (err) {
err.message = '[CLIENT-BEAT] failed to send beat: ' + JSON.stringify(beatInfo) + ', caused by ' + err.message;
this.emit('error', err);
}
if (beatInfo.scheduled) return;
beatInfo.scheduled = true;
this._clientBeatInterval = await this.serverProxy.sendBeat(beatInfo);
beatInfo.scheduled = false;
}
async _startBeat() {
@ -78,15 +64,14 @@ class BeatReactor extends Base {
this._isRunning = true;
while (!this._isClosed) {
for (const beatInfo of this._dom2Beat.values()) {
this._beat(beatInfo);
}
await Promise.all(Array.from(this._dom2Beat.values())
.map(beatInfo => this._beat(beatInfo)));
await sleep(this._clientBeatInterval);
}
this._isRunning = false;
}
close() {
async _close() {
this._isClosed = true;
this._isRunning = false;
this._dom2Beat.clear();

View File

@ -17,6 +17,7 @@
'use strict';
const utils = require('../util');
const Base = require('sdk-base');
const assert = require('assert');
const Instance = require('./instance');
@ -34,10 +35,7 @@ class NacosNamingClient extends Base {
assert(options.logger, '');
super(Object.assign({}, defaultOptions, options, { initMethod: '_init' }));
this._serverProxy = new NamingProxy({
logger: this.logger,
serverList: this.options.serverList,
});
this._serverProxy = new NamingProxy(this.options);
this._beatReactor = new BeatReactor({
serverProxy: this._serverProxy,
logger: this.logger,
@ -56,45 +54,63 @@ class NacosNamingClient extends Base {
return this.options.logger;
}
async registerInstance(serviceName, ip, port, clusterName = Constants.NAMING_DEFAULT_CLUSTER_NAME) {
let instance = null;
if (typeof ip === 'object') {
instance = new Instance(ip);
} else {
instance = new Instance({
ip,
port,
weight: 1,
clusterName,
});
async registerInstance(serviceName, instance, groupName = Constants.DEFAULT_GROUP) {
if (!(instance instanceof Instance)) {
instance = new Instance(instance);
}
const beatInfo = {
port: instance.port,
ip: instance.ip,
weight: instance.weight,
metadata: instance.metadata,
dom: serviceName,
};
this._beatReactor.addBeatInfo(serviceName, beatInfo);
await this._serverProxy.registerService(serviceName, instance);
const serviceNameWithGroup = utils.getGroupedName(serviceName, groupName);
if (instance.ephemeral) {
const beatInfo = {
serviceName: serviceNameWithGroup,
ip: instance.ip,
port: instance.port,
cluster: instance.clusterName,
weight: instance.weight,
metadata: instance.metadata,
scheduled: false,
};
this._beatReactor.addBeatInfo(serviceNameWithGroup, beatInfo);
}
await this._serverProxy.registerService(serviceNameWithGroup, groupName, instance);
}
async deregisterInstance(serviceName, ip, port, clusterName = Constants.NAMING_DEFAULT_CLUSTER_NAME) {
this._beatReactor.removeBeatInfo(serviceName, ip, port);
await this._serverProxy.deregisterService(serviceName, ip, port, clusterName);
async deregisterInstance(serviceName, instance, groupName = Constants.DEFAULT_GROUP) {
if (!(instance instanceof Instance)) {
instance = new Instance(instance);
}
const serviceNameWithGroup = utils.getGroupedName(serviceName, groupName);
this._beatReactor.removeBeatInfo(serviceNameWithGroup, instance.ip, instance.port);
await this._serverProxy.deregisterService(serviceName, instance);
}
async getAllInstances(serviceName, clusters = []) {
const serviceInfo = await this._hostReactor.getServiceInfo({
serviceName,
clusters,
allIPs: false,
env: '',
});
async getAllInstances(serviceName, groupName = Constants.DEFAULT_GROUP, clusters = '', subscribe = true) {
let serviceInfo;
const serviceNameWithGroup = utils.getGroupedName(serviceName, groupName);
if (subscribe) {
serviceInfo = await this._hostReactor.getServiceInfo(serviceNameWithGroup, clusters);
} else {
serviceInfo = await this._hostReactor.getServiceInfoDirectlyFromServer(serviceNameWithGroup, clusters);
}
if (!serviceInfo) return [];
return serviceInfo.hosts;
}
async selectInstances(serviceName, groupName = Constants.DEFAULT_GROUP, clusters = '', healthy = true, subscribe = true) {
let serviceInfo;
const serviceNameWithGroup = utils.getGroupedName(serviceName, groupName);
if (subscribe) {
serviceInfo = await this._hostReactor.getServiceInfo(serviceNameWithGroup, clusters);
} else {
serviceInfo = await this._hostReactor.getServiceInfoDirectlyFromServer(serviceNameWithGroup, clusters);
}
if (!serviceInfo || !serviceInfo.hosts || !serviceInfo.hosts.length) {
return [];
}
return serviceInfo.hosts.filter(host => {
return host.healthy === healthy && host.enabled && host.weight > 0;
});
}
async getServerStatus() {
const isHealthy = await this._serverProxy.serverHealthy();
return isHealthy ? 'UP' : 'DOWN';
@ -106,7 +122,12 @@ class NacosNamingClient extends Base {
serviceName: info,
};
}
this._hostReactor.subscribe(info, listener);
const groupName = info.groupName || Constants.DEFAULT_GROUP;
const serviceNameWithGroup = utils.getGroupedName(info.serviceName, groupName);
this._hostReactor.subscribe({
serviceName: serviceNameWithGroup,
clusters: info.clusters || '',
}, listener);
}
unSubscribe(info, listener) {
@ -115,12 +136,17 @@ class NacosNamingClient extends Base {
serviceName: info,
};
}
this._hostReactor.unSubscribe(info, listener);
const groupName = info.groupName || Constants.DEFAULT_GROUP;
const serviceNameWithGroup = utils.getGroupedName(info.serviceName, groupName);
this._hostReactor.unSubscribe({
serviceName: serviceNameWithGroup,
clusters: info.clusters || '',
}, listener);
}
close() {
this._beatReactor.close();
this._hostReactor.close();
async _close() {
await this._beatReactor.close();
await this._hostReactor.close();
}
}

View File

@ -22,7 +22,7 @@ const Base = require('sdk-base');
const Constants = require('../const');
const ServiceInfo = require('./service_info');
const PushReceiver = require('./push_receiver');
const localIp = require('address').ip();
const equals = require('equals');
class HostReactor extends Base {
constructor(options = {}) {
@ -30,6 +30,9 @@ class HostReactor extends Base {
assert(options.serverProxy, '[HostReactor] options.serverProxy is required');
super(Object.assign({}, options, { initMethod: '_init' }));
// TODO:
// cacheDir
this._serviceInfoMap = new Map();
this._updatingSet = new Set();
this._futureMap = new Map();
@ -53,7 +56,10 @@ class HostReactor extends Base {
}
async _init() {
await this._pushReceiver.ready();
await Promise.all([
this.serverProxy.ready(),
this._pushReceiver.ready(),
]);
}
processServiceJSON(json) {
@ -87,7 +93,7 @@ class HostReactor extends Base {
const key = host.ip + ':' + host.port;
newHostMap.set(key, host);
if (oldHostMap.has(key) && JSON.stringify(host) !== JSON.stringify(oldHostMap.get(key))) {
if (oldHostMap.has(key) && !equals(host, oldHostMap.get(key))) {
modHosts.push(host);
continue;
}
@ -128,16 +134,13 @@ class HostReactor extends Base {
this.emit(`${serviceInfo.getKey()}_changed`, serviceInfo.hosts, serviceInfo);
// TODO: 本地缓存
}
this.logger.info('[HostReactor] current ips(%d) service: %s -> %s', serviceInfo.ipCount, serviceInfo.name, JSON.stringify(serviceInfo.hosts));
return serviceInfo;
}
_getKey(param) {
const serviceName = param.serviceName;
const clusters = (param.clusters || []).join(',');
const env = param.env || '';
const allIPs = param.allIPs || false;
return ServiceInfo.getKey(serviceName, clusters, env, allIPs);
const clusters = param.clusters || Constants.NAMING_DEFAULT_CLUSTER_NAME;
return ServiceInfo.getKey(serviceName, clusters);
}
subscribe(param, listener) {
@ -146,7 +149,7 @@ class HostReactor extends Base {
if (serviceInfo) {
setImmediate(() => { listener(serviceInfo.hosts); });
} else {
this.getServiceInfo(param);
this.getServiceInfo(param.serviceName, param.clusters || Constants.NAMING_DEFAULT_CLUSTER_NAME);
}
this.on(key + '_changed', listener);
}
@ -160,12 +163,16 @@ class HostReactor extends Base {
}
}
async getServiceInfo(param) {
const serviceName = param.serviceName;
const clusters = (param.clusters || []).join(',');
const env = param.env || '';
const allIPs = param.allIPs || false;
const key = ServiceInfo.getKey(serviceName, clusters, env, allIPs);
async getServiceInfoDirectlyFromServer(serviceName, clusters = Constants.NAMING_DEFAULT_CLUSTER_NAME) {
const result = await this.serverProxy.queryList(serviceName, clusters, 0, false);
if (result) {
return this.processServiceJSON(result);
}
return null;
}
async getServiceInfo(serviceName, clusters = Constants.NAMING_DEFAULT_CLUSTER_NAME) {
const key = ServiceInfo.getKey(serviceName, clusters);
// TODO: failover
let serviceInfo = this._serviceInfoMap.get(key);
@ -173,121 +180,54 @@ class HostReactor extends Base {
serviceInfo = new ServiceInfo({
name: serviceName,
clusters,
env,
allIPs,
hosts: [],
});
this._serviceInfoMap.set(key, serviceInfo);
this._updatingSet.add(key);
if (allIPs) {
await this.updateService4AllIPNow(serviceName, clusters, env);
} else {
await this.updateServiceNow(serviceName, clusters, env);
}
await this.updateServiceNow(serviceName, clusters);
this._updatingSet.delete(key);
} else if (this._updatingSet.has(key)) {
// await updating
await this.await(`${key}_changed`);
}
this._scheduleUpdateIfAbsent(serviceName, clusters, env, allIPs);
this._scheduleUpdateIfAbsent(serviceName, clusters);
return this._serviceInfoMap.get(key);
}
async updateService4AllIPNow(serviceName, clusters, env) {
async updateServiceNow(serviceName, clusters) {
try {
const params = {
dom: serviceName,
clusters,
udpPort: this._pushReceiver.udpPort + '',
};
const key = ServiceInfo.getKey(serviceName, clusters, env, true);
const oldService = this._serviceInfoMap.get(key);
if (oldService) {
params.checksum = oldService.checksum;
}
const result = await this.serverProxy.reqAPI(Constants.NACOS_URL_BASE + '/api/srvAllIP', params);
if (result) {
const serviceInfo = this.processServiceJSON(result);
serviceInfo.isAllIPs = true;
}
this.logger.debug('[HostReactor] updateService4AllIPNow() serviceName: %s, clusters: %s, env: %s, result: %s',
serviceName, clusters, env, result);
} catch (err) {
err.message = 'failed to update serviceName: ' + serviceName + ', caused by: ' + err.message;
this.emit('error', err);
}
}
async updateServiceNow(serviceName, clusters, env) {
const key = ServiceInfo.getKey(serviceName, clusters, env, false);
const oldService = this._serviceInfoMap.get(key);
try {
const params = {
dom: serviceName,
clusters,
udpPort: this._pushReceiver.udpPort + '',
env,
clientIP: localIp,
unconsistentDom: '', // TODO:
};
const envSpliter = ',';
if (env && !env.includes(envSpliter)) {
params.useEnvId = 'true';
}
if (oldService) {
params.checksum = oldService.checksum;
}
const result = await this.serverProxy.reqAPI(Constants.NACOS_URL_BASE + '/api/srvIPXT', params);
const result = await this.serverProxy.queryList(serviceName, clusters, this._pushReceiver.udpPort, false);
if (result) {
this.processServiceJSON(result);
}
this.logger.debug('[HostReactor] updateServiceNow() serviceName: %s, clusters: %s, env: %s, result: %s',
serviceName, clusters, env, result);
this.logger.debug('[HostReactor] updateServiceNow() serviceName: %s, clusters: %s, result: %s', serviceName, clusters, result);
} catch (err) {
err.message = 'failed to update serviceName: ' + serviceName + ', caused by: ' + err.message;
this.emit('error', err);
}
}
async refreshOnly(serviceName, clusters, env, allIPs) {
try {
const params = {
dom: serviceName,
clusters,
udpPort: this._pushReceiver.udpPort + '',
unit: env,
clientIP: localIp,
unconsistentDom: '', // TODO:
};
const envSpliter = ',';
if (env && !env.includes(envSpliter)) {
params.useEnvId = 'true';
}
if (allIPs) {
await this.serverProxy.reqAPI(Constants.NACOS_URL_BASE + '/api/srvAllIP', params);
if (err.status === 404) {
this.logger.warn(err.message);
} else {
await this.serverProxy.reqAPI(Constants.NACOS_URL_BASE + '/api/srvIPXT', params);
this.emit('error', err);
}
}
}
async refreshOnly(serviceName, clusters) {
try {
await this.serverProxy.queryList(serviceName, clusters, this._pushReceiver.udpPort, false);
} catch (err) {
err.message = 'failed to update serviceName: ' + serviceName + ', caused by: ' + err.message;
this.emit('error', err);
}
}
_scheduleUpdateIfAbsent(serviceName, clusters, env, allIPs) {
const key = ServiceInfo.getKey(serviceName, clusters, env, allIPs);
_scheduleUpdateIfAbsent(serviceName, clusters) {
const key = ServiceInfo.getKey(serviceName, clusters);
if (this._futureMap.has(key)) {
return;
}
// 第一次延迟 1s 更新
const timer = setTimeout(() => {
this._doUpdate(serviceName, clusters, env, allIPs)
this._doUpdate(serviceName, clusters)
.catch(err => {
this.emit('error', err);
});
@ -299,35 +239,31 @@ class HostReactor extends Base {
this._futureMap.set(key, task);
}
async _doUpdate(serviceName, clusters, env, allIPs) {
const key = ServiceInfo.getKey(serviceName, clusters, env, allIPs);
async _doUpdate(serviceName, clusters) {
const key = ServiceInfo.getKey(serviceName, clusters);
const task = this._futureMap.get(key);
if (!task) return;
const serviceInfo = this._serviceInfoMap.get(key);
if (!serviceInfo || serviceInfo.lastRefTime <= task.lastRefTime) {
if (allIPs) {
await this.updateService4AllIPNow(serviceName, clusters, env);
} else {
await this.updateServiceNow(serviceName, clusters, env);
}
await this.updateServiceNow(serviceName, clusters);
} else {
this.logger.debug('[HostReactor] refreshOnly, serviceInfo.lastRefTime: %s, task.lastRefTime: %s, serviceName: %s, clusters: %s, env: %s',
serviceInfo.lastRefTime, task.lastRefTime, serviceName, clusters, env);
this.logger.debug('[HostReactor] refreshOnly, serviceInfo.lastRefTime: %s, task.lastRefTime: %s, serviceName: %s, clusters: %s',
serviceInfo.lastRefTime, task.lastRefTime, serviceName, clusters);
// if serviceName already updated by push, we should not override it
// since the push data may be different from pull through force push
await this.refreshOnly(serviceName, clusters, env, allIPs);
await this.refreshOnly(serviceName, clusters);
}
if (this._futureMap.has(key)) {
const serviceInfo = this._serviceInfoMap.get(key);
let delay = 1000;
let delay = Constants.DEFAULT_DELAY;
if (serviceInfo) {
delay = serviceInfo.cacheMillis;
task.lastRefTime = serviceInfo.lastRefTime;
}
const timer = setTimeout(() => {
this._doUpdate(serviceName, clusters, env, allIPs)
this._doUpdate(serviceName, clusters)
.catch(err => {
this.emit('error', err);
});
@ -337,7 +273,7 @@ class HostReactor extends Base {
}
}
close() {
async _close() {
this._pushReceiver.close();
this._updatingSet.clear();

View File

@ -33,6 +33,7 @@ class Instance {
this.healthy = true;
}
this.enabled = typeof data.enabled === 'boolean' ? data.enabled : true;
this.ephemeral = typeof data.ephemeral === 'boolean' ? data.ephemeral : true;
this.clusterName = data.clusterName || Constants.NAMING_DEFAULT_CLUSTER_NAME; // Cluster information of instance
this.serviceName = data.serviceName;
this.metadata = data.metadata || {};

View File

@ -19,14 +19,22 @@
const uuid = require('uuid/v4');
const Base = require('sdk-base');
const utils = require('../util');
const assert = require('assert');
const utility = require('utility');
const Constants = require('../const');
const localIp = require('address').ip();
const sleep = require('mz-modules/sleep');
const defaultOptions = {
namespace: 'default',
httpclient: require('urllib'),
ssl: false,
ak: null,
sk: null,
appName: '',
endpoint: null,
vipSrvRefInterMillis: 30000,
};
const DEFAULT_SERVER_PORT = 8848;
@ -36,19 +44,25 @@ class NameProxy extends Base {
if (typeof options.serverList === 'string' && options.serverList) {
options.serverList = options.serverList.split(',');
}
super(Object.assign({}, defaultOptions, options));
// 硬负载域名
if (options.serverList.length === 1) {
this.nacosDomain = options.serverList[0];
}
super(Object.assign({}, defaultOptions, options, { initMethod: '_init' }));
this.serverList = options.serverList || [];
this.ready(true);
// 硬负载域名
if (this.serverList.length === 1) {
this.nacosDomain = this.serverList[0];
}
this.serversFromEndpoint = [];
this.lastSrvRefTime = 0;
}
get logger() {
return this.options.logger;
}
get endpoint() {
return this.options.endpoint;
}
get namespace() {
return this.options.namespace;
}
@ -57,20 +71,99 @@ class NameProxy extends Base {
return this.options.httpclient;
}
async _callServer(serverAddr, method, api, params) {
const headers = {
async _getServerListFromEndpoint() {
const urlString = 'http://' + this.endpoint + '/nacos/serverlist';
const headers = this._builderHeaders();
const result = await this.httpclient.request(urlString, {
method: 'GET',
headers,
dataType: 'text',
});
if (result.status !== 200) {
throw new Error('Error while requesting: ' + urlString + ', Server returned: ' + result.status);
}
const content = result.data;
return content.split('\r\n');
}
async _refreshSrvIfNeed() {
if (this.serverList.length !== 0) {
return;
}
if (Date.now() - this.lastSrvRefTime < this.options.vipSrvRefInterMillis) {
return;
}
try {
const list = await this._getServerListFromEndpoint();
if (!list || !list.length) {
throw new Error('Can not acquire Nacos list');
}
this.serversFromEndpoint = list;
this.lastSrvRefTime = Date.now();
} catch (err) {
this.logger.warn(err);
}
}
async _init() {
if (!this.endpoint) return;
await this._refreshSrvIfNeed();
this._refreshLoop();
}
async _refreshLoop() {
while (!this._closed) {
await sleep(this.options.vipSrvRefInterMillis);
await this._refreshSrvIfNeed();
}
}
_getSignData(serviceName) {
return serviceName ? Date.now() + '@@' + serviceName : Date.now() + '';
}
_checkSignature(params) {
const { ak, sk, appName } = this.options;
if (!ak && !sk) return;
const signData = this._getSignData(params.serviceName);
const signature = utils.sign(signData, sk);
params.signature = signature;
params.data = signData;
params.ak = ak;
params.app = appName;
}
_builderHeaders() {
return {
'User-Agent': Constants.VERSION,
'Client-Version': Constants.VERSION,
'Accept-Encoding': 'gzip,deflate,sdch',
'Request-Module': 'Naming',
Connection: 'Keep-Alive',
RequestId: uuid(),
'User-Agent': 'Nacos-Java-Client',
};
}
async _callServer(serverAddr, method, api, params = {}) {
this._checkSignature(params);
params.namespaceId = this.namespace;
const headers = this._builderHeaders();
if (!serverAddr.includes(Constants.SERVER_ADDR_IP_SPLITER)) {
serverAddr = serverAddr + Constants.SERVER_ADDR_IP_SPLITER + DEFAULT_SERVER_PORT;
}
const url = (this.options.ssl ? 'https://' : 'http://') + serverAddr + api;
if (this.options.username && this.options.password) {
params.username = this.options.username;
params.password = this.options.password;
}
const result = await this.httpclient.request(url, {
method,
headers,
@ -87,12 +180,13 @@ class NameProxy extends Base {
}
const err = new Error('failed to req API: ' + url + '. code: ' + result.status + ' msg: ' + result.data);
err.name = 'NacosException';
err.status = result.status;
throw err;
}
async reqAPI(api, params, method) {
async _reqAPI(api, params, method) {
// TODO:
const servers = this.serverList;
const servers = this.serverList.length ? this.serverList : this.serversFromEndpoint;
const size = servers.length;
if (size === 0 && !this.nacosDomain) {
@ -123,41 +217,94 @@ class NameProxy extends Base {
throw new Error('failed to req API: ' + api + ' after all servers(' + this.nacosDomain + ') tried');
}
async registerService(serviceName, instance) {
this.logger.info('[NameProxy][REGISTER-SERVICE] registering service: %s with instance:%j', serviceName, instance);
async registerService(serviceName, groupName, instance) {
this.logger.info('[NameProxy][REGISTER-SERVICE] %s registering service: %s with instance:%j', this.namespace, serviceName, instance);
const params = {
tenant: this.namespace,
namespaceId: this.namespace,
serviceName,
groupName,
clusterName: instance.clusterName,
ip: instance.ip,
port: instance.port + '',
weight: instance.weight + '',
enable: instance.enabled ? 'true' : 'false',
healthy: instance.healthy ? 'true' : 'false',
ephemeral: instance.ephemeral ? 'true' : 'false',
metadata: JSON.stringify(instance.metadata),
clusterName: instance.clusterName,
serviceName,
};
return await this.reqAPI(Constants.NACOS_URL_INSTANCE, params, 'PUT');
return await this._reqAPI(Constants.NACOS_URL_INSTANCE, params, 'POST');
}
async deregisterService(serviceName, ip, port, cluster) {
async deregisterService(serviceName, instance) {
this.logger.info('[NameProxy][DEREGISTER-SERVICE] %s deregistering service: %s with instance:%j', this.namespace, serviceName, instance);
const params = {
tenant: this.namespace,
ip,
port: port + '',
namespaceId: this.namespace,
serviceName,
cluster,
clusterName: instance.clusterName,
ip: instance.ip,
port: instance.port + '',
ephemeral: instance.ephemeral !== false ? 'true' : 'false',
};
return await this.reqAPI(Constants.NACOS_URL_INSTANCE, params, 'DELETE');
return await this._reqAPI(Constants.NACOS_URL_INSTANCE, params, 'DELETE');
}
async queryList(serviceName, clusters, udpPort, healthyOnly) {
const params = {
namespaceId: this.namespace,
serviceName,
clusters,
udpPort: udpPort + '',
clientIP: localIp,
healthyOnly: healthyOnly ? 'true' : 'false',
};
return await this._reqAPI(Constants.NACOS_URL_BASE + '/instance/list', params, 'GET');
}
async serverHealthy() {
try {
await this.reqAPI(Constants.NACOS_URL_BASE + '/api/hello', {}, 'GET');
const str = await this._reqAPI(Constants.NACOS_URL_BASE + '/operator/metrics', {}, 'GET');
const result = JSON.parse(str);
return result && result.status === 'UP';
} catch (_) {
return false;
}
return true;
}
async sendBeat(beatInfo) {
try {
const params = {
beat: JSON.stringify(beatInfo),
namespaceId: this.namespace,
serviceName: beatInfo.serviceName,
};
const jsonStr = await this._reqAPI(Constants.NACOS_URL_BASE + '/instance/beat', params, 'PUT');
const result = JSON.parse(jsonStr);
if (result && result.clientBeatInterval) {
return Number(result.clientBeatInterval);
}
} catch (err) {
err.message = `[CLIENT-BEAT] failed to send beat: ${JSON.stringify(beatInfo)}, caused by ${err.message}`;
this.logger.error(err);
}
return Constants.DEFAULT_DELAY;
}
async getServiceList(pageNo, pageSize, groupName) {
const params = {
pageNo: pageNo + '',
pageSize: pageSize + '',
namespaceId: this.namespace,
groupName,
};
// TODO: selector
const result = await this._reqAPI(Constants.NACOS_URL_BASE + '/service/list', params, 'GET');
const json = JSON.parse(result);
return {
count: Number(json.count),
data: json.doms,
};
}
}

View File

@ -66,7 +66,7 @@ class PushReceiver extends Base {
});
this._server.on('message', (msg, rinfo) => this._handlePushMessage(msg, rinfo));
// 随机绑定一个端口
this._server.bind();
this._server.bind({port: 0, exclusive: true}, null);
}
_handlePushMessage(msg, rinfo) {
@ -104,7 +104,13 @@ class PushReceiver extends Base {
}
close() {
this._isClosed = true;
return new Promise((resolve) => {
this._isClosed = true;
if (this._server) {
this._server.close(resolve)
this._server = null;
}
});
}
}

View File

@ -17,20 +17,21 @@
'use strict';
const Constants = require('../const');
const EMPTY = '';
const SPLITER = '@@';
const ALL_IPS = '000--00-ALL_IPS--00--000';
// const ALL_IPS = '000--00-ALL_IPS--00--000';
class ServiceInfo {
constructor(data) {
this.name = data.name || data.dom;
this.groupName = data.groupName;
this.clusters = data.clusters;
this.isAllIPs = data.allIPs || false;
this.cacheMillis = data.cacheMillis || 1000;
this.cacheMillis = data.cacheMillis || Constants.DEFAULT_DELAY;
this.hosts = data.hosts;
this.lastRefTime = data.lastRefTime || 0;
this.checksum = data.checksum || '';
this.env = data.env || '';
this.jsonFromServer = EMPTY;
}
@ -39,81 +40,47 @@ class ServiceInfo {
}
get isValid() {
return !!this.hosts;
const valid = !!this.hosts;
// 如果 this.hosts 是空数组要返回 false
if (valid && Array.isArray(this.hosts)) {
return this.hosts.length > 0;
}
return valid;
}
getKey() {
const name = this.name;
const clusters = this.clusters;
const unit = this.env;
const isAllIPs = this.isAllIPs;
return ServiceInfo.getKey(name, clusters, unit, isAllIPs);
return ServiceInfo.getKey(this.name, this.clusters);
}
toString() {
return this.getKey();
}
static getKey(name, clusters, unit, isAllIPs) {
if (!unit) {
unit = EMPTY;
}
if (clusters && unit) {
return isAllIPs ? name + SPLITER + clusters + SPLITER + unit + SPLITER + ALL_IPS :
name + SPLITER + clusters + SPLITER + unit;
}
static getKey(name, clusters) {
if (clusters) {
return isAllIPs ? name + SPLITER + clusters + SPLITER + ALL_IPS : name + SPLITER + clusters;
return name + SPLITER + clusters;
}
if (unit) {
return isAllIPs ? name + SPLITER + EMPTY + SPLITER + unit + SPLITER + ALL_IPS :
name + SPLITER + EMPTY + SPLITER + unit;
}
return isAllIPs ? name + SPLITER + ALL_IPS : name;
return name;
}
static parse(key) {
const maxKeySectionCount = 4;
const allIpFlagIndex = 3;
const envIndex = 2;
const clusterIndex = 1;
const serviceNameIndex = 0;
static fromKey(key) {
let name;
let clusters;
let env;
let allIPs = false;
let groupName;
const keys = key.split(SPLITER);
if (keys.length >= maxKeySectionCount) {
name = keys[serviceNameIndex];
clusters = keys[clusterIndex];
env = keys[envIndex];
if (keys[allIpFlagIndex] === ALL_IPS) {
allIPs = true;
}
} else if (keys.length >= allIpFlagIndex) {
name = keys[serviceNameIndex];
clusters = keys[clusterIndex];
if (keys[envIndex] === ALL_IPS) {
allIPs = true;
} else {
env = keys[envIndex];
}
} else if (keys.length >= envIndex) {
name = keys[serviceNameIndex];
if (keys[clusterIndex] === ALL_IPS) {
allIPs = true;
} else {
clusters = keys[clusterIndex];
}
const segs = key.split(SPLITER);
if (segs.length === 2) {
groupName = segs[0];
name = segs[1];
} else if (segs.length === 3) {
groupName = segs[0];
name = segs[1];
clusters = segs[2];
}
return new ServiceInfo({
name,
clusters,
env,
allIPs,
groupName,
});
}
}

View File

@ -18,6 +18,8 @@
'use strict';
const zlib = require('zlib');
const crypto = require('crypto');
const Constants = require('../const');
const GZIP_MAGIC = 35615;
@ -36,3 +38,25 @@ exports.tryDecompress = buf => {
}
return zlib.gunzipSync(buf);
};
exports.sign = (data, key) => {
return crypto.createHmac('sha1', key).update(data).digest('base64');
};
exports.getGroupedName = (serviceName, groupName) => {
return groupName + Constants.SERVICE_INFO_SPLITER + serviceName;
};
exports.getServiceName = serviceNameWithGroup => {
if (!serviceNameWithGroup.includes(Constants.SERVICE_INFO_SPLITER)) {
return serviceNameWithGroup;
}
return serviceNameWithGroup.split(Constants.SERVICE_INFO_SPLITER)[1];
};
exports.getGroupName = serviceNameWithGroup => {
if (!serviceNameWithGroup.includes(Constants.SERVICE_INFO_SPLITER)) {
return Constants.DEFAULT_GROUP;
}
return serviceNameWithGroup.split(Constants.SERVICE_INFO_SPLITER)[0];
};

View File

@ -1,11 +1,12 @@
{
"name": "nacos-naming",
"version": "1.1.1",
"version": "2.6.0",
"description": "nacos (https://nacos.io/en-us/) nodejs sdk",
"main": "index.js",
"files": [
"lib",
"index.js"
"index.js",
"index.d.ts"
],
"scripts": {
"autod": "autod",
@ -38,25 +39,26 @@
},
"ci": {
"type": "travis",
"version": "8, 10"
"version": "8, 10, 12"
},
"dependencies": {
"address": "^1.0.3",
"address": "^1.1.0",
"equals": "^1.0.5",
"mz-modules": "^2.1.0",
"sdk-base": "^3.5.1",
"urllib": "^2.33.0",
"utility": "^1.15.0",
"sdk-base": "^3.6.0",
"urllib": "^2.33.3",
"utility": "^1.16.1",
"uuid": "^3.3.2"
},
"devDependencies": {
"autod": "^3.0.1",
"autod": "^3.1.0",
"await-event": "^2.1.0",
"contributors": "^0.5.1",
"egg-bin": "^4.10.0",
"egg-bin": "^4.13.1",
"egg-ci": "^1.11.0",
"eslint": "^5.12.0",
"eslint-config-egg": "^7.1.0",
"mm": "^2.4.1",
"eslint": "^5.16.0",
"eslint-config-egg": "^7.3.1",
"mm": "^2.5.0",
"mz-modules": "^2.1.0"
}
}

View File

@ -36,18 +36,32 @@ describe('test/naming/client.test.js', () => {
await client.ready();
});
afterEach(mm.restore);
after(() => {
client.close();
after(async () => {
await client.close();
});
it('should registerInstance & deregisterInstance ok', async function() {
client.subscribe(serviceName, hosts => {
client.subscribe({
serviceName,
clusters: 'NODEJS',
}, hosts => {
console.log(hosts);
client.emit('update', hosts);
});
client.registerInstance(serviceName, '1.1.1.1', 8080, 'NODEJS');
client.registerInstance(serviceName, '2.2.2.2', 8080, 'NODEJS');
const instance_1 = {
ip: '1.1.1.1',
port: 8080,
clusterName: 'NODEJS',
};
const instance_2 = {
ip: '2.2.2.2',
port: 8080,
clusterName: 'NODEJS',
};
client.registerInstance(serviceName, instance_1);
client.registerInstance(serviceName, instance_2);
let hosts = [];
@ -58,7 +72,7 @@ describe('test/naming/client.test.js', () => {
assert(hosts.find(host => host.ip === '1.1.1.1' && host.port === 8080));
assert(hosts.find(host => host.ip === '2.2.2.2' && host.port === 8080));
client.deregisterInstance(serviceName, '1.1.1.1', 8080, 'NODEJS');
client.deregisterInstance(serviceName, instance_1);
while (hosts.length !== 1) {
hosts = await client.await('update');
@ -66,7 +80,54 @@ describe('test/naming/client.test.js', () => {
assert(hosts.find(host => host.ip === '2.2.2.2' && host.port === 8080));
client.deregisterInstance(serviceName, '2.2.2.2', 8080, 'NODEJS');
client.deregisterInstance(serviceName, instance_2);
while (hosts.length !== 0) {
hosts = await client.await('update');
}
client.unSubscribe({
serviceName,
clusters: 'NODEJS',
});
});
it('should registerInstance & deregisterInstance with default cluster ok', async function() {
client.subscribe(serviceName, hosts => {
console.log(hosts);
client.emit('update', hosts);
});
const instance_1 = {
ip: '1.1.1.1',
port: 8080,
};
const instance_2 = {
ip: '2.2.2.2',
port: 8080,
};
client.registerInstance(serviceName, instance_1);
client.registerInstance(serviceName, instance_2);
let hosts = [];
while (hosts.length !== 2) {
hosts = await client.await('update');
}
assert(hosts.find(host => host.ip === '1.1.1.1' && host.port === 8080));
assert(hosts.find(host => host.ip === '2.2.2.2' && host.port === 8080));
client.deregisterInstance(serviceName, instance_1);
while (hosts.length !== 1) {
hosts = await client.await('update');
}
assert(hosts.find(host => host.ip === '2.2.2.2' && host.port === 8080));
client.deregisterInstance(serviceName, instance_2);
while (hosts.length !== 0) {
hosts = await client.await('update');
@ -104,9 +165,19 @@ describe('test/naming/client.test.js', () => {
});
it('should getAllInstances ok', async function() {
await client.registerInstance(serviceName, '1.1.1.1', 8080, 'NODEJS');
await client.registerInstance(serviceName, '2.2.2.2', 8080, 'OTHERS');
const serviceName = 'nodejs.test.getAllInstances' + process.versions.node;
const instance_1 = {
ip: '1.1.1.1',
port: 8080,
clusterName: 'NODEJS',
};
const instance_2 = {
ip: '2.2.2.2',
port: 8080,
clusterName: 'OTHERS',
};
await client.registerInstance(serviceName, instance_1);
await client.registerInstance(serviceName, instance_2);
await sleep(2000);
let hosts = await client.getAllInstances(serviceName);
@ -114,12 +185,22 @@ describe('test/naming/client.test.js', () => {
assert(hosts.find(host => host.ip === '1.1.1.1' && host.port === 8080));
assert(hosts.find(host => host.ip === '2.2.2.2' && host.port === 8080));
hosts = await client.getAllInstances(serviceName, ['NODEJS']);
hosts = await client.getAllInstances(serviceName, 'DEFAULT_GROUP', 'NODEJS,OTHERS');
assert(hosts.length === 2);
assert(hosts.find(host => host.ip === '1.1.1.1' && host.port === 8080));
assert(hosts.find(host => host.ip === '2.2.2.2' && host.port === 8080));
hosts = await client.getAllInstances(serviceName, 'DEFAULT_GROUP', 'NODEJS,OTHERS', false);
assert(hosts.length === 2);
assert(hosts.find(host => host.ip === '1.1.1.1' && host.port === 8080));
assert(hosts.find(host => host.ip === '2.2.2.2' && host.port === 8080));
hosts = await client.getAllInstances(serviceName, 'DEFAULT_GROUP', 'NODEJS');
assert(hosts.length === 1);
assert(hosts.find(host => host.ip === '1.1.1.1' && host.port === 8080));
await client.deregisterInstance(serviceName, '1.1.1.1', 8080, 'NODEJS');
await client.deregisterInstance(serviceName, '2.2.2.2', 8080, 'OTHERS');
await client.deregisterInstance(serviceName, instance_1);
await client.deregisterInstance(serviceName, instance_2);
await sleep(2000);
@ -127,6 +208,67 @@ describe('test/naming/client.test.js', () => {
assert(hosts.length === 0);
});
it('should selectInstances ok', async function() {
const serviceName = 'nodejs.test.selectInstance' + process.versions.node;
const instance_1 = {
ip: '11.11.11.11',
port: 8080,
healthy: true,
clusterName: 'NODEJS',
ephemeral: false,
};
const instance_2 = {
ip: '22.22.22.22',
port: 8080,
healthy: false,
clusterName: 'OTHERS',
ephemeral: false,
};
const instance_3 = {
ip: '33.33.33.33',
port: 8080,
healthy: true,
clusterName: 'OTHERS',
ephemeral: false,
};
await client.registerInstance(serviceName, instance_1);
await client.registerInstance(serviceName, instance_2);
await client.registerInstance(serviceName, instance_3);
await sleep(2000);
let hosts = await client.selectInstances(serviceName);
assert(hosts.length === 2);
assert(hosts.find(host => host.ip === '11.11.11.11' && host.port === 8080));
assert(hosts.find(host => host.ip === '33.33.33.33' && host.port === 8080));
hosts = await client.selectInstances(serviceName, 'DEFAULT_GROUP', 'NODEJS,OTHERS');
assert(hosts.length === 2);
assert(hosts.find(host => host.ip === '11.11.11.11' && host.port === 8080));
assert(hosts.find(host => host.ip === '33.33.33.33' && host.port === 8080));
hosts = await client.selectInstances(serviceName, 'DEFAULT_GROUP', 'NODEJS,OTHERS', false, false);
assert(hosts.length === 1);
// assert(hosts.find(host => host.ip === '1.1.1.1' && host.port === 8080));
assert(hosts.find(host => host.ip === '22.22.22.22' && host.port === 8080));
hosts = await client.selectInstances(serviceName, 'DEFAULT_GROUP', 'OTHERS');
assert(hosts.length === 1);
assert(hosts.find(host => host.ip === '33.33.33.33' && host.port === 8080));
hosts = await client.selectInstances(serviceName, 'DEFAULT_GROUP', 'OTHERS', false);
assert(hosts.length === 1);
assert(hosts.find(host => host.ip === '22.22.22.22' && host.port === 8080));
await client.deregisterInstance(serviceName, instance_1);
await client.deregisterInstance(serviceName, instance_2);
await client.deregisterInstance(serviceName, instance_3);
await sleep(2000);
hosts = await client.selectInstances(serviceName);
assert(hosts.length === 0);
});
it('should getServerStatus ok', async function() {
let status = await client.getServerStatus();
assert(status === 'UP');

View File

@ -17,6 +17,7 @@
'use strict';
const mm = require('mm');
const assert = require('assert');
const NameProxy = require('../../lib/naming/proxy');
const Instance = require('../../lib/naming/instance');
@ -24,13 +25,24 @@ const HostReactor = require('../../lib/naming/host_reactor');
const logger = console;
const serviceName = 'nodejs.test.' + process.versions.node;
const groupName = 'DEFAULT_GROUP';
const serviceNameWithGroup = groupName + '@@' + serviceName;
describe('test/naming/host_reactor.test.js', () => {
it('should ok', async function() {
const serverProxy = new NameProxy({
let serverProxy;
before(async () => {
serverProxy = new NameProxy({
logger,
serverList: '127.0.0.1:8848',
});
await serverProxy.ready();
});
after(async () => {
await serverProxy.close();
});
afterEach(mm.restore);
it('should ok', async function() {
const hostReactor = new HostReactor({
logger,
serverProxy,
@ -38,7 +50,8 @@ describe('test/naming/host_reactor.test.js', () => {
await hostReactor.ready();
hostReactor.subscribe({
serviceName,
serviceName: serviceNameWithGroup,
clusters: 'NODEJS',
}, hosts => {
hostReactor.emit('update', hosts);
});
@ -52,70 +65,79 @@ describe('test/naming/host_reactor.test.js', () => {
valid: true,
enabled: true,
});
serverProxy.registerService(serviceName, instance);
serverProxy.registerService(serviceNameWithGroup, groupName, instance);
let hosts = [];
while (hosts.length !== 1) {
hosts = await hostReactor.await('update');
}
assert(hosts.find(host => host.ip === '1.1.1.1' && host.port === 8080));
assert(hosts.some(host => host.ip === '1.1.1.1' && host.port === 8080));
const key = serviceNameWithGroup + '@@NODEJS';
console.log(hostReactor.getServiceInfoMap);
assert(hostReactor.getServiceInfoMap && hostReactor.getServiceInfoMap[serviceName]);
assert(hostReactor.getServiceInfoMap && hostReactor.getServiceInfoMap[key]);
hostReactor.processServiceJSON(JSON.stringify({
dom: serviceName,
clusters: '',
isAllIPs: false,
metadata: {},
dom: serviceNameWithGroup,
cacheMillis: 10000,
useSpecifiedURL: false,
hosts: null,
lastRefTime: 1542806333263,
checksum: 'c1762ddd16f512ae13bcf2c5a07e2e221542806333263',
name: serviceNameWithGroup,
checksum: 'cc4e0ff13773c6d443d9ba0532b32810',
lastRefTime: 1556603044852,
env: '',
clusters: 'NODEJS',
}));
assert(hostReactor.getServiceInfoMap[serviceName].hosts.length === 1);
assert(hostReactor.getServiceInfoMap[key].hosts.length === 1);
hostReactor.processServiceJSON(JSON.stringify({
dom: serviceName,
clusters: '',
isAllIPs: false,
metadata: {},
dom: serviceNameWithGroup,
cacheMillis: 10000,
hosts: hostReactor.getServiceInfoMap[serviceName].hosts,
lastRefTime: 1542806333262,
checksum: 'c1762ddd16f512ae13bcf2c5a07e2e221542806333263',
useSpecifiedURL: false,
hosts: hostReactor.getServiceInfoMap[key].hosts,
name: serviceNameWithGroup,
checksum: 'cc4e0ff13773c6d443d9ba0532b32811',
lastRefTime: 1556603044852,
env: '',
clusters: 'NODEJS',
}));
assert(hostReactor.getServiceInfoMap[serviceName].hosts.length === 1);
assert(hostReactor.getServiceInfoMap[key].hosts.length === 1);
hostReactor.processServiceJSON(JSON.stringify({
dom: serviceName,
clusters: '',
isAllIPs: false,
metadata: {},
dom: serviceNameWithGroup,
cacheMillis: 10000,
hosts: hostReactor.getServiceInfoMap[serviceName].hosts.map(host => {
useSpecifiedURL: false,
hosts: hostReactor.getServiceInfoMap[key].hosts.map(host => {
return Object.assign({}, host, { enabled: false });
}),
lastRefTime: 1542806333262,
checksum: 'c1762ddd16f512ae13bcf2c5a07e2e221542806333263',
name: serviceNameWithGroup,
checksum: 'cc4e0ff13773c6d443d9ba0532b32812',
lastRefTime: 1556603044852,
env: '',
clusters: 'NODEJS',
}));
assert(hostReactor.getServiceInfoMap[serviceName].hosts.length === 1);
assert(!hostReactor.getServiceInfoMap[serviceName].hosts[0].enabled);
assert(hostReactor.getServiceInfoMap[key].hosts.length === 1);
assert(!hostReactor.getServiceInfoMap[key].hosts[0].enabled);
hostReactor.processServiceJSON(JSON.stringify({
dom: serviceName + '_1',
clusters: '',
isAllIPs: false,
metadata: {},
dom: 'mock_dom',
cacheMillis: 10000,
hosts: hostReactor.getServiceInfoMap[serviceName].hosts,
lastRefTime: 1542806333263,
checksum: 'c1762ddd16f512ae13bcf2c5a07e2e221542806333263',
useSpecifiedURL: false,
hosts: hostReactor.getServiceInfoMap[key].hosts,
name: 'mock_dom',
checksum: 'cc4e0ff13773c6d443d9ba0532b32813',
lastRefTime: 1556603044852,
env: '',
clusters: 'NODEJS',
}));
assert(hostReactor.getServiceInfoMap[serviceName + '_1']);
assert(hostReactor.getServiceInfoMap['mock_dom@@NODEJS']);
serverProxy.deregisterService(serviceName, instance.ip, instance.port, instance.clusterName);
serverProxy.deregisterService(serviceName, instance);
while (hosts.length !== 0) {
hosts = await hostReactor.await('update');
@ -125,20 +147,18 @@ describe('test/naming/host_reactor.test.js', () => {
assert(hosts.length === 0);
};
hostReactor.subscribe({
serviceName,
serviceName: serviceNameWithGroup,
clusters: 'NODEJS',
}, listener);
hostReactor.unSubscribe({
serviceName,
serviceName: serviceNameWithGroup,
clusters: 'NODEJS',
}, listener);
hostReactor.close();
await hostReactor.close();
});
it('should updateService4AllIPNow ok', async function() {
const serverProxy = new NameProxy({
logger,
serverList: '127.0.0.1:8848',
});
it('should updateServiceNow ok', async () => {
const hostReactor = new HostReactor({
logger,
serverProxy,
@ -146,17 +166,36 @@ describe('test/naming/host_reactor.test.js', () => {
await hostReactor.ready();
const arr = await Promise.all([
hostReactor.getServiceInfo({
serviceName,
allIPs: true,
}),
hostReactor.getServiceInfo({
serviceName,
allIPs: true,
}),
hostReactor.getServiceInfo(serviceNameWithGroup, 'NODEJS'),
hostReactor.getServiceInfo(serviceNameWithGroup, 'NODEJS'),
]);
console.log(arr);
assert(arr && arr.length === 2);
assert(arr.every(item => !!item));
hostReactor.close();
await hostReactor.close();
});
it('should emit error if serverProxy.queryList failed', async () => {
mm.error(serverProxy, 'queryList', 'mock error');
const hostReactor = new HostReactor({
logger,
serverProxy,
});
await hostReactor.ready();
hostReactor.updateServiceNow(serviceNameWithGroup, 'NODEJS');
await assert.rejects(async () => {
await hostReactor.await('error');
}, /failed to update serviceName/);
hostReactor.refreshOnly(serviceNameWithGroup, 'NODEJS');
await assert.rejects(async () => {
await hostReactor.await('error');
}, /failed to update serviceName/);
await hostReactor.close();
});
});

View File

@ -28,7 +28,7 @@ describe('test/naming/instance.test.js', () => {
valid: true,
enabled: false,
});
assert(instance1.toString() === '{"ip":"1.1.1.1","port":8888,"weight":1,"healthy":true,"enabled":false,"clusterName":"DEFAULT","metadata":{}}');
assert(instance1.toString() === '{"ip":"1.1.1.1","port":8888,"weight":1,"healthy":true,"enabled":false,"ephemeral":true,"clusterName":"DEFAULT","metadata":{}}');
assert(instance1.toInetAddr() === '1.1.1.1:8888');
const instance2 = new Instance({
@ -37,7 +37,7 @@ describe('test/naming/instance.test.js', () => {
healthy: true,
enabled: false,
});
assert(instance2.toString() === '{"ip":"1.1.1.1","port":8888,"weight":1,"healthy":true,"enabled":false,"clusterName":"DEFAULT","metadata":{}}');
assert(instance2.toString() === '{"ip":"1.1.1.1","port":8888,"weight":1,"healthy":true,"enabled":false,"ephemeral":true,"clusterName":"DEFAULT","metadata":{}}');
assert(instance2.toInetAddr() === '1.1.1.1:8888');
assert(instance1.equal(instance2));
@ -46,7 +46,7 @@ describe('test/naming/instance.test.js', () => {
ip: '1.1.1.1',
port: 8888,
});
assert(instance3.toString() === '{"ip":"1.1.1.1","port":8888,"weight":1,"healthy":true,"enabled":true,"clusterName":"DEFAULT","metadata":{}}');
assert(instance3.toString() === '{"ip":"1.1.1.1","port":8888,"weight":1,"healthy":true,"enabled":true,"ephemeral":true,"clusterName":"DEFAULT","metadata":{}}');
assert(instance3.toInetAddr() === '1.1.1.1:8888');
assert(!instance1.equal(instance3));

View File

@ -18,9 +18,11 @@
'use strict';
const mm = require('mm');
const http = require('http');
const assert = require('assert');
const sleep = require('mz-modules/sleep');
const NameProxy = require('../../lib/naming/proxy');
const Instance = require('../../lib/naming/instance');
const logger = console;
const serviceName = 'nodejs.test.' + process.versions.node;
@ -34,41 +36,43 @@ describe('test/naming/proxy.test.js', () => {
serverList: '127.0.0.1',
});
await proxy.ready();
let result = await proxy.registerService(serviceName, {
const groupName = 'DEFAULT_GROUP';
const instance = new Instance({
ip: '1.1.1.1',
port: 8080,
clusterName: 'NODEJS',
weight: 1.0,
metadata: {},
serviceName,
});
let result = await proxy.registerService(serviceName, groupName, instance);
assert(result === 'ok');
await sleep(1000);
let jsonStr = await proxy.reqAPI('/nacos/v1/ns/instances', {
serviceName,
}, 'GET');
let jsonStr = await proxy.queryList(serviceName, 'NODEJS', '', 'false');
let serviceInfo = JSON.parse(jsonStr);
assert(serviceInfo && serviceInfo.dom === serviceName);
assert(serviceInfo && serviceInfo.dom === 'DEFAULT_GROUP@@' + serviceName);
assert(serviceInfo.hosts && serviceInfo.hosts.length === 1);
assert(serviceInfo.hosts[0].ip === '1.1.1.1');
assert(serviceInfo.hosts[0].port === 8080);
result = await proxy.deregisterService(serviceName, '1.1.1.1', 8080, 'NODEJS');
result = await proxy.deregisterService(serviceName, instance);
assert(result === 'ok');
jsonStr = await proxy.reqAPI('/nacos/v1/ns/instances', {
serviceName,
}, 'GET');
jsonStr = await proxy.queryList(serviceName, 'NODEJS', '', 'false');
serviceInfo = JSON.parse(jsonStr);
assert(serviceInfo && serviceInfo.dom === serviceName);
assert(serviceInfo && serviceInfo.dom === 'DEFAULT_GROUP@@' + serviceName);
assert(serviceInfo.hosts && serviceInfo.hosts.length === 0);
await proxy.close();
});
it('should serverHealthy ok', async function() {
const proxy = new NameProxy({
logger,
endpoint: '127.0.0.1:8849',
serverList: '127.0.0.1:8848',
});
await proxy.ready();
@ -76,19 +80,28 @@ describe('test/naming/proxy.test.js', () => {
let isHealthy = await proxy.serverHealthy();
assert(isHealthy);
mm.http.request(/\/nacos\/v1\/ns\/api\/hello/, '', {
mm.http.request(/\/nacos\/v1\/ns\/operator\/metrics/, '{"status": "DOWN"}', {
statusCode: 200,
});
isHealthy = await proxy.serverHealthy();
assert(!isHealthy);
mm.http.request(/\/nacos\/v1\/ns\/operator\/metrics/, '', {
statusCode: 304,
});
isHealthy = await proxy.serverHealthy();
assert(isHealthy);
assert(!isHealthy);
mm.http.request(/\/nacos\/v1\/ns\/api\/hello/, '', {
mm.http.request(/\/nacos\/v1\/ns\/operator\/metrics/, '', {
statusCode: 500,
});
isHealthy = await proxy.serverHealthy();
assert(!isHealthy);
await proxy.close();
});
it('should failed if no server available', async function() {
@ -100,6 +113,8 @@ describe('test/naming/proxy.test.js', () => {
const isHealthy = await proxy.serverHealthy();
assert(!isHealthy);
await proxy.close();
});
it('should support naocsDomain', async function() {
@ -113,11 +128,127 @@ describe('test/naming/proxy.test.js', () => {
let isHealthy = await proxy.serverHealthy();
assert(isHealthy);
mm.http.request(/\/nacos\/v1\/ns\/api\/hello/, '', {
mm.http.request(/\/nacos\/v1\/ns\/operator\/metrics/, '', {
statusCode: 500,
});
isHealthy = await proxy.serverHealthy();
assert(!isHealthy);
await proxy.close();
});
it('should sendBeat ok', async () => {
const proxy = new NameProxy({
logger,
serverList: '127.0.0.1:8848',
});
await proxy.ready();
const beatInfo = {
serviceName: 'DEFAULT_GROUP@@' + serviceName,
ip: '1.1.1.1',
port: 8080,
cluster: 'NODEJS',
weight: 1,
metadata: {},
scheduled: false,
};
let result = await proxy.sendBeat(beatInfo);
console.log(result);
assert(typeof result === 'number' && result > 0);
mm.error(proxy, '_reqAPI', 'mock error');
result = await proxy.sendBeat(beatInfo);
assert(result === 0);
await proxy.close();
});
it('should getServiceList ok', async () => {
const proxy = new NameProxy({
logger,
serverList: '127.0.0.1:8848',
});
await proxy.ready();
let data = await proxy.getServiceList(0, 10, 'DEFAULT_GROUP');
console.log(data);
const groupName = 'DEFAULT_GROUP';
const instance = new Instance({
ip: '1.1.1.1',
port: 8080,
clusterName: 'NODEJS',
weight: 1.0,
metadata: {},
serviceName,
});
let result = await proxy.registerService(serviceName, groupName, instance);
assert(result === 'ok');
await sleep(1000);
data = await proxy.getServiceList(0, 10, 'DEFAULT_GROUP');
console.log(data);
result = await proxy.deregisterService(serviceName, instance);
assert(result === 'ok');
await proxy.close();
});
describe('endpoint', () => {
let server;
before(done => {
server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'application/text' });
res.end('127.0.0.1:8848');
});
server.listen(8849, done);
});
after(done => {
server.once('close', done);
server.close();
});
it('should get serverList from endpoint', async () => {
const proxy = new NameProxy({
logger,
endpoint: '127.0.0.1:8849',
vipSrvRefInterMillis: 5000,
});
await proxy.ready();
assert(proxy.serverList && proxy.serverList.length === 0);
assert(proxy.serversFromEndpoint && proxy.serversFromEndpoint.length === 1);
assert(proxy.lastSrvRefTime > 0);
const isHealthy = await proxy.serverHealthy();
assert(isHealthy);
await sleep(6000);
const lastSrvRefTime = proxy.lastSrvRefTime;
assert(Date.now() - lastSrvRefTime < 5000);
await proxy._refreshSrvIfNeed();
assert(proxy.lastSrvRefTime === lastSrvRefTime);
await proxy.close();
});
it('should not healthy', async () => {
const proxy = new NameProxy({
logger,
endpoint: 'unknown.com',
});
await proxy.ready();
const isHealthy = await proxy.serverHealthy();
assert(!isHealthy);
await proxy.close();
});
});
});

View File

@ -36,63 +36,51 @@ describe('test/naming/service_info.test.js', () => {
weight: 1.0,
valid: true,
enabled: true,
ephemeral: true,
metadata: {},
}],
});
assert(serviceInfo.ipCount === 1);
assert(serviceInfo.isValid);
assert(serviceInfo.getKey() === 'xxx@@clusters@@000--00-ALL_IPS--00--000');
assert(serviceInfo.getKey() === 'xxx@@clusters');
mm(serviceInfo, 'isAllIPs', false);
assert(serviceInfo.getKey() === 'xxx@@clusters');
mm(serviceInfo, 'env', 'test');
assert(serviceInfo.getKey() === 'xxx@@clusters@@test');
assert(serviceInfo.getKey() === 'xxx@@clusters');
mm(serviceInfo, 'isAllIPs', true);
assert(serviceInfo.getKey() === 'xxx@@clusters@@test@@000--00-ALL_IPS--00--000');
assert(serviceInfo.getKey() === 'xxx@@clusters');
mm(serviceInfo, 'clusters', null);
assert(serviceInfo.getKey() === 'xxx@@@@test@@000--00-ALL_IPS--00--000');
assert(serviceInfo.getKey() === 'xxx');
mm(serviceInfo, 'isAllIPs', false);
assert(serviceInfo.getKey() === 'xxx@@@@test');
mm(serviceInfo, 'env', null);
assert(serviceInfo.getKey() === 'xxx');
mm(serviceInfo, 'isAllIPs', true);
assert(serviceInfo.getKey() === 'xxx@@000--00-ALL_IPS--00--000');
assert(serviceInfo.toString() === 'xxx@@000--00-ALL_IPS--00--000');
assert(serviceInfo.getKey() === 'xxx');
assert(serviceInfo.toString() === 'xxx');
});
it('should parse from string', () => {
let data = ServiceInfo.parse('xxx@@clusters@@000--00-ALL_IPS--00--000');
assert(data.name === 'xxx');
assert(data.clusters === 'clusters');
assert(data.isAllIPs);
data = ServiceInfo.parse('xxx@@clusters@@test@@000--00-ALL_IPS--00--000');
assert(data.name === 'xxx');
assert(data.clusters === 'clusters');
assert(data.isAllIPs);
assert(data.env === 'test');
data = ServiceInfo.parse('xxx@@clusters');
assert(data.name === 'xxx');
assert(data.clusters === 'clusters');
assert(!data.isAllIPs);
data = ServiceInfo.parse('xxx@@clusters@@test');
assert(data.name === 'xxx');
assert(data.clusters === 'clusters');
assert(data.env === 'test');
assert(!data.isAllIPs);
data = ServiceInfo.parse('xxx@@000--00-ALL_IPS--00--000');
let data = ServiceInfo.fromKey('DEFAULT_GROUP@@xxx');
assert(data.name === 'xxx');
assert(data.groupName === 'DEFAULT_GROUP');
assert(!data.clusters);
assert(!data.env);
assert(data.isAllIPs);
assert(!data.isAllIPs);
data = ServiceInfo.fromKey('DEFAULT_GROUP@@xxx@@clusters');
assert(data.name === 'xxx');
assert(data.clusters === 'clusters');
assert(!data.isAllIPs);
assert(data.groupName === 'DEFAULT_GROUP');
data = ServiceInfo.fromKey('xxx');
assert(!data.name);
assert(!data.clusters);
assert(!data.isAllIPs);
assert(!data.groupName);
});
});

View File

@ -29,4 +29,24 @@ describe('test/util/index.test.js', () => {
const zipped = zlib.gzipSync(buf);
assert.deepEqual(util.tryDecompress(zipped), buf);
});
it('should getGroupedName ok', () => {
const serviceWithGroupName = util.getGroupedName('serviceName', 'groupName');
assert(serviceWithGroupName === 'groupName@@serviceName');
});
it('should getServiceName ok', () => {
assert(util.getServiceName('groupName@@serviceName') === 'serviceName');
assert(util.getServiceName('serviceName') === 'serviceName');
});
it('should getGroupName ok', () => {
assert(util.getGroupName('groupName@@serviceName') === 'groupName');
assert(util.getGroupName('serviceName') === 'DEFAULT_GROUP');
});
it('should sign ok', () => {
const result = util.sign('1556606455782@@nodejs.test', 'xxxxxx');
assert(result === 'hhmW6gWCqR0g8dctGZXQclYomYg=');
});
});

View File

@ -3,6 +3,22 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [2.0.1](https://github.com/nacos-group/nacos-sdk-nodejs/compare/v2.0.0...v2.0.1) (2021-01-26)
**Note:** Version bump only for package nacos
## [1.1.2](https://github.com/nacos-group/nacos-sdk-nodejs/compare/v1.1.1...v1.1.2) (2019-03-31)
**Note:** Version bump only for package nacos
## [1.1.1](https://github.com/nacos-group/nacos-sdk-nodejs/compare/v1.1.0...v1.1.1) (2019-01-17)
**Note:** Version bump only for package nacos

View File

@ -21,6 +21,13 @@
npm install nacos --save
```
## Version Mapping
Node.js SDK \ Nacos Server | 0.x.0 | 1.0.0 |
--- | --- | --- |
1.x | √ | |
2.x | | √ |
## Usage
### Service Discovery
@ -34,14 +41,21 @@ const logger = console;
const client = new NacosNamingClient({
logger,
serverList: '127.0.0.1:8848', // replace to real nacos serverList
namespace: 'public',
});
await client.ready();
const serviceName = 'nodejs.test.domain';
// registry instance
await client.registerInstance(serviceName, '1.1.1.1', 8080, 'NODEJS');
await client.registerInstance(serviceName, '2.2.2.2', 8080, 'NODEJS');
await client.registerInstance(serviceName, {
ip: '1.1.1.1',
port: 8080,
});
await client.registerInstance(serviceName, {
ip: '2.2.2.2',
port: 8080,
});
// subscribe instance
client.subscribe(serviceName, hosts => {
@ -49,7 +63,10 @@ client.subscribe(serviceName, hosts => {
});
// deregister instance
await client.deregisterInstance(serviceName, '1.1.1.1', 8080, 'NODEJS');
await client.deregisterInstance(serviceName, {
ip: '1.1.1.1',
port: 8080,
});
```
### Config Service
@ -100,27 +117,36 @@ default value: [ClientOptions default value](https://github.com/nacos-group/naco
### Service Discovery
- `registerInstance(serviceName, ip, port, [cluster])` Register an instance to service.
- serviceName <String> Service name
- ip <String> IP of instance
- port <Number> Port of instance
- cluster <String> Virtual cluster name
- `registerInstance(serviceName, instance, [groupName])` Register an instance to service.
- serviceName {String} Service name
- instance {Instance}
- ip {String} IP of instance
- port {Number} Port of instance
- [weight] {Number} weight of the instance, default is 1.0
- [ephemeral] {Boolean} active until the client is alive, default is true
- [clusterName] {String} Virtual cluster name
- [groupName] {String} group name, default is `DEFAULT_GROUP`
- `deregisterInstance(serviceName, ip, port, [cluster])` Delete instance from service.
- serviceName <String> Service name
- ip <String> IP of instance
- port <Number> Port of instance
- cluster <String> Virtual cluster name
- `getAllInstances(serviceName, [clusters])` Query instance list of service.
- serviceName <String> Service name
- clusters <Array> Cluster names
- serviceName {String} Service name
- instance {Instance}
- ip {String} IP of instance
- port {Number} Port of instance
- [weight] {Number} weight of the instance, default is 1.0
- [ephemeral] {Boolean} active until the client is alive, default is true
- [clusterName] {String} Virtual cluster name
- [groupName] {String} group name, default is `DEFAULT_GROUP`
- `getAllInstances(serviceName, [groupName], [clusters], [subscribe])` Query instance list of service.
- serviceName {String} Service name
- [groupName] {String} group name, default is `DEFAULT_GROUP`
- [clusters] {String} Cluster names
- [subscribe] {Boolean} whether subscribe the service, default is true
- `getServerStatus()` Get the status of nacos server, 'UP' or 'DOWN'.
- `subscribe(info, listener)` Subscribe the instances of the service
- info <Object | String> service info, if type is string, it's the serviceName
- listener <Function> the listener function
- unSubscribe(info, [listener]) Unsubscribe the instances of the service
- info <Object | String> service info, if type is string, it's the serviceName
- listener <Function> the listener function, if not provide, will unSubscribe all listeners under this service
- info {Object}|{String} service info, if type is string, it's the serviceName
- listener {Function} the listener function
- `unSubscribe(info, [listener])` Unsubscribe the instances of the service
- info {Object}|{String} service info, if type is string, it's the serviceName
- listener {Function} the listener function, if not provide, will unSubscribe all listeners under this service
### Config Service
@ -151,6 +177,10 @@ Please let us know how can we help. Do check out [issues](https://github.com/nac
PR is welcome.
nacos-sdk-nodejs ding group 44654232
![image](https://user-images.githubusercontent.com/17695352/172582005-c661e2a0-49fa-425c-bf99-785bb7cd4dc1.png)
## License
[Apache License V2](LICENSE)

View File

@ -1,6 +1,6 @@
{
"name": "nacos",
"version": "1.1.1",
"version": "2.6.0",
"description": "nacos client main package",
"keywords": [
"nacos",
@ -12,8 +12,8 @@
"main": "dist/index.js",
"author": "czy88840616@gmail.com",
"dependencies": {
"nacos-config": "^1.1.0",
"nacos-naming": "^1.1.1"
"nacos-config": "^2.6.0",
"nacos-naming": "^2.6.0"
},
"devDependencies": {
"@types/mocha": "^5.2.5",

View File

@ -1,7 +1,4 @@
#!/usr/bin/env bash
npm run contributors
npm run build
git add .
git commit -m "chore: ready for publish"
lerna publish $* --conventional-commits
lerna publish $*