@danqiusheng/nest-nacos 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/LICENSE +21 -21
- package/README.en.md +197 -0
- package/README.md +353 -385
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/nacos-config.service.d.ts +25 -45
- package/dist/nacos-config.service.d.ts.map +1 -1
- package/dist/nacos-config.service.js +241 -102
- package/dist/nacos-config.service.js.map +1 -1
- package/dist/nacos-naming.service.d.ts +16 -46
- package/dist/nacos-naming.service.d.ts.map +1 -1
- package/dist/nacos-naming.service.js +119 -88
- package/dist/nacos-naming.service.js.map +1 -1
- package/dist/nacos.decorators.d.ts +0 -4
- package/dist/nacos.decorators.d.ts.map +1 -1
- package/dist/nacos.decorators.js +1 -12
- package/dist/nacos.decorators.js.map +1 -1
- package/dist/nacos.health.d.ts +2 -6
- package/dist/nacos.health.d.ts.map +1 -1
- package/dist/nacos.health.js +5 -25
- package/dist/nacos.health.js.map +1 -1
- package/dist/nacos.interfaces.d.ts +85 -57
- package/dist/nacos.interfaces.d.ts.map +1 -1
- package/dist/nacos.module.d.ts +2 -11
- package/dist/nacos.module.d.ts.map +1 -1
- package/dist/nacos.module.js +22 -19
- package/dist/nacos.module.js.map +1 -1
- package/dist/nacos.providers.d.ts +4 -11
- package/dist/nacos.providers.d.ts.map +1 -1
- package/dist/nacos.providers.js +37 -23
- package/dist/nacos.providers.js.map +1 -1
- package/dist/nacos.utils.d.ts +6 -0
- package/dist/nacos.utils.d.ts.map +1 -0
- package/dist/nacos.utils.js +79 -0
- package/dist/nacos.utils.js.map +1 -0
- package/package.json +19 -10
package/README.md
CHANGED
|
@@ -1,194 +1,77 @@
|
|
|
1
|
-
# nest-nacos
|
|
1
|
+
# @danqiusheng/nest-nacos
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
面向 NestJS 10/11 的 Nacos 2.x 配置中心与服务注册发现模块。
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## 目录
|
|
10
|
-
|
|
11
|
-
1. [介绍](#1-介绍)
|
|
12
|
-
2. [安装](#2-安装)
|
|
13
|
-
3. [快速开始](#3-快速开始)
|
|
14
|
-
4. [同步配置 forRoot](#4-同步配置-forroot)
|
|
15
|
-
5. [异步配置 forRootAsync](#5-异步配置-forrootasync)
|
|
16
|
-
6. [服务发现](#6-服务发现)
|
|
17
|
-
7. [配置中心](#7-配置中心)
|
|
18
|
-
8. [装饰器](#8-装饰器)
|
|
19
|
-
9. [健康检查](#9-健康检查)
|
|
20
|
-
10. [API: NacosNamingService](#10-api-nacosnamingservice)
|
|
21
|
-
11. [API: NacosConfigService](#11-api-nacosconfigservice)
|
|
22
|
-
12. [配置项参考](#12-配置项参考)
|
|
23
|
-
13. [常见问题](#13-常见问题)
|
|
24
|
-
|
|
25
|
-
---
|
|
26
|
-
|
|
27
|
-
## 1. 介绍
|
|
28
|
-
|
|
29
|
-
**nest-nacos** 是一个 NestJS 插件,封装了 `nacos-sdk-nodejs`,为 NestJS 应用提供 **服务发现** 和 **配置中心** 能力。基于 gRPC 传输协议,支持 Nacos 2.x / 3.x 服务端。
|
|
30
|
-
|
|
31
|
-
### 特性
|
|
32
|
-
|
|
33
|
-
| 特性 | 说明 |
|
|
34
|
-
|------|------|
|
|
35
|
-
| 动态模块 | 支持 `forRoot` / `forRootAsync`,可配合 `@nestjs/config` 从环境变量读取配置 |
|
|
36
|
-
| 自动注册 | 模块初始化时自动注册服务实例,销毁时自动注销,无需手动管理生命周期 |
|
|
37
|
-
| 配置热更新 | 订阅配置变更,服务端实时推送,内置本地缓存与 JSON 解析 |
|
|
38
|
-
| gRPC 传输 | 默认使用 gRPC,服务端实时推送,无需 UDP 端口,支持自动重连 |
|
|
39
|
-
| 装饰器注入 | 提供 `@InjectNacosNaming` / `@InjectNacosConfig` 装饰器,简化依赖注入 |
|
|
40
|
-
| 健康检查 | 内置 `NacosHealthIndicator`,可对接 `@nestjs/terminus` 暴露健康端点 |
|
|
41
|
-
|
|
42
|
-
### 兼容性
|
|
43
|
-
|
|
44
|
-
| nest-nacos | nacos-sdk-nodejs | Nacos Server | 传输协议 |
|
|
45
|
-
|---|---|---|---|
|
|
46
|
-
| 1.x | 2.6.x | 3.x / 2.x | gRPC(默认)/ HTTP(仅 2.x) |
|
|
47
|
-
|
|
48
|
-
---
|
|
49
|
-
|
|
50
|
-
## 2. 安装
|
|
51
|
-
|
|
52
|
-
### 步骤 1:安装 nest-nacos 和 nacos SDK
|
|
53
|
-
|
|
54
|
-
```bash
|
|
55
|
-
# 使用 npm
|
|
56
|
-
npm install nest-nacos nacos
|
|
57
|
-
|
|
58
|
-
# 使用 yarn
|
|
59
|
-
yarn add nest-nacos nacos
|
|
60
|
-
|
|
61
|
-
# 使用 pnpm
|
|
62
|
-
pnpm add nest-nacos nacos
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
### 步骤 2:确保宿主依赖已安装
|
|
66
|
-
|
|
67
|
-
以下包是 peerDependencies,通常 NestJS 项目中已存在:
|
|
5
|
+
## 1. 安装
|
|
68
6
|
|
|
69
7
|
```bash
|
|
70
|
-
npm install @
|
|
8
|
+
npm install @danqiusheng/nest-nacos nacos
|
|
71
9
|
```
|
|
72
10
|
|
|
73
|
-
|
|
11
|
+
## 2. 最简接入
|
|
74
12
|
|
|
75
|
-
|
|
13
|
+
未开启 Nacos 鉴权时,只需要一个地址:
|
|
76
14
|
|
|
77
|
-
```
|
|
78
|
-
npm install @nestjs/config
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
> **提示**:Nacos Server 版本选择:如果使用 Nacos 3.x,必须使用 gRPC 传输(默认)。HTTP 传输仅支持 Nacos 2.x。
|
|
82
|
-
|
|
83
|
-
---
|
|
84
|
-
|
|
85
|
-
## 3. 快速开始
|
|
86
|
-
|
|
87
|
-
以下是一个最小可用示例,展示如何在 `AppModule` 中同时启用服务发现和配置中心:
|
|
88
|
-
|
|
89
|
-
```typescript
|
|
15
|
+
```ts
|
|
90
16
|
import { Module } from '@nestjs/common';
|
|
91
|
-
import { NacosModule } from 'nest-nacos';
|
|
17
|
+
import { NacosModule } from '@danqiusheng/nest-nacos';
|
|
92
18
|
|
|
93
19
|
@Module({
|
|
94
|
-
imports: [
|
|
95
|
-
NacosModule.forRoot({
|
|
96
|
-
naming: {
|
|
97
|
-
serverList: '127.0.0.1:8848',
|
|
98
|
-
username: 'nacos',
|
|
99
|
-
password: 'nacos',
|
|
100
|
-
},
|
|
101
|
-
config: {
|
|
102
|
-
serverAddr: '127.0.0.1:8848',
|
|
103
|
-
},
|
|
104
|
-
instances: [
|
|
105
|
-
{ serviceName: 'order-service', ip: '192.168.1.100', port: 3000 },
|
|
106
|
-
],
|
|
107
|
-
}),
|
|
108
|
-
],
|
|
20
|
+
imports: [NacosModule.forRoot('127.0.0.1:8848')],
|
|
109
21
|
})
|
|
110
22
|
export class AppModule {}
|
|
111
23
|
```
|
|
112
24
|
|
|
113
|
-
|
|
25
|
+
这会同时启用配置中心和服务注册发现,默认使用:
|
|
114
26
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
27
|
+
| 配置 | 默认值 |
|
|
28
|
+
|---|---|
|
|
29
|
+
| namespace | `public` |
|
|
30
|
+
| group | `DEFAULT_GROUP` |
|
|
31
|
+
| transport | HTTP OpenAPI |
|
|
32
|
+
| 重试次数 | 3 次(包含第一次调用) |
|
|
118
33
|
|
|
119
|
-
|
|
34
|
+
开启用户名密码鉴权时,也只需要配置一次连接信息:
|
|
120
35
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
36
|
+
```ts
|
|
37
|
+
NacosModule.forRoot({
|
|
38
|
+
serverAddr: '127.0.0.1:8848',
|
|
39
|
+
username: 'nacos',
|
|
40
|
+
password: 'nacos',
|
|
41
|
+
});
|
|
42
|
+
```
|
|
124
43
|
|
|
125
|
-
|
|
126
|
-
import { NacosModule } from 'nest-nacos';
|
|
44
|
+
`serverAddr` 支持集群地址数组:
|
|
127
45
|
|
|
46
|
+
```ts
|
|
128
47
|
NacosModule.forRoot({
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
config: {
|
|
136
|
-
serverAddr: '127.0.0.1:8848',
|
|
137
|
-
namespace: 'production',
|
|
138
|
-
},
|
|
139
|
-
// 启动时自动注册的实例
|
|
140
|
-
instances: [
|
|
141
|
-
{
|
|
142
|
-
serviceName: 'order-service',
|
|
143
|
-
ip: '192.168.1.100',
|
|
144
|
-
port: 3000,
|
|
145
|
-
weight: 1.0,
|
|
146
|
-
metadata: { version: '1.0.0' },
|
|
147
|
-
},
|
|
148
|
-
],
|
|
149
|
-
// 启动时预订阅的配置项
|
|
150
|
-
subscribeConfigs: [
|
|
151
|
-
{ dataId: 'database.yaml', group: 'DEFAULT_GROUP' },
|
|
152
|
-
{ dataId: 'redis.yaml', group: 'DEFAULT_GROUP' },
|
|
153
|
-
],
|
|
154
|
-
})
|
|
48
|
+
serverAddr: ['10.0.0.11:8848', '10.0.0.12:8848'],
|
|
49
|
+
namespace: 'production',
|
|
50
|
+
username: process.env.NACOS_USERNAME,
|
|
51
|
+
password: process.env.NACOS_PASSWORD,
|
|
52
|
+
ssl: false,
|
|
53
|
+
});
|
|
155
54
|
```
|
|
156
55
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
## 5. 异步配置 forRootAsync
|
|
56
|
+
## 3. 使用环境变量
|
|
160
57
|
|
|
161
|
-
|
|
58
|
+
插件不会隐式读取环境变量。推荐使用 Nest `ConfigModule` 明确传入配置:
|
|
162
59
|
|
|
163
|
-
```
|
|
60
|
+
```ts
|
|
164
61
|
import { Module } from '@nestjs/common';
|
|
165
62
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
|
166
|
-
import { NacosModule } from 'nest-nacos';
|
|
63
|
+
import { NacosModule } from '@danqiusheng/nest-nacos';
|
|
167
64
|
|
|
168
65
|
@Module({
|
|
169
66
|
imports: [
|
|
170
67
|
ConfigModule.forRoot({ isGlobal: true }),
|
|
171
68
|
NacosModule.forRootAsync({
|
|
172
|
-
imports: [ConfigModule],
|
|
173
69
|
inject: [ConfigService],
|
|
174
70
|
useFactory: (config: ConfigService) => ({
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
password: config.get('NACOS_PASSWORD'),
|
|
180
|
-
},
|
|
181
|
-
config: {
|
|
182
|
-
serverAddr: config.get('NACOS_SERVER_ADDR'),
|
|
183
|
-
namespace: config.get('NACOS_NAMESPACE'),
|
|
184
|
-
},
|
|
185
|
-
instances: [
|
|
186
|
-
{
|
|
187
|
-
serviceName: config.get('SERVICE_NAME'),
|
|
188
|
-
ip: config.get('SERVICE_IP'),
|
|
189
|
-
port: config.get<number>('SERVICE_PORT'),
|
|
190
|
-
},
|
|
191
|
-
],
|
|
71
|
+
serverAddr: config.getOrThrow<string>('NACOS_SERVER_ADDR'),
|
|
72
|
+
namespace: config.get<string>('NACOS_NAMESPACE') ?? 'public',
|
|
73
|
+
username: config.get<string>('NACOS_USERNAME'),
|
|
74
|
+
password: config.get<string>('NACOS_PASSWORD'),
|
|
192
75
|
}),
|
|
193
76
|
}),
|
|
194
77
|
],
|
|
@@ -196,314 +79,399 @@ import { NacosModule } from 'nest-nacos';
|
|
|
196
79
|
export class AppModule {}
|
|
197
80
|
```
|
|
198
81
|
|
|
199
|
-
|
|
82
|
+
`.env` 示例:
|
|
200
83
|
|
|
201
|
-
```
|
|
84
|
+
```dotenv
|
|
202
85
|
NACOS_SERVER_ADDR=127.0.0.1:8848
|
|
203
86
|
NACOS_NAMESPACE=public
|
|
204
87
|
NACOS_USERNAME=nacos
|
|
205
88
|
NACOS_PASSWORD=nacos
|
|
206
|
-
SERVICE_NAME=order-service
|
|
207
|
-
SERVICE_IP=192.168.1.100
|
|
208
|
-
SERVICE_PORT=3000
|
|
209
89
|
```
|
|
210
90
|
|
|
211
|
-
|
|
91
|
+
`forRootAsync` 同时支持 `useFactory`、`useClass` 和 `useExisting`。
|
|
212
92
|
|
|
213
|
-
|
|
93
|
+
## 4. 自动注册当前服务
|
|
214
94
|
|
|
215
|
-
|
|
95
|
+
在模块配置中加入 `instances`,应用启动时自动注册,关闭时自动注销:
|
|
216
96
|
|
|
217
|
-
|
|
97
|
+
```ts
|
|
98
|
+
NacosModule.forRoot({
|
|
99
|
+
serverAddr: '127.0.0.1:8848',
|
|
100
|
+
instances: [
|
|
101
|
+
{
|
|
102
|
+
serviceName: 'order-service',
|
|
103
|
+
ip: '192.168.1.20',
|
|
104
|
+
port: 3000,
|
|
105
|
+
weight: 1,
|
|
106
|
+
ephemeral: true,
|
|
107
|
+
metadata: { env: 'prod', version: '1.2.0' },
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
});
|
|
111
|
+
```
|
|
218
112
|
|
|
219
|
-
|
|
220
|
-
import { Injectable, OnModuleInit } from '@nestjs/common';
|
|
221
|
-
import { NacosNamingService, InjectNacosNaming } from 'nest-nacos';
|
|
113
|
+
如果 IP 或端口来自环境变量,应使用 `forRootAsync` 构造 `instances`。
|
|
222
114
|
|
|
223
|
-
|
|
224
|
-
export class RpcService implements OnModuleInit {
|
|
225
|
-
@InjectNacosNaming()
|
|
226
|
-
private readonly naming: NacosNamingService;
|
|
227
|
-
|
|
228
|
-
async onModuleInit() {
|
|
229
|
-
// 订阅 user-service 实例变更(服务端推送)
|
|
230
|
-
this.naming.subscribe('user-service', (hosts) => {
|
|
231
|
-
console.log(`user-service 实例数: ${hosts.length}`);
|
|
232
|
-
});
|
|
233
|
-
}
|
|
115
|
+
## 5. 服务注册与发现
|
|
234
116
|
|
|
235
|
-
|
|
236
|
-
async callUserService() {
|
|
237
|
-
const instances = await this.naming.selectInstances('user-service');
|
|
238
|
-
if (instances.length === 0) {
|
|
239
|
-
throw new Error('user-service 无可用实例');
|
|
240
|
-
}
|
|
241
|
-
const target = instances[0];
|
|
242
|
-
return `http://${target.ip}:${target.port}`;
|
|
243
|
-
}
|
|
117
|
+
### 注入服务
|
|
244
118
|
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
119
|
+
```ts
|
|
120
|
+
import { Injectable } from '@nestjs/common';
|
|
121
|
+
import {
|
|
122
|
+
InjectNacosNaming,
|
|
123
|
+
NacosNamingService,
|
|
124
|
+
} from '@danqiusheng/nest-nacos';
|
|
125
|
+
|
|
126
|
+
@Injectable()
|
|
127
|
+
export class ServiceDiscovery {
|
|
128
|
+
constructor(
|
|
129
|
+
@InjectNacosNaming()
|
|
130
|
+
private readonly naming: NacosNamingService,
|
|
131
|
+
) {}
|
|
254
132
|
}
|
|
255
133
|
```
|
|
256
134
|
|
|
257
|
-
|
|
135
|
+
也可以直接使用构造器注入 `NacosNamingService`。
|
|
258
136
|
|
|
259
|
-
|
|
137
|
+
### 手动注册和注销
|
|
260
138
|
|
|
261
|
-
|
|
139
|
+
```ts
|
|
140
|
+
const instance = {
|
|
141
|
+
serviceName: 'payment-service',
|
|
142
|
+
ip: '192.168.1.30',
|
|
143
|
+
port: 3000,
|
|
144
|
+
metadata: { env: 'prod' },
|
|
145
|
+
};
|
|
262
146
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
147
|
+
await this.naming.register(instance);
|
|
148
|
+
await this.naming.deregister(instance);
|
|
149
|
+
```
|
|
266
150
|
|
|
267
|
-
|
|
268
|
-
export class DbService implements OnModuleInit {
|
|
269
|
-
@InjectNacosConfig()
|
|
270
|
-
private readonly nacosConfig: NacosConfigService;
|
|
151
|
+
通过插件注册的实例会被记录,Nest 模块销毁时会自动注销。
|
|
271
152
|
|
|
272
|
-
|
|
153
|
+
### 获取健康实例
|
|
273
154
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
155
|
+
```ts
|
|
156
|
+
const instances = await this.naming.selectInstances('payment-service');
|
|
157
|
+
```
|
|
277
158
|
|
|
278
|
-
|
|
279
|
-
this.nacosConfig.subscribe('database.yaml', 'DEFAULT_GROUP', (content) => {
|
|
280
|
-
this.dbConfig = JSON.parse(content);
|
|
281
|
-
console.log('数据库配置已热更新');
|
|
282
|
-
});
|
|
283
|
-
}
|
|
159
|
+
只选择一个实例:
|
|
284
160
|
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
JSON.stringify({ host: '10.0.0.1', port: 3306 }),
|
|
291
|
-
);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
161
|
+
```ts
|
|
162
|
+
const instance = await this.naming.selectInstance('payment-service', {
|
|
163
|
+
strategy: 'roundRobin',
|
|
164
|
+
metadata: { env: 'prod' },
|
|
165
|
+
});
|
|
294
166
|
```
|
|
295
167
|
|
|
296
|
-
|
|
168
|
+
支持三种策略:
|
|
297
169
|
|
|
298
|
-
|
|
170
|
+
| strategy | 行为 |
|
|
171
|
+
|---|---|
|
|
172
|
+
| `random` | 随机选择,默认值 |
|
|
173
|
+
| `roundRobin` | 按服务轮询 |
|
|
174
|
+
| `weighted` | 按 Nacos 实例权重随机选择 |
|
|
299
175
|
|
|
300
|
-
|
|
176
|
+
找不到满足健康状态和 metadata 条件的实例时,`selectInstance()` 会抛出异常。
|
|
301
177
|
|
|
302
|
-
|
|
178
|
+
### 订阅服务变化
|
|
303
179
|
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
| `@InjectNacosNamingClient()` | `NacosNamingClient` | 注入原始 SDK 客户端(高级用法) |
|
|
309
|
-
| `@InjectNacosConfigClient()` | `NacosConfigClient` | 注入原始 SDK 客户端(高级用法) |
|
|
180
|
+
```ts
|
|
181
|
+
const listener = (hosts: unknown[]) => {
|
|
182
|
+
console.log('payment-service 实例变化', hosts);
|
|
183
|
+
};
|
|
310
184
|
|
|
311
|
-
|
|
185
|
+
this.naming.subscribe('payment-service', listener);
|
|
186
|
+
this.naming.unSubscribe('payment-service', listener);
|
|
187
|
+
|
|
188
|
+
// 不传 listener:取消该服务的全部本地订阅
|
|
189
|
+
this.naming.unSubscribe('payment-service');
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### 更新权重
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
await this.naming.updateWeight(instance, 0.5);
|
|
196
|
+
```
|
|
312
197
|
|
|
313
|
-
|
|
198
|
+
## 6. 配置中心
|
|
199
|
+
|
|
200
|
+
### 注入服务
|
|
201
|
+
|
|
202
|
+
```ts
|
|
314
203
|
import { Injectable } from '@nestjs/common';
|
|
315
204
|
import {
|
|
316
|
-
NacosNamingService,
|
|
317
|
-
NacosConfigService,
|
|
318
|
-
InjectNacosNaming,
|
|
319
205
|
InjectNacosConfig,
|
|
320
|
-
|
|
206
|
+
NacosConfigService,
|
|
207
|
+
} from '@danqiusheng/nest-nacos';
|
|
321
208
|
|
|
322
209
|
@Injectable()
|
|
323
|
-
export class
|
|
324
|
-
@InjectNacosNaming()
|
|
325
|
-
private readonly naming: NacosNamingService;
|
|
326
|
-
|
|
327
|
-
@InjectNacosConfig()
|
|
328
|
-
private readonly config: NacosConfigService;
|
|
329
|
-
|
|
330
|
-
// 也可以用构造函数注入
|
|
210
|
+
export class SettingsService {
|
|
331
211
|
constructor(
|
|
332
|
-
|
|
333
|
-
private readonly
|
|
212
|
+
@InjectNacosConfig()
|
|
213
|
+
private readonly nacos: NacosConfigService,
|
|
334
214
|
) {}
|
|
335
215
|
}
|
|
336
216
|
```
|
|
337
217
|
|
|
338
|
-
|
|
218
|
+
### 读取配置
|
|
219
|
+
|
|
220
|
+
```ts
|
|
221
|
+
const raw = await this.nacos.getConfig('application.yaml');
|
|
222
|
+
|
|
223
|
+
const config = await this.nacos.getParsedConfig<{
|
|
224
|
+
server: { port: number };
|
|
225
|
+
}>('application.yaml');
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
格式默认根据 `dataId` 后缀自动识别:
|
|
229
|
+
|
|
230
|
+
| 后缀/format | 返回值 |
|
|
231
|
+
|---|---|
|
|
232
|
+
| `.json` / `json` | JSON 对象或数组 |
|
|
233
|
+
| `.yaml`、`.yml` / `yaml` | YAML 解析结果 |
|
|
234
|
+
| `.properties` / `properties` | 键值对象 |
|
|
235
|
+
| 其他 / `txt` | 原始字符串 |
|
|
236
|
+
|
|
237
|
+
强制按 JSON 解析:
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
const config = await this.nacos.getConfigAsJson<AppConfig>(
|
|
241
|
+
'application.json',
|
|
242
|
+
);
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### 发布和删除配置
|
|
246
|
+
|
|
247
|
+
```ts
|
|
248
|
+
const published = await this.nacos.publish(
|
|
249
|
+
'application.json',
|
|
250
|
+
'DEFAULT_GROUP',
|
|
251
|
+
JSON.stringify({ featureEnabled: true }),
|
|
252
|
+
);
|
|
253
|
+
|
|
254
|
+
const removed = await this.nacos.remove(
|
|
255
|
+
'application.json',
|
|
256
|
+
'DEFAULT_GROUP',
|
|
257
|
+
);
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
返回值为 Nacos SDK 的最终布尔结果。public namespace 删除已处理 `tenant=public` 导致的假成功问题。
|
|
261
|
+
|
|
262
|
+
### 订阅热更新
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
const listener = (raw: string, parsed?: unknown) => {
|
|
266
|
+
console.log('配置已更新', parsed);
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
this.nacos.subscribe(
|
|
270
|
+
'application.yaml',
|
|
271
|
+
'DEFAULT_GROUP',
|
|
272
|
+
listener,
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
this.nacos.unSubscribe(
|
|
276
|
+
'application.yaml',
|
|
277
|
+
'DEFAULT_GROUP',
|
|
278
|
+
listener,
|
|
279
|
+
);
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
同一个 `dataId + group` 只建立一个底层 SDK 订阅,多个业务 listener 不会导致重复回调。
|
|
283
|
+
|
|
284
|
+
## 7. 启动时预加载配置
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
NacosModule.forRoot({
|
|
288
|
+
serverAddr: '127.0.0.1:8848',
|
|
289
|
+
subscribeConfigs: [
|
|
290
|
+
{ dataId: 'application.yaml', format: 'yaml' },
|
|
291
|
+
{ dataId: 'database.properties', format: 'properties' },
|
|
292
|
+
{
|
|
293
|
+
dataId: 'optional.json',
|
|
294
|
+
required: false,
|
|
295
|
+
subscribe: false,
|
|
296
|
+
},
|
|
297
|
+
],
|
|
298
|
+
});
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
默认行为:
|
|
302
|
+
|
|
303
|
+
- group 为 `DEFAULT_GROUP`。
|
|
304
|
+
- format 为 `auto`。
|
|
305
|
+
- `subscribe: true`,预加载后继续监听热更新。
|
|
306
|
+
- `required: true`,读取失败会按启动失败策略处理。
|
|
307
|
+
|
|
308
|
+
合并所有已声明配置:
|
|
309
|
+
|
|
310
|
+
```ts
|
|
311
|
+
const merged = await this.nacos.getMergedConfig<AppConfig>();
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
对象会按声明顺序深度合并,后面的配置覆盖前面的同名字段。
|
|
315
|
+
|
|
316
|
+
## 8. 本地缓存和启动失败策略
|
|
317
|
+
|
|
318
|
+
```ts
|
|
319
|
+
NacosModule.forRoot({
|
|
320
|
+
serverAddr: '127.0.0.1:8848',
|
|
321
|
+
subscribeConfigs: [{ dataId: 'application.yaml' }],
|
|
322
|
+
configLoading: {
|
|
323
|
+
failureMode: 'fallback',
|
|
324
|
+
cacheDir: '.nacos-cache',
|
|
325
|
+
retry: {
|
|
326
|
+
attempts: 3,
|
|
327
|
+
delayMs: 300,
|
|
328
|
+
backoffFactor: 2,
|
|
329
|
+
maxDelayMs: 3000,
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
| failureMode | 行为 |
|
|
336
|
+
|---|---|
|
|
337
|
+
| `throw` | 读取必需配置失败时阻止应用启动,默认值 |
|
|
338
|
+
| `fallback` | 优先读取本地缓存;无缓存且配置必需时阻止启动 |
|
|
339
|
+
| `continue` | 记录警告并继续启动 |
|
|
340
|
+
|
|
341
|
+
成功从 Nacos 读取配置后,插件会原子更新缓存文件。缓存目录应加入部署持久化目录,不建议提交到 Git。
|
|
339
342
|
|
|
340
343
|
## 9. 健康检查
|
|
341
344
|
|
|
342
|
-
|
|
345
|
+
`getServerStatus()` 和 `isHealthy()` 都是异步方法:
|
|
346
|
+
|
|
347
|
+
```ts
|
|
348
|
+
const status = await this.naming.getServerStatus(); // UP | DOWN
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
可直接提供 Nest HTTP 健康接口:
|
|
343
352
|
|
|
344
|
-
```
|
|
353
|
+
```ts
|
|
345
354
|
import { Controller, Get } from '@nestjs/common';
|
|
346
|
-
import {
|
|
347
|
-
import { NacosHealthIndicator } from 'nest-nacos';
|
|
355
|
+
import { NacosHealthIndicator } from '@danqiusheng/nest-nacos';
|
|
348
356
|
|
|
349
|
-
@Controller('health')
|
|
350
|
-
export class
|
|
351
|
-
constructor(
|
|
352
|
-
private health: HealthCheckService,
|
|
353
|
-
private nacos: NacosHealthIndicator,
|
|
354
|
-
) {}
|
|
357
|
+
@Controller('health/nacos')
|
|
358
|
+
export class NacosHealthController {
|
|
359
|
+
constructor(private readonly health: NacosHealthIndicator) {}
|
|
355
360
|
|
|
356
361
|
@Get()
|
|
357
362
|
check() {
|
|
358
|
-
return this.health.
|
|
359
|
-
() => this.nacos.isHealthy(),
|
|
360
|
-
]);
|
|
363
|
+
return this.health.isHealthy();
|
|
361
364
|
}
|
|
362
365
|
}
|
|
363
366
|
```
|
|
364
367
|
|
|
365
|
-
|
|
368
|
+
返回:
|
|
366
369
|
|
|
367
|
-
|
|
370
|
+
```json
|
|
371
|
+
{ "status": "up" }
|
|
372
|
+
```
|
|
368
373
|
|
|
369
|
-
## 10.
|
|
374
|
+
## 10. 高级连接配置
|
|
370
375
|
|
|
371
|
-
|
|
372
|
-
|------|------|--------|------|
|
|
373
|
-
| `register(inst)` | `NacosInstanceOptions` | `Promise<void>` | 注册服务实例 |
|
|
374
|
-
| `deregister(inst)` | `NacosInstanceOptions` | `Promise<void>` | 注销服务实例 |
|
|
375
|
-
| `getAllInstances(serviceName, groupName?, clusters?, subscribe?)` | `string, ...` | `Promise<Host[]>` | 查询所有实例 |
|
|
376
|
-
| `selectInstances(serviceName, groupName?, clusters?, healthy?, subscribe?)` | `string, ...` | `Promise<Host[]>` | 查询健康实例 |
|
|
377
|
-
| `subscribe(info, listener)` | `string \| SubscribeInfo, Function` | `void` | 订阅实例变更推送 |
|
|
378
|
-
| `unSubscribe(info, listener?)` | `string \| SubscribeInfo, Function?` | `void` | 取消订阅 |
|
|
379
|
-
| `getServerStatus()` | 无 | `'UP' \| 'DOWN'` | 获取服务端状态 |
|
|
380
|
-
| `getRawClient()` | 无 | `NacosNamingClient \| null` | 获取原始 SDK 客户端 |
|
|
376
|
+
绝大多数项目只需要顶层共享配置。只有 Naming 和 Config 使用不同地址、命名空间或云鉴权参数时,才使用嵌套配置:
|
|
381
377
|
|
|
382
|
-
|
|
378
|
+
```ts
|
|
379
|
+
NacosModule.forRoot({
|
|
380
|
+
serverAddr: 'shared-nacos:8848',
|
|
381
|
+
username: 'shared-user',
|
|
382
|
+
password: 'shared-password',
|
|
383
|
+
|
|
384
|
+
naming: {
|
|
385
|
+
serverList: 'naming-nacos:8848',
|
|
386
|
+
namespace: 'service-space',
|
|
387
|
+
appName: 'order-service',
|
|
388
|
+
},
|
|
389
|
+
config: {
|
|
390
|
+
serverAddr: 'config-nacos:8848',
|
|
391
|
+
namespace: 'config-space',
|
|
392
|
+
accessKey: process.env.NACOS_ACCESS_KEY,
|
|
393
|
+
secretKey: process.env.NACOS_SECRET_KEY,
|
|
394
|
+
},
|
|
395
|
+
});
|
|
396
|
+
```
|
|
383
397
|
|
|
384
|
-
`
|
|
398
|
+
嵌套配置优先于顶层共享配置。只配置 `naming` 或只配置 `config`,可以只启用对应能力。
|
|
385
399
|
|
|
386
|
-
|
|
387
|
-
// 形式一:字符串(serviceName)
|
|
388
|
-
naming.subscribe('user-service', (hosts) => { ... });
|
|
400
|
+
## 11. 原始 SDK 客户端
|
|
389
401
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
402
|
+
只有插件服务未覆盖 SDK 能力时才建议使用:
|
|
403
|
+
|
|
404
|
+
```ts
|
|
405
|
+
import {
|
|
406
|
+
InjectNacosConfigClient,
|
|
407
|
+
InjectNacosNamingClient,
|
|
408
|
+
} from '@danqiusheng/nest-nacos';
|
|
409
|
+
import type {
|
|
410
|
+
NacosConfigClient,
|
|
411
|
+
NacosNamingClient,
|
|
412
|
+
} from 'nacos';
|
|
413
|
+
|
|
414
|
+
constructor(
|
|
415
|
+
@InjectNacosNamingClient()
|
|
416
|
+
private readonly namingClient: NacosNamingClient,
|
|
417
|
+
@InjectNacosConfigClient()
|
|
418
|
+
private readonly configClient: NacosConfigClient,
|
|
419
|
+
) {}
|
|
395
420
|
```
|
|
396
421
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
##
|
|
400
|
-
|
|
401
|
-
|
|
|
402
|
-
|
|
403
|
-
|
|
|
404
|
-
|
|
|
405
|
-
|
|
|
406
|
-
|
|
|
407
|
-
|
|
|
408
|
-
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
---
|
|
414
|
-
|
|
415
|
-
## 12. 配置项参考
|
|
416
|
-
|
|
417
|
-
### NacosModuleOptions
|
|
418
|
-
|
|
419
|
-
| 字段 | 类型 | 必填 | 说明 |
|
|
420
|
-
|------|------|------|------|
|
|
421
|
-
| `naming` | `NacosNamingOptions` | 否 | Naming 服务发现配置,不传则不启用 |
|
|
422
|
-
| `config` | `NacosConfigOptions` | 否 | Config 配置中心配置,不传则不启用 |
|
|
423
|
-
| `instances` | `NacosInstanceOptions[]` | 否 | 启动时自动注册的服务实例列表 |
|
|
424
|
-
| `subscribeConfigs` | `{ dataId, group }[]` | 否 | 启动时预订阅的配置项列表 |
|
|
425
|
-
|
|
426
|
-
### NacosNamingOptions
|
|
427
|
-
|
|
428
|
-
| 字段 | 类型 | 默认值 | 说明 |
|
|
429
|
-
|------|------|--------|------|
|
|
430
|
-
| `serverList` | `string \| string[]` | - | Nacos 服务端地址 |
|
|
431
|
-
| `namespace` | `string` | `'public'` | 命名空间 ID |
|
|
432
|
-
| `transport` | `'grpc' \| 'http'` | `'grpc'` | 传输协议 |
|
|
433
|
-
| `username` | `string` | - | 用户名 |
|
|
434
|
-
| `password` | `string` | - | 密码 |
|
|
435
|
-
| `ssl` | `boolean` | `false` | 是否启用 TLS/SSL |
|
|
436
|
-
| `ak` | `string` | - | 阿里云 RAM AccessKey |
|
|
437
|
-
| `sk` | `string` | - | 阿里云 RAM SecretKey |
|
|
438
|
-
| `appName` | `string` | - | 应用名 |
|
|
439
|
-
|
|
440
|
-
### NacosConfigOptions
|
|
441
|
-
|
|
442
|
-
| 字段 | 类型 | 默认值 | 说明 |
|
|
443
|
-
|------|------|--------|------|
|
|
444
|
-
| `serverAddr` | `string \| string[]` | - | Nacos 服务端地址 |
|
|
445
|
-
| `namespace` | `string` | `'public'` | 命名空间 ID |
|
|
446
|
-
| `transport` | `'grpc' \| 'http'` | `'grpc'` | 传输协议 |
|
|
447
|
-
| `username` | `string` | - | 用户名 |
|
|
448
|
-
| `password` | `string` | - | 密码 |
|
|
449
|
-
| `ssl` | `boolean` | `false` | 是否启用 TLS/SSL |
|
|
450
|
-
| `accessKey` | `string` | - | 阿里云 RAM AccessKey |
|
|
451
|
-
| `secretKey` | `string` | - | 阿里云 RAM SecretKey |
|
|
452
|
-
| `signatureRegionId` | `string` | - | v4 签名区域 ID |
|
|
453
|
-
|
|
454
|
-
### NacosInstanceOptions
|
|
455
|
-
|
|
456
|
-
| 字段 | 类型 | 默认值 | 说明 |
|
|
457
|
-
|------|------|--------|------|
|
|
458
|
-
| `serviceName` | `string` | - | 服务名 |
|
|
459
|
-
| `ip` | `string` | - | 实例 IP |
|
|
460
|
-
| `port` | `number` | - | 实例端口 |
|
|
461
|
-
| `weight` | `number` | `1.0` | 权重 |
|
|
462
|
-
| `ephemeral` | `boolean` | `true` | 是否临时实例 |
|
|
463
|
-
| `clusterName` | `string` | - | 集群名 |
|
|
464
|
-
| `groupName` | `string` | `'DEFAULT_GROUP'` | 分组名 |
|
|
465
|
-
| `metadata` | `Record<string, string>` | - | 元数据 |
|
|
466
|
-
|
|
467
|
-
---
|
|
422
|
+
原始客户端由模块统一关闭,不要在业务代码中重复调用 `close()`。
|
|
423
|
+
|
|
424
|
+
## 12. 兼容性
|
|
425
|
+
|
|
426
|
+
| 组件 | 支持范围 |
|
|
427
|
+
|---|---|
|
|
428
|
+
| NestJS | 10.x、11.x |
|
|
429
|
+
| Node.js | >= 16 |
|
|
430
|
+
| Nacos Node SDK | `nacos` 2.6.x |
|
|
431
|
+
| Nacos Server | 2.x(已对 2.5.3 做真实端到端验证) |
|
|
432
|
+
| Nacos Server 3.x | 不支持 |
|
|
433
|
+
| 传输协议 | Nacos HTTP OpenAPI;不支持 gRPC |
|
|
434
|
+
|
|
435
|
+
`nacos@2.6.x` 没有实现 Nacos 3.x 所需的 gRPC 协议。配置 `transport: 'grpc'` 时插件会立即抛出错误,不会静默回退造成假成功。
|
|
436
|
+
|
|
437
|
+
插件本身不需要 Java。只有在本机运行 Nacos Server 时才需要 Java;NestJS 应用连接远程 Nacos 时只需要 Node.js。
|
|
468
438
|
|
|
469
439
|
## 13. 常见问题
|
|
470
440
|
|
|
471
|
-
###
|
|
441
|
+
### `getServerStatus()` 类型是 Promise
|
|
472
442
|
|
|
473
|
-
|
|
443
|
+
必须使用 `await`:
|
|
474
444
|
|
|
475
|
-
```
|
|
476
|
-
|
|
477
|
-
config: { serverAddr: '127.0.0.1:8848' },
|
|
478
|
-
// 不传 naming,NacosNamingService 注入后客户端为 null
|
|
479
|
-
})
|
|
445
|
+
```ts
|
|
446
|
+
const status = await this.naming.getServerStatus();
|
|
480
447
|
```
|
|
481
448
|
|
|
482
|
-
|
|
449
|
+
### 鉴权后 Naming 正常但 Config 失败
|
|
483
450
|
|
|
484
|
-
|
|
451
|
+
使用顶层 `username/password` 可以同时传给两个客户端,避免重复配置遗漏:
|
|
485
452
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
| `http` | 支持 | 不支持 | 仅兼容旧版 Nacos 2.x 时使用 |
|
|
453
|
+
```ts
|
|
454
|
+
NacosModule.forRoot({ serverAddr, username, password });
|
|
455
|
+
```
|
|
490
456
|
|
|
491
|
-
###
|
|
457
|
+
### public namespace 应该怎么写
|
|
492
458
|
|
|
493
|
-
|
|
459
|
+
省略 `namespace` 或设置为 `public` 都可以。插件会按 Naming 和 Config SDK 的不同要求正确归一化。
|
|
494
460
|
|
|
495
|
-
|
|
496
|
-
- Nacos Server 的 gRPC 端口(默认 9848,即 serverPort + 1000)已开放
|
|
497
|
-
- 检查用户名密码是否正确
|
|
461
|
+
### 应用无法退出
|
|
498
462
|
|
|
499
|
-
|
|
463
|
+
请通过 Nest 的 `app.close()` 或正常进程信号关闭。模块销毁时会注销实例、取消订阅并关闭两个 SDK 客户端。
|
|
500
464
|
|
|
501
|
-
|
|
465
|
+
### Nacos 3.x 为什么不能连接
|
|
502
466
|
|
|
503
|
-
|
|
467
|
+
当前 Node SDK 没有 Nacos 3.x gRPC 实现。需要 Nacos 3.x 时不能通过修改 `transport` 参数解决,应等待或替换真正支持该协议的 SDK。
|
|
504
468
|
|
|
505
|
-
|
|
469
|
+
## 14. 验证
|
|
506
470
|
|
|
507
|
-
|
|
471
|
+
```bash
|
|
472
|
+
npm ci
|
|
473
|
+
npm run build
|
|
474
|
+
npm test
|
|
475
|
+
```
|
|
508
476
|
|
|
509
|
-
|
|
477
|
+
发布前还应针对真实 Nacos 2.x 执行端到端测试,并从服务端接口反查注册、配置发布和删除结果。
|