Compare commits
60 Commits
Author | SHA1 | Date |
---|---|---|
|
8a5568de77 | |
|
57cd21060b | |
|
7fa34ff6a6 | |
|
22c0e57947 | |
|
cdea9a26b1 | |
|
50ac48e026 | |
|
3c6840bdd7 | |
|
4c9af68899 | |
|
cc6667c5b1 | |
|
d6ee163a36 | |
|
41e3965870 | |
|
a3feb781a7 | |
|
910f7e4bfe | |
|
7f1870778e | |
|
d044e6cef4 | |
|
005134d725 | |
|
e3cb1d5955 | |
|
6df7ece0a8 | |
|
27a8fa7651 | |
|
a3f33bd901 | |
|
194629a25b | |
|
34055b4117 | |
|
9ae37cef32 | |
|
a9a7fe64d8 | |
|
3940268149 | |
|
2b703c0621 | |
|
01affdbb51 | |
|
643dd12ab8 | |
|
c9f5808675 | |
|
3e9670d9cf | |
|
d5e2dd8ae0 | |
|
61a415ec6f | |
|
17ea984a61 | |
|
180dcf72fe | |
|
8c06bda57f | |
|
b219a870bd | |
|
e5c1359509 | |
|
744fc56f29 | |
|
1a63399df0 | |
|
f1bfe2d065 | |
|
371f2dbc82 | |
|
84f6032f44 | |
|
133550f482 | |
|
edb996043e | |
|
688ca8f38d | |
|
614623577b | |
|
2ea1abb9b0 | |
|
4182e142bf | |
|
f6d5a7462a | |
|
989eba2dc9 | |
|
34368536f9 | |
|
f7b9ce72ba | |
|
b06340b757 | |
|
8caaf2504c | |
|
7793dd5a73 | |
|
dfde567b25 | |
|
06cad6fa4e | |
|
d81a4ab8e9 | |
|
93c3559a05 | |
|
2942e19b90 |
10
.travis.yml
10
.travis.yml
|
@ -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
|
||||
|
|
6
AUTHORS
6
AUTHORS
|
@ -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>
|
||||
|
|
23
CHANGELOG.md
23
CHANGELOG.md
|
@ -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)
|
||||
|
||||
|
||||
|
|
72
README.md
72
README.md
|
@ -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
|
||||

|
||||
|
||||
|
||||
## License
|
||||
|
||||
[Apache License V2](LICENSE)
|
||||
|
|
|
@ -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);
|
||||
})();
|
|
@ -2,6 +2,6 @@
|
|||
"name": "nacos-example",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"nacos": "^1.0.0"
|
||||
"nacos": "^2.5.0"
|
||||
}
|
||||
}
|
||||
|
|
16
lerna.json
16
lerna.json
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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¶m_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 {
|
||||
|
|
|
@ -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}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
||||
});
|
||||
|
|
|
@ -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));
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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)
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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();
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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 || {};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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=');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||

|
||||
|
||||
|
||||
## License
|
||||
|
||||
[Apache License V2](LICENSE)
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 $*
|
||||
|
|
Loading…
Reference in New Issue