nebula-engine 1.0.0-beta3

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/README.md ADDED
@@ -0,0 +1,1255 @@
1
+ # Nebula Engine
2
+
3
+ 基于 Hono 的轻量级微服务引擎框架,支持插件化扩展和自动类型推断。
4
+
5
+ ## 目录
6
+
7
+ - [特性](#特性)
8
+ - [快速开始](#快速开始)
9
+ - [框架概述](#框架概述)
10
+ - [核心原理](#核心原理)
11
+ - [插件系统](#插件系统)
12
+ - [内置插件](#内置插件)
13
+ - [Action 插件](#action-插件)
14
+ - [Route 插件](#route-插件)
15
+ - [Cache 插件](#cache-插件)
16
+ - [ClientCode 插件](#clientcode-插件)
17
+ - [Schedule 插件](#schedule-插件)
18
+ - [GracefulShutdown 插件](#gracefulshutdown-插件)
19
+ - [最佳实践](#最佳实践)
20
+ - [测试指南](#测试指南)
21
+ - [从 1.x 迁移到 2.x](#从-1x-迁移到-2x)
22
+ - [开发](#开发)
23
+
24
+ ## 特性
25
+
26
+ - 🎯 **基于 Hono 的高性能 HTTP Server**:利用 Hono 的轻量级和快速特性
27
+ - 🔌 **插件化架构**:所有功能都通过插件实现,支持自定义扩展
28
+ - 🎨 **装饰器驱动的 API 定义**:使用装饰器简化代码,提高可读性
29
+ - 📝 **自动类型推断**:基于 TypeScript 和 Zod 实现完整的类型安全
30
+ - 🔄 **支持多装饰器叠加**:可以同时使用路由、缓存、限流等多个装饰器
31
+ - 🚀 **显式插件注册**:所有插件必须显式注册,避免隐式依赖
32
+ - 🛡️ **运行时类型验证**:使用 Zod 进行参数和返回值验证
33
+ - 🌊 **流式传输支持**:支持 AsyncIterator 流式数据传输
34
+ - 📦 **自动客户端生成**:自动生成类型化的客户端代码,支持服务间互调
35
+ - ⏰ **定时任务支持**:基于 etcd 的分布式定时任务调度
36
+ - 🛑 **优雅停机**:自动追踪处理器执行,支持优雅停机
37
+
38
+ ## 快速开始
39
+
40
+ ### 安装
41
+
42
+ ```bash
43
+ npm install nebula-engine
44
+ ```
45
+
46
+ ### 基本示例
47
+
48
+ ```typescript
49
+ import {
50
+ Factory,
51
+ ActionPlugin,
52
+ Action,
53
+ RoutePlugin,
54
+ Route,
55
+ z,
56
+ } from "nebula-engine";
57
+
58
+ // 1. 创建引擎工厂(必须显式注册所有需要的插件)
59
+ const { Module, Microservice } = Factory.create(
60
+ new ActionPlugin(),
61
+ new RoutePlugin()
62
+ );
63
+
64
+ // 2. 定义数据模型
65
+ const UserSchema = z.object({
66
+ id: z.string(),
67
+ name: z.string(),
68
+ age: z.number(),
69
+ });
70
+ type User = z.infer<typeof UserSchema>;
71
+
72
+ // 3. 定义服务模块
73
+ @Module("users")
74
+ class UserService {
75
+ private users = new Map<string, User>();
76
+
77
+ @Action({
78
+ description: "获取用户信息",
79
+ params: [z.string()],
80
+ returns: UserSchema,
81
+ })
82
+ async getUser(id: string): Promise<User> {
83
+ const user = this.users.get(id);
84
+ if (!user) {
85
+ throw new Error("用户不存在");
86
+ }
87
+ return user;
88
+ }
89
+
90
+ @Route({
91
+ method: "GET",
92
+ path: "/health",
93
+ })
94
+ async health() {
95
+ return { status: "ok" };
96
+ }
97
+ }
98
+
99
+ // 4. 创建并启动引擎
100
+ const engine = new Microservice({
101
+ name: "user-service",
102
+ version: "1.0.0",
103
+ prefix: "/api",
104
+ });
105
+
106
+ await engine.start();
107
+ console.log(`服务启动在端口 ${engine.getPort()}`);
108
+ ```
109
+
110
+ ## 框架概述
111
+
112
+ Nebula Engine 是一个基于插件的微服务框架,核心设计理念是:
113
+
114
+ 1. **插件化架构**:所有功能都通过插件实现,包括路由、缓存、定时任务等
115
+ 2. **显式注册**:所有插件必须显式注册,不会自动包含任何默认插件
116
+ 3. **类型安全**:基于 TypeScript 和 Zod 实现完整的类型推断和运行时验证
117
+ 4. **装饰器驱动**:使用装饰器简化 API 定义,提高代码可读性
118
+
119
+ ### 核心组件
120
+
121
+ - **Factory**:创建类型化的引擎工厂,返回 `Module` 装饰器和 `Microservice` 类
122
+ - **Microservice**:引擎核心类,管理插件生命周期和模块实例
123
+ - **Plugin**:插件接口,定义插件的生命周期钩子和优先级
124
+ - **Handler**:处理器元数据,支持通过 `wrap` API 包装方法
125
+
126
+ ## 核心原理
127
+
128
+ ### 工厂模式
129
+
130
+ 框架使用工厂模式创建类型化的引擎实例:
131
+
132
+ ```typescript
133
+ const { Module, Microservice } = Factory.create(
134
+ new ActionPlugin(),
135
+ new RoutePlugin()
136
+ );
137
+ ```
138
+
139
+ `Factory.create` 会:
140
+ 1. 合并所有插件的 Module 配置类型
141
+ 2. 创建类型化的 `Module` 装饰器
142
+ 3. 创建类型化的 `Microservice` 类
143
+
144
+ ### 插件系统
145
+
146
+ 插件系统采用**优先级驱动的洋葱圈模型**:
147
+
148
+ 1. **优先级排序**:插件按优先级自动排序(数值越小,优先级越高)
149
+ 2. **包装链构建**:包装插件通过 `handler.wrap()` 构建包装链
150
+ 3. **路由注册**:路由插件最后执行,注册 HTTP 路由
151
+
152
+ 详细说明请参考 [插件系统文档](./docs/plugin-system.md)。
153
+
154
+ ### 模块发现机制
155
+
156
+ 框架使用 Symbol 作为模块元数据的键,实现模块发现:
157
+
158
+ 1. 每个 `Factory.create` 调用生成唯一的 Symbol
159
+ 2. `Module` 装饰器使用该 Symbol 存储模块元数据
160
+ 3. 引擎启动时通过 Symbol 查找所有模块
161
+
162
+ 这种机制确保了不同工厂实例之间的隔离。
163
+
164
+ ## 插件系统
165
+
166
+ 框架的核心是插件系统,所有功能都通过插件实现。插件系统采用优先级驱动的洋葱圈模型,让用户无需关心插件注册顺序。
167
+
168
+ **详细文档请参考:[插件系统完整指南](./docs/plugin-system.md)**
169
+
170
+ ### 插件优先级
171
+
172
+ ```typescript
173
+ export enum PluginPriority {
174
+ SYSTEM = 50, // 系统级插件(优雅停机等)
175
+ SECURITY = 100, // 安全相关插件(限流、认证等)
176
+ LOGGING = 200, // 日志、监控插件
177
+ BUSINESS = 300, // 业务逻辑插件(默认)
178
+ PERFORMANCE = 400, // 性能优化插件(缓存等)
179
+ ROUTE = 1000, // 路由插件(必须最后执行)
180
+ }
181
+ ```
182
+
183
+ ### 插件生命周期
184
+
185
+ 插件可以定义以下生命周期钩子:
186
+
187
+ - `onInit`:引擎初始化时调用
188
+ - `onModuleLoad`:模块加载后调用
189
+ - `onHandlerLoad`:处理器加载时调用(可以调用 `handler.wrap()`)
190
+ - `onBeforeStart`:引擎启动前调用
191
+ - `onAfterStart`:引擎启动后调用
192
+ - `onDestroy`:引擎销毁时调用
193
+
194
+ ## 内置插件
195
+
196
+ ### Action 插件
197
+
198
+ Action 插件用于定义 RPC 风格的 API 端点,支持参数和返回值验证。
199
+
200
+ #### 安装和注册
201
+
202
+ ```typescript
203
+ import { ActionPlugin, Action } from "nebula-engine";
204
+
205
+ const { Module, Microservice } = Factory.create(
206
+ new ActionPlugin()
207
+ );
208
+ ```
209
+
210
+ #### 基本用法
211
+
212
+ ```typescript
213
+ import { z } from "nebula-engine";
214
+
215
+ @Module("users")
216
+ class UserService {
217
+ @Action({
218
+ description: "创建用户",
219
+ params: [z.string(), z.number()],
220
+ returns: UserSchema,
221
+ })
222
+ async createUser(name: string, age: number): Promise<User> {
223
+ return { id: "1", name, age };
224
+ }
225
+ }
226
+ ```
227
+
228
+ #### 特性
229
+
230
+ - **参数验证**:使用 Zod schema 验证请求参数
231
+ - **返回值验证**:使用 Zod schema 验证返回值
232
+ - **EJSON 支持**:自动处理 EJSON 序列化/反序列化,支持更多数据类型
233
+ - **流式传输**:支持 `async generator` 函数,实现流式数据传输
234
+ - **幂等性标记**:支持 `idempotence: true` 标记,用于客户端重试
235
+ - **Context 注入**:如果方法签名包含 `Context` 参数,自动注入 Hono Context
236
+
237
+ #### 流式传输示例
238
+
239
+ ```typescript
240
+ @Action({
241
+ description: "流式返回数据",
242
+ params: [z.number()],
243
+ returns: z.number(),
244
+ stream: true,
245
+ })
246
+ async *streamNumbers(count: number) {
247
+ for (let i = 0; i < count; i++) {
248
+ yield i;
249
+ await new Promise((resolve) => setTimeout(resolve, 100));
250
+ }
251
+ }
252
+ ```
253
+
254
+ #### 模块配置
255
+
256
+ ```typescript
257
+ @Module("users", {
258
+ actionMiddlewares: [
259
+ async (ctx, next) => {
260
+ // 模块级中间件
261
+ await next();
262
+ },
263
+ ],
264
+ })
265
+ class UserService {
266
+ @Action({
267
+ description: "获取用户",
268
+ params: [z.string()],
269
+ returns: UserSchema,
270
+ middlewares: [
271
+ async (ctx, next) => {
272
+ // 动作级中间件
273
+ await next();
274
+ },
275
+ ],
276
+ })
277
+ async getUser(id: string) {}
278
+ }
279
+ ```
280
+
281
+ #### ActionOptions 配置
282
+
283
+ ```typescript
284
+ interface ActionOptions {
285
+ description?: string; // 动作描述
286
+ params?: z.ZodTypeAny[]; // 参数验证 schema
287
+ returns?: z.ZodTypeAny; // 返回值验证 schema
288
+ stream?: boolean; // 是否流式传输
289
+ idempotence?: boolean; // 是否幂等操作
290
+ middlewares?: MiddlewareHandler[]; // 动作级中间件
291
+ }
292
+ ```
293
+
294
+ ### Route 插件
295
+
296
+ Route 插件用于定义 HTTP 路由,支持 RESTful API 和页面路由。
297
+
298
+ #### 安装和注册
299
+
300
+ ```typescript
301
+ import { RoutePlugin, Route, Page } from "nebula-engine";
302
+
303
+ const { Module, Microservice } = Factory.create(
304
+ new RoutePlugin()
305
+ );
306
+ ```
307
+
308
+ #### 基本用法
309
+
310
+ ```typescript
311
+ import { Context } from "hono";
312
+
313
+ @Module("api")
314
+ class ApiService {
315
+ @Route({
316
+ method: "GET",
317
+ path: "/users/:id",
318
+ })
319
+ async getUser(ctx: Context) {
320
+ const id = ctx.req.param("id");
321
+ return ctx.json({ id, name: "John" });
322
+ }
323
+
324
+ @Page({
325
+ path: "/dashboard",
326
+ })
327
+ async dashboard(ctx: Context) {
328
+ return <div>Dashboard</div>;
329
+ }
330
+ }
331
+ ```
332
+
333
+ #### 特性
334
+
335
+ - **多路径支持**:支持为同一个处理器注册多个路径
336
+ - **多方法支持**:支持为同一个路径注册多个 HTTP 方法
337
+ - **中间件支持**:支持全局、模块级、路由级三层中间件
338
+ - **自动响应处理**:自动处理 `Response` 对象、JSX 元素和普通对象
339
+ - **路由描述**:支持 `description` 字段,用于文档生成
340
+
341
+ #### 中间件示例
342
+
343
+ ```typescript
344
+ // 全局中间件(在 RoutePlugin 构造函数中配置)
345
+ const routePlugin = new RoutePlugin({
346
+ globalMiddlewares: [
347
+ async (ctx, next) => {
348
+ // 全局鉴权中间件
349
+ const token = ctx.req.header("Authorization");
350
+ if (!token) {
351
+ return ctx.json({ error: "Unauthorized" }, 401);
352
+ }
353
+ await next();
354
+ },
355
+ ],
356
+ });
357
+
358
+ // 模块级中间件
359
+ @Module("api", {
360
+ routeMiddlewares: [
361
+ async (ctx, next) => {
362
+ // 模块级中间件
363
+ await next();
364
+ },
365
+ ],
366
+ })
367
+ class ApiService {
368
+ @Route({
369
+ method: "GET",
370
+ path: "/users",
371
+ middlewares: [
372
+ async (ctx, next) => {
373
+ // 路由级中间件
374
+ await next();
375
+ },
376
+ ],
377
+ })
378
+ async getUsers() {}
379
+ }
380
+ ```
381
+
382
+ #### 多路径示例
383
+
384
+ ```typescript
385
+ @Route({
386
+ path: ["/", "/home", "/dashboard"],
387
+ description: "首页",
388
+ })
389
+ async homePage(ctx: Context) {
390
+ return <HomePage />;
391
+ }
392
+ ```
393
+
394
+ #### RouteOptions 配置
395
+
396
+ ```typescript
397
+ interface RouteOptions {
398
+ method?: HTTPMethod | HTTPMethod[]; // HTTP 方法(默认 GET)
399
+ path: string | string[]; // 路由路径(支持多个)
400
+ middlewares?: MiddlewareHandler[]; // 路由级中间件
401
+ description?: string; // 路由描述
402
+ }
403
+ ```
404
+
405
+ ### Cache 插件
406
+
407
+ Cache 插件提供方法级别的缓存功能,支持内存和 Redis 两种存储后端。
408
+
409
+ #### 安装和注册
410
+
411
+ ```typescript
412
+ import {
413
+ CachePlugin,
414
+ Cache,
415
+ MemoryCacheAdapter,
416
+ RedisCacheAdapter,
417
+ } from "nebula-engine";
418
+
419
+ // 使用内存缓存(默认)
420
+ const cachePlugin = new CachePlugin();
421
+
422
+ // 或使用 Redis 缓存
423
+ const cachePlugin = new CachePlugin(
424
+ new RedisCacheAdapter({ client: redisClient })
425
+ );
426
+ ```
427
+
428
+ #### 基本用法
429
+
430
+ ```typescript
431
+ @Module("users")
432
+ class UserService {
433
+ @Cache({ ttl: 5000 }) // 缓存 5 秒
434
+ async getUser(id: string): Promise<User> {
435
+ // 这个方法的结果会被缓存
436
+ return fetchUserFromDatabase(id);
437
+ }
438
+ }
439
+ ```
440
+
441
+ #### 自定义缓存键
442
+
443
+ ```typescript
444
+ @Cache({
445
+ ttl: 5000,
446
+ key: (id: string, name: string) => ({ id, name }), // 自定义键生成
447
+ })
448
+ async getUser(id: string, name: string): Promise<User> {
449
+ return fetchUser(id, name);
450
+ }
451
+ ```
452
+
453
+ #### 特性
454
+
455
+ - **多种存储后端**:支持内存和 Redis
456
+ - **TTL 支持**:支持设置缓存过期时间
457
+ - **自定义键生成**:支持自定义缓存键生成函数
458
+ - **自动清理**:内存缓存支持自动清理过期项
459
+ - **模块配置**:支持模块级别的默认 TTL 和启用/禁用
460
+
461
+ #### 模块配置
462
+
463
+ ```typescript
464
+ @Module("users", {
465
+ cacheDefaultTtl: 10000, // 模块默认 TTL(10秒)
466
+ cacheEnabled: true, // 模块默认启用缓存
467
+ cacheCleanupInterval: 60000, // 清理间隔(60秒)
468
+ })
469
+ class UserService {
470
+ @Cache({ ttl: 5000 }) // 会覆盖模块默认值
471
+ async getUser() {}
472
+ }
473
+ ```
474
+
475
+ #### CacheOptions 配置
476
+
477
+ ```typescript
478
+ interface CacheOptions {
479
+ ttl?: number; // 缓存过期时间(毫秒,默认60000)
480
+ key?: (...args: any[]) => any; // 自定义键生成函数
481
+ enabled?: boolean; // 是否启用缓存(默认true)
482
+ }
483
+ ```
484
+
485
+ #### 缓存键生成
486
+
487
+ 缓存键的生成规则:
488
+ 1. 如果提供了 `key` 函数,使用其返回值
489
+ 2. 否则使用方法参数(args)
490
+ 3. 将结果进行 ejson 序列化
491
+ 4. 使用 SHA256 哈希生成最终键
492
+ 5. 最终格式:`{moduleName}:{methodName}:{hash}`
493
+
494
+ 示例:
495
+ ```typescript
496
+ @Cache({
497
+ key: (id: string, date: Date) => ({ id, date }), // 返回对象
498
+ ttl: 5000,
499
+ })
500
+ async getUserData(id: string, date: Date) {
501
+ // 缓存键:users:getUserData:{hash}
502
+ }
503
+ ```
504
+
505
+ ### ClientCode 插件
506
+
507
+ ClientCode 插件自动生成类型化的客户端代码,支持服务间互调。
508
+
509
+ #### 安装和注册
510
+
511
+ ```typescript
512
+ import { ClientCodePlugin } from "nebula-engine";
513
+
514
+ const { Module, Microservice } = Factory.create(
515
+ new ActionPlugin(),
516
+ new ClientCodePlugin({
517
+ clientSavePath: "./generated/client.ts", // 可选:保存到本地文件
518
+ })
519
+ );
520
+ ```
521
+
522
+ #### 使用生成的客户端
523
+
524
+ 1. **下载客户端代码**:访问 `http://localhost:3000/api/client.ts` 下载生成的代码
525
+ 2. **使用客户端**:
526
+
527
+ ```typescript
528
+ import { GeneratedClient } from "./generated/client";
529
+
530
+ const client = new GeneratedClient({
531
+ baseUrl: "http://localhost:3000",
532
+ prefix: "/api",
533
+ });
534
+
535
+ // 调用服务方法
536
+ const user = await client.users.getUser("123");
537
+ ```
538
+
539
+ #### 特性
540
+
541
+ - **自动生成**:基于 Action 装饰器自动生成客户端代码
542
+ - **类型安全**:生成的客户端代码包含完整的类型定义
543
+ - **参数名提取**:自动提取方法参数名,生成更友好的 API
544
+ - **流式传输支持**:生成的客户端支持流式传输(AsyncIterator)
545
+ - **幂等性支持**:生成的客户端支持幂等性标记,用于自动重试
546
+ - **本地保存**:支持将生成的代码保存到本地文件,方便开发调试
547
+ - **EJSON 支持**:生成的客户端自动支持 EJSON 序列化/反序列化
548
+
549
+ #### 客户端代码示例
550
+
551
+ 生成的客户端代码示例:
552
+
553
+ ```typescript
554
+ export class MicroserviceClient extends BaseMicroserviceClient {
555
+ public readonly users = this.registerModule<UsersModule>("users", {
556
+ getUser: { stream: false, idempotent: false },
557
+ createUser: { stream: false, idempotent: false },
558
+ });
559
+ }
560
+
561
+ // 使用
562
+ const client = new MicroserviceClient({
563
+ baseUrl: "http://localhost:3000",
564
+ prefix: "/api",
565
+ });
566
+
567
+ const user = await client.users.getUser("123");
568
+ ```
569
+
570
+ ### Schedule 插件
571
+
572
+ Schedule 插件提供分布式定时任务功能,基于 etcd 实现主节点选举。
573
+
574
+ #### 安装和注册
575
+
576
+ ```typescript
577
+ import { SchedulePlugin, Schedule } from "nebula-engine";
578
+ import { Etcd3 } from "etcd3";
579
+
580
+ // 使用真实的 etcd
581
+ const etcdClient = new Etcd3({
582
+ hosts: ["localhost:2379"],
583
+ });
584
+
585
+ const { Module, Microservice } = Factory.create(
586
+ new SchedulePlugin({ etcdClient })
587
+ );
588
+
589
+ // 或使用 Mock Etcd(用于测试和本地开发)
590
+ const { Module, Microservice } = Factory.create(
591
+ new SchedulePlugin({ useMockEtcd: true })
592
+ );
593
+ ```
594
+
595
+ #### 基本用法
596
+
597
+ ```typescript
598
+ @Module("tasks")
599
+ class TaskService {
600
+ @Schedule({
601
+ interval: 60000, // 60秒执行一次
602
+ mode: ScheduleMode.FIXED_RATE, // 固定频率
603
+ })
604
+ async cleanupTask() {
605
+ // 清理任务
606
+ }
607
+
608
+ @Schedule({
609
+ interval: 5000,
610
+ mode: ScheduleMode.FIXED_DELAY, // 固定延迟(上次执行完成后延迟)
611
+ })
612
+ async reportTask() {
613
+ // 报告任务
614
+ }
615
+ }
616
+ ```
617
+
618
+ #### 特性
619
+
620
+ - **分布式调度**:基于 etcd 实现主节点选举,确保多实例中只有一个执行任务
621
+ - **两种模式**:
622
+ - `FIXED_RATE`:固定频率,按固定间隔执行
623
+ - `FIXED_DELAY`:固定延迟,上次执行完成后延迟指定时间再执行
624
+ - **Mock Etcd 支持**:支持使用 Mock Etcd,无需真实 etcd 服务即可开发和测试
625
+ - **OpenTelemetry 追踪**:支持 OpenTelemetry 追踪
626
+
627
+ 详细文档请参考:[Schedule 插件文档](./src/plugins/schedule/README.md)
628
+
629
+ ### GracefulShutdown 插件
630
+
631
+ GracefulShutdown 插件提供优雅停机功能,自动追踪处理器执行并等待完成。
632
+
633
+ #### 安装和注册
634
+
635
+ ```typescript
636
+ import { GracefulShutdownPlugin } from "nebula-engine";
637
+
638
+ const { Module, Microservice } = Factory.create(
639
+ new ActionPlugin(),
640
+ new RoutePlugin(),
641
+ new GracefulShutdownPlugin({
642
+ shutdownTimeout: 10 * 60 * 1000, // 10分钟超时(默认)
643
+ })
644
+ );
645
+ ```
646
+
647
+ #### 工作原理
648
+
649
+ 1. **处理器追踪**:自动追踪所有处理器的执行状态(Action、Route、Schedule 等)
650
+ 2. **信号监听**:监听 `SIGINT`、`SIGTERM`、`SIGBREAK` 等系统信号
651
+ 3. **优雅停机**:
652
+ - 收到信号后,拒绝新的请求
653
+ - 等待所有正在执行的处理器完成
654
+ - 如果超时,强制停机
655
+ - 停止引擎并退出进程
656
+
657
+ #### 配置选项
658
+
659
+ ```typescript
660
+ new GracefulShutdownPlugin({
661
+ shutdownTimeout: 10 * 60 * 1000, // 停机超时时间(默认10分钟)
662
+ enabled: true, // 是否启用(默认true)
663
+ })
664
+ ```
665
+
666
+ #### 特性
667
+
668
+ - **自动追踪**:自动追踪所有处理器的执行状态
669
+ - **跨平台支持**:兼容 Windows、Linux、macOS
670
+ - **超时保护**:支持设置停机超时时间,防止无限等待
671
+ - **拒绝新请求**:停机期间自动拒绝新的请求
672
+
673
+ ## 最佳实践
674
+
675
+ ### 1. 插件注册顺序
676
+
677
+ 虽然插件系统会自动按优先级排序,但建议按功能分组注册插件:
678
+
679
+ ```typescript
680
+ const { Module, Microservice } = Factory.create(
681
+ // 系统插件
682
+ new GracefulShutdownPlugin(),
683
+
684
+ // 业务插件
685
+ new ActionPlugin(),
686
+ new RoutePlugin(),
687
+
688
+ // 性能插件
689
+ new CachePlugin(),
690
+
691
+ // 工具插件
692
+ new ClientCodePlugin(),
693
+ new SchedulePlugin({ useMockEtcd: true }),
694
+ );
695
+ ```
696
+
697
+ ### 2. 模块组织
698
+
699
+ 建议按功能模块组织代码:
700
+
701
+ ```typescript
702
+ // services/user-service.ts
703
+ @Module("users")
704
+ class UserService {
705
+ @Action({ ... })
706
+ async getUser() {}
707
+ }
708
+
709
+ // services/order-service.ts
710
+ @Module("orders")
711
+ class OrderService {
712
+ @Action({ ... })
713
+ async getOrder() {}
714
+ }
715
+ ```
716
+
717
+ ### 3. 类型定义
718
+
719
+ 使用 Zod schema 定义数据类型,实现类型安全和运行时验证:
720
+
721
+ ```typescript
722
+ import { z } from "nebula-engine";
723
+
724
+ const UserSchema = z.object({
725
+ id: z.string(),
726
+ name: z.string(),
727
+ age: z.number().min(0).max(150),
728
+ });
729
+
730
+ type User = z.infer<typeof UserSchema>;
731
+ ```
732
+
733
+ ### 4. 错误处理
734
+
735
+ 在 Action 方法中抛出错误,框架会自动处理:
736
+
737
+ ```typescript
738
+ @Action({
739
+ params: [z.string()],
740
+ returns: UserSchema,
741
+ })
742
+ async getUser(id: string): Promise<User> {
743
+ const user = await db.findUser(id);
744
+ if (!user) {
745
+ throw new Error("用户不存在"); // 框架会自动返回错误响应
746
+ }
747
+ return user;
748
+ }
749
+ ```
750
+
751
+ ### 5. 中间件使用
752
+
753
+ 合理使用中间件实现横切关注点:
754
+
755
+ ```typescript
756
+ // 认证中间件
757
+ const authMiddleware = async (ctx: Context, next: () => Promise<void>) => {
758
+ const token = ctx.req.header("Authorization");
759
+ if (!token) {
760
+ return ctx.json({ error: "Unauthorized" }, 401);
761
+ }
762
+ // 验证 token 并注入用户信息
763
+ ctx.set("user", { id: "123", name: "John" });
764
+ await next();
765
+ };
766
+
767
+ // 在 RoutePlugin 中配置全局中间件
768
+ const routePlugin = new RoutePlugin({
769
+ globalMiddlewares: [authMiddleware],
770
+ });
771
+ ```
772
+
773
+ ### 6. 缓存策略
774
+
775
+ 合理使用缓存提高性能:
776
+
777
+ ```typescript
778
+ // 频繁查询但变化不频繁的数据
779
+ @Cache({ ttl: 5 * 60 * 1000 }) // 缓存 5 分钟
780
+ async getConfig(): Promise<Config> {
781
+ return db.findConfig();
782
+ }
783
+
784
+ // 使用自定义键避免缓存冲突
785
+ @Cache({
786
+ ttl: 60000,
787
+ key: (userId: string, type: string) => ({ userId, type }),
788
+ })
789
+ async getUserData(userId: string, type: string) {
790
+ return db.findUserData(userId, type);
791
+ }
792
+ ```
793
+
794
+ ### 7. 版本路由
795
+
796
+ 引擎启动后会自动注册版本路由 `{prefix}/version`(如果路径未被占用),用于健康检查:
797
+
798
+ ```typescript
799
+ // 访问 http://localhost:3000/api/version
800
+ // 返回:
801
+ {
802
+ "name": "user-service",
803
+ "version": "1.0.0",
804
+ "status": "running"
805
+ }
806
+ ```
807
+
808
+ ### 8. 预检机制
809
+
810
+ 框架提供了预检机制,用于在服务启动前进行必要的检查和初始化(如数据库连接、Redis 连接等):
811
+
812
+ ```typescript
813
+ import { startCheck, PreStartChecker } from "nebula-engine";
814
+
815
+ // 定义预检项
816
+ const checkers: PreStartChecker[] = [
817
+ {
818
+ name: "数据库连接检查",
819
+ check: async () => {
820
+ const db = await connectDB();
821
+ await db.ping();
822
+ },
823
+ },
824
+ {
825
+ name: "Redis 连接检查",
826
+ check: async () => {
827
+ const redis = await connectRedis();
828
+ await redis.ping();
829
+ },
830
+ },
831
+ {
832
+ name: "可选检查项",
833
+ check: async () => {
834
+ // 某些检查
835
+ },
836
+ skip: true, // 可以跳过某些检查
837
+ },
838
+ ];
839
+
840
+ // 执行预检并启动服务
841
+ await startCheck(checkers, async () => {
842
+ const { Module, Microservice } = Factory.create(
843
+ new ActionPlugin(),
844
+ new RoutePlugin()
845
+ );
846
+
847
+ const engine = new Microservice({
848
+ name: "user-service",
849
+ version: "1.0.0",
850
+ });
851
+
852
+ await engine.start();
853
+ console.log(`服务启动在端口 ${engine.getPort()}`);
854
+ });
855
+ ```
856
+
857
+ 预检机制的优势:
858
+ - **依赖检查**:确保所有必要的外部服务都可用
859
+ - **优雅失败**:如果检查失败,服务不会启动
860
+ - **清晰日志**:提供详细的检查日志,便于问题诊断
861
+ - **可选检查**:支持跳过某些非必需的检查项
862
+
863
+ ## 测试指南
864
+
865
+ 框架提供了两种测试方法,根据测试场景选择合适的测试方式,可以避免不必要的 HTTP 服务器启动,提高测试效率。
866
+
867
+ ### `engine.handler` - 用于不依赖 Hono 的场景
868
+
869
+ **适用场景**:
870
+ - Action 插件测试(不涉及 Hono 中间件)
871
+ - Cache 插件测试
872
+ - 其他不依赖 HTTP 层的插件测试
873
+ - 测试远程 RPC 调用逻辑
874
+
875
+ **优势**:
876
+ - 无需启动 HTTP 服务器,测试更快
877
+ - 更符合 RPC 调用的语义
878
+ - 完整的类型推导支持(自动推导参数和返回值类型)
879
+ - 自动执行包装链(缓存、限流等)
880
+
881
+ **使用示例**:
882
+
883
+ ```typescript
884
+ import { Testing } from "../../core/testing";
885
+ import { ActionPlugin, Action } from "./index";
886
+ import { z } from "zod";
887
+
888
+ describe("ActionPlugin", () => {
889
+ let engine: ReturnType<typeof Testing.createTestEngine>["engine"];
890
+ let Module: ReturnType<typeof Testing.createTestEngine>["Module"];
891
+
892
+ beforeEach(() => {
893
+ const testEngine = Testing.createTestEngine({
894
+ plugins: [new ActionPlugin()],
895
+ });
896
+ engine = testEngine.engine;
897
+ Module = testEngine.Module;
898
+ });
899
+
900
+ it("应该能够调用 Action handler", async () => {
901
+ @Module("users")
902
+ class UserService {
903
+ @Action({ params: [z.string()] })
904
+ getUser(id: string): { id: string; name: string } {
905
+ return { id, name: "Alice" };
906
+ }
907
+ }
908
+
909
+ // 获取 handler 并调用(类型自动推导)
910
+ const getUserHandler = engine.handler(UserService, "getUser");
911
+ const result = await getUserHandler("123");
912
+ // result 的类型自动推导为 { id: string; name: string }
913
+ expect(result).toEqual({ id: "123", name: "Alice" });
914
+
915
+ // 或者链式调用
916
+ const result2 = await engine.handler(UserService, "getUser")("456");
917
+ expect(result2).toEqual({ id: "456", name: "Alice" });
918
+ });
919
+ });
920
+ ```
921
+
922
+ ### `engine.request` - 用于依赖 Hono 的场景
923
+
924
+ **适用场景**:
925
+ - Route 插件测试(需要测试路由、中间件、Context 等)
926
+ - 需要测试完整 HTTP 请求/响应流程的场景
927
+ - 需要测试 Hono 中间件的场景
928
+
929
+ **优势**:
930
+ - 完整执行 Hono 中间件链
931
+ - 支持所有 HTTP 方法和请求选项
932
+ - 返回标准 `Response` 对象
933
+ - 无需启动 HTTP 服务器
934
+
935
+ **使用示例**:
936
+
937
+ ```typescript
938
+ import { Testing } from "../../core/testing";
939
+ import { RoutePlugin, Route } from "./index";
940
+ import { Context } from "hono";
941
+
942
+ describe("RoutePlugin", () => {
943
+ let engine: ReturnType<typeof Testing.createTestEngine>["engine"];
944
+ let Module: ReturnType<typeof Testing.createTestEngine>["Module"];
945
+
946
+ beforeEach(() => {
947
+ const testEngine = Testing.createTestEngine({
948
+ plugins: [new RoutePlugin()],
949
+ });
950
+ engine = testEngine.engine;
951
+ Module = testEngine.Module;
952
+ });
953
+
954
+ it("应该能够调用 Route handler", async () => {
955
+ @Module("users")
956
+ class UserService {
957
+ @Route({ path: "/users/:id" })
958
+ getUser(ctx: Context) {
959
+ const id = ctx.req.param("id");
960
+ return { id, name: "Alice" };
961
+ }
962
+ }
963
+
964
+ // 使用 request 方法(完整执行中间件)
965
+ const response = await engine.request("/users/123");
966
+ expect(response.ok).toBe(true);
967
+ const data = await response.json();
968
+ expect(data).toEqual({ id: "123", name: "Alice" });
969
+
970
+ // 使用 Request 对象
971
+ const request = new Request("http://localhost/users/456", {
972
+ method: "POST",
973
+ headers: { "Content-Type": "application/json" },
974
+ body: JSON.stringify({ name: "Bob" }),
975
+ });
976
+ const response2 = await engine.request(request);
977
+ expect(response2.ok).toBe(true);
978
+ });
979
+ });
980
+ ```
981
+
982
+ ### 选择指南
983
+
984
+ | 场景 | 使用方法 | 原因 |
985
+ |------|---------|------|
986
+ | Action 插件测试 | `engine.handler` | 不依赖 Hono,表示 RPC 调用 |
987
+ | Route 插件测试 | `engine.request` | 需要测试路由和中间件 |
988
+ | Cache 插件测试 | `engine.handler` | 不依赖 Hono,测试包装链 |
989
+ | 中间件测试 | `engine.request` | 需要完整执行中间件链 |
990
+ | 集成测试 | `fetch` + `engine.start()` | 需要真实 HTTP 服务器 |
991
+
992
+ ### 注意事项
993
+
994
+ 1. **集成测试**:应使用 `fetch` + `engine.start()` 启动真实 HTTP 服务器,确保测试场景接近生产环境
995
+ 2. **特定测试**:`engine.test.ts` 中的特定测试可能故意设计为测试特定功能,保持原有方式
996
+ 3. **避免混用**:在同一测试文件中保持一致的测试方法
997
+ 4. **类型推导**:`engine.handler` 支持完整的类型推导,无需显式指定泛型参数
998
+
999
+ ## 从 1.x 迁移到 2.x
1000
+
1001
+ ### 主要变化
1002
+
1003
+ #### 1. 插件系统重构
1004
+
1005
+ **1.x 版本**:
1006
+ - 使用 `new Microservice({ modules: [...], plugins: [...] })` 创建引擎
1007
+ - 某些插件(如 RoutePlugin)作为默认插件自动包含
1008
+
1009
+ **2.x 版本**:
1010
+ - 使用 `Factory.create(...plugins)` 创建引擎工厂
1011
+ - **所有插件都必须显式注册**,不再有默认插件
1012
+
1013
+ ```typescript
1014
+ // 1.x
1015
+ const service = new Microservice({
1016
+ modules: [UserService],
1017
+ plugins: [new CachePlugin()],
1018
+ });
1019
+
1020
+ // 2.x
1021
+ const { Module, Microservice } = Factory.create(
1022
+ new ActionPlugin(),
1023
+ new RoutePlugin(),
1024
+ new CachePlugin()
1025
+ );
1026
+ const engine = new Microservice({
1027
+ name: "user-service",
1028
+ version: "1.0.0",
1029
+ });
1030
+ ```
1031
+
1032
+ #### 2. 模块定义方式变化
1033
+
1034
+ **1.x 版本**:
1035
+ ```typescript
1036
+ @Module("users", {
1037
+ description: "用户服务模块",
1038
+ version: "1.0.0",
1039
+ })
1040
+ class UserService {}
1041
+ ```
1042
+
1043
+ **2.x 版本**:
1044
+ ```typescript
1045
+ // Module 装饰器由 Factory.create 返回
1046
+ const { Module } = Factory.create(...plugins);
1047
+
1048
+ @Module("users", {
1049
+ // 插件配置直接平铺在 options 中
1050
+ cacheDefaultTtl: 5000,
1051
+ routePrefix: "/api/v1",
1052
+ })
1053
+ class UserService {}
1054
+ ```
1055
+
1056
+ #### 3. Action 装饰器变化
1057
+
1058
+ **1.x 版本**:
1059
+ ```typescript
1060
+ @Action({
1061
+ cache: true,
1062
+ cacheTTL: 60,
1063
+ })
1064
+ async getUser() {}
1065
+ ```
1066
+
1067
+ **2.x 版本**:
1068
+ ```typescript
1069
+ // 缓存功能独立为 Cache 插件
1070
+ @Action({ ... })
1071
+ @Cache({ ttl: 60000 })
1072
+ async getUser() {}
1073
+ ```
1074
+
1075
+ #### 4. Page 装饰器变化
1076
+
1077
+ **1.x 版本**:
1078
+ - `Page` 装饰器需要 `PageRenderPlugin`
1079
+
1080
+ **2.x 版本**:
1081
+ - `Page` 装饰器是 `Route` 的别名,由 `RoutePlugin` 提供
1082
+ - 不再需要单独的 `PageRenderPlugin`
1083
+
1084
+ ```typescript
1085
+ // 1.x
1086
+ import { Page, PageRenderPlugin } from "nebula-engine";
1087
+ const service = new Microservice({
1088
+ plugins: [new PageRenderPlugin()],
1089
+ });
1090
+
1091
+ // 2.x
1092
+ import { RoutePlugin, Page } from "nebula-engine";
1093
+ const { Module } = Factory.create(new RoutePlugin());
1094
+ ```
1095
+
1096
+ #### 5. 客户端生成变化
1097
+
1098
+ **1.x 版本**:
1099
+ ```typescript
1100
+ const service = new Microservice({
1101
+ generateClient: new URL("./client.ts", import.meta.url),
1102
+ });
1103
+ ```
1104
+
1105
+ **2.x 版本**:
1106
+ ```typescript
1107
+ const { Module, Microservice } = Factory.create(
1108
+ new ActionPlugin(),
1109
+ new ClientCodePlugin({
1110
+ clientSavePath: "./generated/client.ts", // 可选
1111
+ })
1112
+ );
1113
+ // 客户端代码自动在 {prefix}/client.ts 提供下载
1114
+ ```
1115
+
1116
+ #### 6. 启动方式变化
1117
+
1118
+ **1.x 版本**:
1119
+ ```typescript
1120
+ const service = new Microservice({ ... });
1121
+ await service.init();
1122
+ service.start(3000);
1123
+ ```
1124
+
1125
+ **2.x 版本**:
1126
+ ```typescript
1127
+ const engine = new Microservice({ ... });
1128
+ const port = await engine.start(); // 返回实际使用的端口
1129
+ // 或指定端口
1130
+ const port = await engine.start(3000);
1131
+ ```
1132
+
1133
+ #### 7. 已移除的功能
1134
+
1135
+ 以下功能在 2.x 版本中已被移除:
1136
+
1137
+ - **WebSocket 支持**:1.x 版本中的 WebSocket 功能已移除
1138
+ - **startCheck**:启动前检查功能已整合到主包,可以直接从 `nebula-engine` 导入
1139
+ - **内置 PageRenderPlugin**:页面渲染功能已整合到 RoutePlugin 中
1140
+
1141
+ ### 迁移步骤
1142
+
1143
+ 1. **更新依赖**:
1144
+ ```bash
1145
+ npm install nebula-engine@^2.0.0
1146
+ ```
1147
+
1148
+ 2. **重构引擎创建**:
1149
+ ```typescript
1150
+ // 旧代码
1151
+ const service = new Microservice({
1152
+ modules: [UserService],
1153
+ plugins: [new CachePlugin()],
1154
+ });
1155
+
1156
+ // 新代码
1157
+ const { Module, Microservice } = Factory.create(
1158
+ new ActionPlugin(),
1159
+ new RoutePlugin(),
1160
+ new CachePlugin()
1161
+ );
1162
+ const engine = new Microservice({
1163
+ name: "user-service",
1164
+ version: "1.0.0",
1165
+ });
1166
+ ```
1167
+
1168
+ 3. **更新模块定义**:
1169
+ ```typescript
1170
+ // 确保使用 Factory.create 返回的 Module
1171
+ const { Module } = Factory.create(...plugins);
1172
+
1173
+ @Module("users")
1174
+ class UserService {}
1175
+ ```
1176
+
1177
+ 4. **分离缓存装饰器**:
1178
+ ```typescript
1179
+ // 旧代码
1180
+ @Action({ cache: true, cacheTTL: 60 })
1181
+
1182
+ // 新代码
1183
+ @Action({ ... })
1184
+ @Cache({ ttl: 60000 })
1185
+ ```
1186
+
1187
+ 5. **更新客户端生成**:
1188
+ ```typescript
1189
+ // 添加 ClientCodePlugin
1190
+ const { Module, Microservice } = Factory.create(
1191
+ new ActionPlugin(),
1192
+ new ClientCodePlugin()
1193
+ );
1194
+ ```
1195
+
1196
+ 6. **更新启动代码**:
1197
+ ```typescript
1198
+ // 旧代码
1199
+ await service.init();
1200
+ service.start(3000);
1201
+
1202
+ // 新代码
1203
+ const port = await engine.start(3000);
1204
+ ```
1205
+
1206
+ ### 兼容性说明
1207
+
1208
+ - **装饰器 API**:`@Action`、`@Route`、`@Cache` 等装饰器的 API 基本保持不变
1209
+ - **类型系统**:类型推断机制保持不变,但需要显式注册插件
1210
+ - **中间件系统**:中间件 API 保持不变,但配置方式有所变化
1211
+
1212
+ ## 开发
1213
+
1214
+ ### 项目结构
1215
+
1216
+ ```
1217
+ src/
1218
+ core/ # 核心框架代码
1219
+ factory.ts # 工厂类
1220
+ engine.ts # 引擎核心
1221
+ types.ts # 类型定义
1222
+ decorators.ts # 装饰器实现
1223
+ plugins/ # 插件实现
1224
+ action/ # Action 插件
1225
+ route/ # Route 插件
1226
+ cache/ # Cache 插件
1227
+ client-code/ # ClientCode 插件
1228
+ schedule/ # Schedule 插件
1229
+ graceful-shutdown/ # GracefulShutdown 插件
1230
+ index.ts # 入口文件
1231
+ ```
1232
+
1233
+ ### 开发命令
1234
+
1235
+ ```bash
1236
+ # 安装依赖
1237
+ npm install
1238
+
1239
+ # 运行测试
1240
+ npm test
1241
+
1242
+ # 编译
1243
+ npm run build
1244
+
1245
+ # 开发模式(启动集成测试服务)
1246
+ npm run dev
1247
+ ```
1248
+
1249
+ ### 贡献指南
1250
+
1251
+ 欢迎提交 Issue 和 Pull Request!
1252
+
1253
+ ## License
1254
+
1255
+ MIT