imean-service-engine
Version:
基于Hono的轻量级微服务引擎框架
1,256 lines (1,000 loc) • 32.5 kB
Markdown
# IMean Service Engine v2
基于 Hono 的轻量级微服务引擎框架,支持插件化扩展和自动类型推断。
## 目录
- [特性](#特性)
- [快速开始](#快速开始)
- [框架概述](#框架概述)
- [核心原理](#核心原理)
- [插件系统](#插件系统)
- [内置插件](#内置插件)
- [Action 插件](#action-插件)
- [Route 插件](#route-插件)
- [Cache 插件](#cache-插件)
- [ClientCode 插件](#clientcode-插件)
- [Schedule 插件](#schedule-插件)
- [GracefulShutdown 插件](#gracefulshutdown-插件)
- [最佳实践](#最佳实践)
- [测试指南](#测试指南)
- [从 1.x 迁移到 2.x](#从-1x-迁移到-2x)
- [开发](#开发)
## 特性
- 🎯 **基于 Hono 的高性能 HTTP Server**:利用 Hono 的轻量级和快速特性
- 🔌 **插件化架构**:所有功能都通过插件实现,支持自定义扩展
- 🎨 **装饰器驱动的 API 定义**:使用装饰器简化代码,提高可读性
- 📝 **自动类型推断**:基于 TypeScript 和 Zod 实现完整的类型安全
- 🔄 **支持多装饰器叠加**:可以同时使用路由、缓存、限流等多个装饰器
- 🚀 **显式插件注册**:所有插件必须显式注册,避免隐式依赖
- 🛡️ **运行时类型验证**:使用 Zod 进行参数和返回值验证
- 🌊 **流式传输支持**:支持 AsyncIterator 流式数据传输
- 📦 **自动客户端生成**:自动生成类型化的客户端代码,支持服务间互调
- ⏰ **定时任务支持**:基于 etcd 的分布式定时任务调度
- 🛑 **优雅停机**:自动追踪处理器执行,支持优雅停机
## 快速开始
### 安装
```bash
npm install imean-service-engine
```
### 基本示例
```typescript
import {
Factory,
ActionPlugin,
Action,
RoutePlugin,
Route,
z,
} from "imean-service-engine";
// 1. 创建引擎工厂(必须显式注册所有需要的插件)
const { Module, Microservice } = Factory.create(
new ActionPlugin(),
new RoutePlugin()
);
// 2. 定义数据模型
const UserSchema = z.object({
id: z.string(),
name: z.string(),
age: z.number(),
});
type User = z.infer<typeof UserSchema>;
// 3. 定义服务模块
@Module("users")
class UserService {
private users = new Map<string, User>();
@Action({
description: "获取用户信息",
params: [z.string()],
returns: UserSchema,
})
async getUser(id: string): Promise<User> {
const user = this.users.get(id);
if (!user) {
throw new Error("用户不存在");
}
return user;
}
@Route({
method: "GET",
path: "/health",
})
async health() {
return { status: "ok" };
}
}
// 4. 创建并启动引擎
const engine = new Microservice({
name: "user-service",
version: "1.0.0",
prefix: "/api",
});
await engine.start();
console.log(`服务启动在端口 ${engine.getPort()}`);
```
## 框架概述
IMean Service Engine 是一个基于插件的微服务框架,核心设计理念是:
1. **插件化架构**:所有功能都通过插件实现,包括路由、缓存、定时任务等
2. **显式注册**:所有插件必须显式注册,不会自动包含任何默认插件
3. **类型安全**:基于 TypeScript 和 Zod 实现完整的类型推断和运行时验证
4. **装饰器驱动**:使用装饰器简化 API 定义,提高代码可读性
### 核心组件
- **Factory**:创建类型化的引擎工厂,返回 `Module` 装饰器和 `Microservice` 类
- **Microservice**:引擎核心类,管理插件生命周期和模块实例
- **Plugin**:插件接口,定义插件的生命周期钩子和优先级
- **Handler**:处理器元数据,支持通过 `wrap` API 包装方法
## 核心原理
### 工厂模式
框架使用工厂模式创建类型化的引擎实例:
```typescript
const { Module, Microservice } = Factory.create(
new ActionPlugin(),
new RoutePlugin()
);
```
`Factory.create` 会:
1. 合并所有插件的 Module 配置类型
2. 创建类型化的 `Module` 装饰器
3. 创建类型化的 `Microservice` 类
### 插件系统
插件系统采用**优先级驱动的洋葱圈模型**:
1. **优先级排序**:插件按优先级自动排序(数值越小,优先级越高)
2. **包装链构建**:包装插件通过 `handler.wrap()` 构建包装链
3. **路由注册**:路由插件最后执行,注册 HTTP 路由
详细说明请参考 [插件系统文档](./docs/plugin-system.md)。
### 模块发现机制
框架使用 Symbol 作为模块元数据的键,实现模块发现:
1. 每个 `Factory.create` 调用生成唯一的 Symbol
2. `Module` 装饰器使用该 Symbol 存储模块元数据
3. 引擎启动时通过 Symbol 查找所有模块
这种机制确保了不同工厂实例之间的隔离。
## 插件系统
框架的核心是插件系统,所有功能都通过插件实现。插件系统采用优先级驱动的洋葱圈模型,让用户无需关心插件注册顺序。
**详细文档请参考:[插件系统完整指南](./docs/plugin-system.md)**
### 插件优先级
```typescript
export enum PluginPriority {
SYSTEM = 50, // 系统级插件(优雅停机等)
SECURITY = 100, // 安全相关插件(限流、认证等)
LOGGING = 200, // 日志、监控插件
BUSINESS = 300, // 业务逻辑插件(默认)
PERFORMANCE = 400, // 性能优化插件(缓存等)
ROUTE = 1000, // 路由插件(必须最后执行)
}
```
### 插件生命周期
插件可以定义以下生命周期钩子:
- `onInit`:引擎初始化时调用
- `onModuleLoad`:模块加载后调用
- `onHandlerLoad`:处理器加载时调用(可以调用 `handler.wrap()`)
- `onBeforeStart`:引擎启动前调用
- `onAfterStart`:引擎启动后调用
- `onDestroy`:引擎销毁时调用
## 内置插件
### Action 插件
Action 插件用于定义 RPC 风格的 API 端点,支持参数和返回值验证。
#### 安装和注册
```typescript
import { ActionPlugin, Action } from "imean-service-engine";
const { Module, Microservice } = Factory.create(
new ActionPlugin()
);
```
#### 基本用法
```typescript
import { z } from "imean-service-engine";
@Module("users")
class UserService {
@Action({
description: "创建用户",
params: [z.string(), z.number()],
returns: UserSchema,
})
async createUser(name: string, age: number): Promise<User> {
return { id: "1", name, age };
}
}
```
#### 特性
- **参数验证**:使用 Zod schema 验证请求参数
- **返回值验证**:使用 Zod schema 验证返回值
- **EJSON 支持**:自动处理 EJSON 序列化/反序列化,支持更多数据类型
- **流式传输**:支持 `async generator` 函数,实现流式数据传输
- **幂等性标记**:支持 `idempotence: true` 标记,用于客户端重试
- **Context 注入**:如果方法签名包含 `Context` 参数,自动注入 Hono Context
#### 流式传输示例
```typescript
@Action({
description: "流式返回数据",
params: [z.number()],
returns: z.number(),
stream: true,
})
async *streamNumbers(count: number) {
for (let i = 0; i < count; i++) {
yield i;
await new Promise((resolve) => setTimeout(resolve, 100));
}
}
```
#### 模块配置
```typescript
@Module("users", {
actionMiddlewares: [
async (ctx, next) => {
// 模块级中间件
await next();
},
],
})
class UserService {
@Action({
description: "获取用户",
params: [z.string()],
returns: UserSchema,
middlewares: [
async (ctx, next) => {
// 动作级中间件
await next();
},
],
})
async getUser(id: string) {}
}
```
#### ActionOptions 配置
```typescript
interface ActionOptions {
description?: string; // 动作描述
params?: z.ZodTypeAny[]; // 参数验证 schema
returns?: z.ZodTypeAny; // 返回值验证 schema
stream?: boolean; // 是否流式传输
idempotence?: boolean; // 是否幂等操作
middlewares?: MiddlewareHandler[]; // 动作级中间件
}
```
### Route 插件
Route 插件用于定义 HTTP 路由,支持 RESTful API 和页面路由。
#### 安装和注册
```typescript
import { RoutePlugin, Route, Page } from "imean-service-engine";
const { Module, Microservice } = Factory.create(
new RoutePlugin()
);
```
#### 基本用法
```typescript
import { Context } from "hono";
@Module("api")
class ApiService {
@Route({
method: "GET",
path: "/users/:id",
})
async getUser(ctx: Context) {
const id = ctx.req.param("id");
return ctx.json({ id, name: "John" });
}
@Page({
path: "/dashboard",
})
async dashboard(ctx: Context) {
return <div>Dashboard</div>;
}
}
```
#### 特性
- **多路径支持**:支持为同一个处理器注册多个路径
- **多方法支持**:支持为同一个路径注册多个 HTTP 方法
- **中间件支持**:支持全局、模块级、路由级三层中间件
- **自动响应处理**:自动处理 `Response` 对象、JSX 元素和普通对象
- **路由描述**:支持 `description` 字段,用于文档生成
#### 中间件示例
```typescript
// 全局中间件(在 RoutePlugin 构造函数中配置)
const routePlugin = new RoutePlugin({
globalMiddlewares: [
async (ctx, next) => {
// 全局鉴权中间件
const token = ctx.req.header("Authorization");
if (!token) {
return ctx.json({ error: "Unauthorized" }, 401);
}
await next();
},
],
});
// 模块级中间件
@Module("api", {
routeMiddlewares: [
async (ctx, next) => {
// 模块级中间件
await next();
},
],
})
class ApiService {
@Route({
method: "GET",
path: "/users",
middlewares: [
async (ctx, next) => {
// 路由级中间件
await next();
},
],
})
async getUsers() {}
}
```
#### 多路径示例
```typescript
@Route({
path: ["/", "/home", "/dashboard"],
description: "首页",
})
async homePage(ctx: Context) {
return <HomePage />;
}
```
#### RouteOptions 配置
```typescript
interface RouteOptions {
method?: HTTPMethod | HTTPMethod[]; // HTTP 方法(默认 GET)
path: string | string[]; // 路由路径(支持多个)
middlewares?: MiddlewareHandler[]; // 路由级中间件
description?: string; // 路由描述
}
```
### Cache 插件
Cache 插件提供方法级别的缓存功能,支持内存和 Redis 两种存储后端。
#### 安装和注册
```typescript
import {
CachePlugin,
Cache,
MemoryCacheAdapter,
RedisCacheAdapter,
} from "imean-service-engine";
// 使用内存缓存(默认)
const cachePlugin = new CachePlugin();
// 或使用 Redis 缓存
const cachePlugin = new CachePlugin(
new RedisCacheAdapter({ client: redisClient })
);
```
#### 基本用法
```typescript
@Module("users")
class UserService {
@Cache({ ttl: 5000 }) // 缓存 5 秒
async getUser(id: string): Promise<User> {
// 这个方法的结果会被缓存
return fetchUserFromDatabase(id);
}
}
```
#### 自定义缓存键
```typescript
@Cache({
ttl: 5000,
key: (id: string, name: string) => ({ id, name }), // 自定义键生成
})
async getUser(id: string, name: string): Promise<User> {
return fetchUser(id, name);
}
```
#### 特性
- **多种存储后端**:支持内存和 Redis
- **TTL 支持**:支持设置缓存过期时间
- **自定义键生成**:支持自定义缓存键生成函数
- **自动清理**:内存缓存支持自动清理过期项
- **模块配置**:支持模块级别的默认 TTL 和启用/禁用
#### 模块配置
```typescript
@Module("users", {
cacheDefaultTtl: 10000, // 模块默认 TTL(10秒)
cacheEnabled: true, // 模块默认启用缓存
cacheCleanupInterval: 60000, // 清理间隔(60秒)
})
class UserService {
@Cache({ ttl: 5000 }) // 会覆盖模块默认值
async getUser() {}
}
```
#### CacheOptions 配置
```typescript
interface CacheOptions {
ttl?: number; // 缓存过期时间(毫秒,默认60000)
key?: (...args: any[]) => any; // 自定义键生成函数
enabled?: boolean; // 是否启用缓存(默认true)
}
```
#### 缓存键生成
缓存键的生成规则:
1. 如果提供了 `key` 函数,使用其返回值
2. 否则使用方法参数(args)
3. 将结果进行 ejson 序列化
4. 使用 SHA256 哈希生成最终键
5. 最终格式:`{moduleName}:{methodName}:{hash}`
示例:
```typescript
@Cache({
key: (id: string, date: Date) => ({ id, date }), // 返回对象
ttl: 5000,
})
async getUserData(id: string, date: Date) {
// 缓存键:users:getUserData:{hash}
}
```
### ClientCode 插件
ClientCode 插件自动生成类型化的客户端代码,支持服务间互调。
#### 安装和注册
```typescript
import { ClientCodePlugin } from "imean-service-engine";
const { Module, Microservice } = Factory.create(
new ActionPlugin(),
new ClientCodePlugin({
clientSavePath: "./generated/client.ts", // 可选:保存到本地文件
})
);
```
#### 使用生成的客户端
1. **下载客户端代码**:访问 `http://localhost:3000/api/client.ts` 下载生成的代码
2. **使用客户端**:
```typescript
import { GeneratedClient } from "./generated/client";
const client = new GeneratedClient({
baseUrl: "http://localhost:3000",
prefix: "/api",
});
// 调用服务方法
const user = await client.users.getUser("123");
```
#### 特性
- **自动生成**:基于 Action 装饰器自动生成客户端代码
- **类型安全**:生成的客户端代码包含完整的类型定义
- **参数名提取**:自动提取方法参数名,生成更友好的 API
- **流式传输支持**:生成的客户端支持流式传输(AsyncIterator)
- **幂等性支持**:生成的客户端支持幂等性标记,用于自动重试
- **本地保存**:支持将生成的代码保存到本地文件,方便开发调试
- **EJSON 支持**:生成的客户端自动支持 EJSON 序列化/反序列化
#### 客户端代码示例
生成的客户端代码示例:
```typescript
export class MicroserviceClient extends BaseMicroserviceClient {
public readonly users = this.registerModule<UsersModule>("users", {
getUser: { stream: false, idempotent: false },
createUser: { stream: false, idempotent: false },
});
}
// 使用
const client = new MicroserviceClient({
baseUrl: "http://localhost:3000",
prefix: "/api",
});
const user = await client.users.getUser("123");
```
### Schedule 插件
Schedule 插件提供分布式定时任务功能,基于 etcd 实现主节点选举。
#### 安装和注册
```typescript
import { SchedulePlugin, Schedule } from "imean-service-engine";
import { Etcd3 } from "etcd3";
// 使用真实的 etcd
const etcdClient = new Etcd3({
hosts: ["localhost:2379"],
});
const { Module, Microservice } = Factory.create(
new SchedulePlugin({ etcdClient })
);
// 或使用 Mock Etcd(用于测试和本地开发)
const { Module, Microservice } = Factory.create(
new SchedulePlugin({ useMockEtcd: true })
);
```
#### 基本用法
```typescript
@Module("tasks")
class TaskService {
@Schedule({
interval: 60000, // 60秒执行一次
mode: ScheduleMode.FIXED_RATE, // 固定频率
})
async cleanupTask() {
// 清理任务
}
@Schedule({
interval: 5000,
mode: ScheduleMode.FIXED_DELAY, // 固定延迟(上次执行完成后延迟)
})
async reportTask() {
// 报告任务
}
}
```
#### 特性
- **分布式调度**:基于 etcd 实现主节点选举,确保多实例中只有一个执行任务
- **两种模式**:
- `FIXED_RATE`:固定频率,按固定间隔执行
- `FIXED_DELAY`:固定延迟,上次执行完成后延迟指定时间再执行
- **Mock Etcd 支持**:支持使用 Mock Etcd,无需真实 etcd 服务即可开发和测试
- **OpenTelemetry 追踪**:支持 OpenTelemetry 追踪
详细文档请参考:[Schedule 插件文档](./src/plugins/schedule/README.md)
### GracefulShutdown 插件
GracefulShutdown 插件提供优雅停机功能,自动追踪处理器执行并等待完成。
#### 安装和注册
```typescript
import { GracefulShutdownPlugin } from "imean-service-engine";
const { Module, Microservice } = Factory.create(
new ActionPlugin(),
new RoutePlugin(),
new GracefulShutdownPlugin({
shutdownTimeout: 10 * 60 * 1000, // 10分钟超时(默认)
})
);
```
#### 工作原理
1. **处理器追踪**:自动追踪所有处理器的执行状态(Action、Route、Schedule 等)
2. **信号监听**:监听 `SIGINT`、`SIGTERM`、`SIGBREAK` 等系统信号
3. **优雅停机**:
- 收到信号后,拒绝新的请求
- 等待所有正在执行的处理器完成
- 如果超时,强制停机
- 停止引擎并退出进程
#### 配置选项
```typescript
new GracefulShutdownPlugin({
shutdownTimeout: 10 * 60 * 1000, // 停机超时时间(默认10分钟)
enabled: true, // 是否启用(默认true)
})
```
#### 特性
- **自动追踪**:自动追踪所有处理器的执行状态
- **跨平台支持**:兼容 Windows、Linux、macOS
- **超时保护**:支持设置停机超时时间,防止无限等待
- **拒绝新请求**:停机期间自动拒绝新的请求
## 最佳实践
### 1. 插件注册顺序
虽然插件系统会自动按优先级排序,但建议按功能分组注册插件:
```typescript
const { Module, Microservice } = Factory.create(
// 系统插件
new GracefulShutdownPlugin(),
// 业务插件
new ActionPlugin(),
new RoutePlugin(),
// 性能插件
new CachePlugin(),
// 工具插件
new ClientCodePlugin(),
new SchedulePlugin({ useMockEtcd: true }),
);
```
### 2. 模块组织
建议按功能模块组织代码:
```typescript
// services/user-service.ts
@Module("users")
class UserService {
@Action({ ... })
async getUser() {}
}
// services/order-service.ts
@Module("orders")
class OrderService {
@Action({ ... })
async getOrder() {}
}
```
### 3. 类型定义
使用 Zod schema 定义数据类型,实现类型安全和运行时验证:
```typescript
import { z } from "imean-service-engine";
const UserSchema = z.object({
id: z.string(),
name: z.string(),
age: z.number().min(0).max(150),
});
type User = z.infer<typeof UserSchema>;
```
### 4. 错误处理
在 Action 方法中抛出错误,框架会自动处理:
```typescript
@Action({
params: [z.string()],
returns: UserSchema,
})
async getUser(id: string): Promise<User> {
const user = await db.findUser(id);
if (!user) {
throw new Error("用户不存在"); // 框架会自动返回错误响应
}
return user;
}
```
### 5. 中间件使用
合理使用中间件实现横切关注点:
```typescript
// 认证中间件
const authMiddleware = async (ctx: Context, next: () => Promise<void>) => {
const token = ctx.req.header("Authorization");
if (!token) {
return ctx.json({ error: "Unauthorized" }, 401);
}
// 验证 token 并注入用户信息
ctx.set("user", { id: "123", name: "John" });
await next();
};
// 在 RoutePlugin 中配置全局中间件
const routePlugin = new RoutePlugin({
globalMiddlewares: [authMiddleware],
});
```
### 6. 缓存策略
合理使用缓存提高性能:
```typescript
// 频繁查询但变化不频繁的数据
@Cache({ ttl: 5 * 60 * 1000 }) // 缓存 5 分钟
async getConfig(): Promise<Config> {
return db.findConfig();
}
// 使用自定义键避免缓存冲突
@Cache({
ttl: 60000,
key: (userId: string, type: string) => ({ userId, type }),
})
async getUserData(userId: string, type: string) {
return db.findUserData(userId, type);
}
```
### 7. 版本路由
引擎启动后会自动注册版本路由 `{prefix}/version`(如果路径未被占用),用于健康检查:
```typescript
// 访问 http://localhost:3000/api/version
// 返回:
{
"name": "user-service",
"version": "1.0.0",
"status": "running"
}
```
### 8. 预检机制
框架提供了预检机制,用于在服务启动前进行必要的检查和初始化(如数据库连接、Redis 连接等):
```typescript
import { startCheck, PreStartChecker } from "imean-service-engine";
// 定义预检项
const checkers: PreStartChecker[] = [
{
name: "数据库连接检查",
check: async () => {
const db = await connectDB();
await db.ping();
},
},
{
name: "Redis 连接检查",
check: async () => {
const redis = await connectRedis();
await redis.ping();
},
},
{
name: "可选检查项",
check: async () => {
// 某些检查
},
skip: true, // 可以跳过某些检查
},
];
// 执行预检并启动服务
await startCheck(checkers, async () => {
const { Module, Microservice } = Factory.create(
new ActionPlugin(),
new RoutePlugin()
);
const engine = new Microservice({
name: "user-service",
version: "1.0.0",
});
await engine.start();
console.log(`服务启动在端口 ${engine.getPort()}`);
});
```
预检机制的优势:
- **依赖检查**:确保所有必要的外部服务都可用
- **优雅失败**:如果检查失败,服务不会启动
- **清晰日志**:提供详细的检查日志,便于问题诊断
- **可选检查**:支持跳过某些非必需的检查项
## 测试指南
框架提供了两种测试方法,根据测试场景选择合适的测试方式,可以避免不必要的 HTTP 服务器启动,提高测试效率。
### `engine.handler` - 用于不依赖 Hono 的场景
**适用场景**:
- Action 插件测试(不涉及 Hono 中间件)
- Cache 插件测试
- 其他不依赖 HTTP 层的插件测试
- 测试远程 RPC 调用逻辑
**优势**:
- 无需启动 HTTP 服务器,测试更快
- 更符合 RPC 调用的语义
- 完整的类型推导支持(自动推导参数和返回值类型)
- 自动执行包装链(缓存、限流等)
**使用示例**:
```typescript
import { Testing } from "../../core/testing";
import { ActionPlugin, Action } from "./index";
import { z } from "zod";
describe("ActionPlugin", () => {
let engine: ReturnType<typeof Testing.createTestEngine>["engine"];
let Module: ReturnType<typeof Testing.createTestEngine>["Module"];
beforeEach(() => {
const testEngine = Testing.createTestEngine({
plugins: [new ActionPlugin()],
});
engine = testEngine.engine;
Module = testEngine.Module;
});
it("应该能够调用 Action handler", async () => {
@Module("users")
class UserService {
@Action({ params: [z.string()] })
getUser(id: string): { id: string; name: string } {
return { id, name: "Alice" };
}
}
// 获取 handler 并调用(类型自动推导)
const getUserHandler = engine.handler(UserService, "getUser");
const result = await getUserHandler("123");
// result 的类型自动推导为 { id: string; name: string }
expect(result).toEqual({ id: "123", name: "Alice" });
// 或者链式调用
const result2 = await engine.handler(UserService, "getUser")("456");
expect(result2).toEqual({ id: "456", name: "Alice" });
});
});
```
### `engine.request` - 用于依赖 Hono 的场景
**适用场景**:
- Route 插件测试(需要测试路由、中间件、Context 等)
- 需要测试完整 HTTP 请求/响应流程的场景
- 需要测试 Hono 中间件的场景
**优势**:
- 完整执行 Hono 中间件链
- 支持所有 HTTP 方法和请求选项
- 返回标准 `Response` 对象
- 无需启动 HTTP 服务器
**使用示例**:
```typescript
import { Testing } from "../../core/testing";
import { RoutePlugin, Route } from "./index";
import { Context } from "hono";
describe("RoutePlugin", () => {
let engine: ReturnType<typeof Testing.createTestEngine>["engine"];
let Module: ReturnType<typeof Testing.createTestEngine>["Module"];
beforeEach(() => {
const testEngine = Testing.createTestEngine({
plugins: [new RoutePlugin()],
});
engine = testEngine.engine;
Module = testEngine.Module;
});
it("应该能够调用 Route handler", async () => {
@Module("users")
class UserService {
@Route({ path: "/users/:id" })
getUser(ctx: Context) {
const id = ctx.req.param("id");
return { id, name: "Alice" };
}
}
// 使用 request 方法(完整执行中间件)
const response = await engine.request("/users/123");
expect(response.ok).toBe(true);
const data = await response.json();
expect(data).toEqual({ id: "123", name: "Alice" });
// 使用 Request 对象
const request = new Request("http://localhost/users/456", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name: "Bob" }),
});
const response2 = await engine.request(request);
expect(response2.ok).toBe(true);
});
});
```
### 选择指南
| 场景 | 使用方法 | 原因 |
|------|---------|------|
| Action 插件测试 | `engine.handler` | 不依赖 Hono,表示 RPC 调用 |
| Route 插件测试 | `engine.request` | 需要测试路由和中间件 |
| Cache 插件测试 | `engine.handler` | 不依赖 Hono,测试包装链 |
| 中间件测试 | `engine.request` | 需要完整执行中间件链 |
| 集成测试 | `fetch` + `engine.start()` | 需要真实 HTTP 服务器 |
### 注意事项
1. **集成测试**:应使用 `fetch` + `engine.start()` 启动真实 HTTP 服务器,确保测试场景接近生产环境
2. **特定测试**:`engine.test.ts` 中的特定测试可能故意设计为测试特定功能,保持原有方式
3. **避免混用**:在同一测试文件中保持一致的测试方法
4. **类型推导**:`engine.handler` 支持完整的类型推导,无需显式指定泛型参数
## 从 1.x 迁移到 2.x
### 主要变化
#### 1. 插件系统重构
**1.x 版本**:
- 使用 `new Microservice({ modules: [...], plugins: [...] })` 创建引擎
- 某些插件(如 RoutePlugin)作为默认插件自动包含
**2.x 版本**:
- 使用 `Factory.create(...plugins)` 创建引擎工厂
- **所有插件都必须显式注册**,不再有默认插件
```typescript
// 1.x
const service = new Microservice({
modules: [UserService],
plugins: [new CachePlugin()],
});
// 2.x
const { Module, Microservice } = Factory.create(
new ActionPlugin(),
new RoutePlugin(),
new CachePlugin()
);
const engine = new Microservice({
name: "user-service",
version: "1.0.0",
});
```
#### 2. 模块定义方式变化
**1.x 版本**:
```typescript
@Module("users", {
description: "用户服务模块",
version: "1.0.0",
})
class UserService {}
```
**2.x 版本**:
```typescript
// Module 装饰器由 Factory.create 返回
const { Module } = Factory.create(...plugins);
@Module("users", {
// 插件配置直接平铺在 options 中
cacheDefaultTtl: 5000,
routePrefix: "/api/v1",
})
class UserService {}
```
#### 3. Action 装饰器变化
**1.x 版本**:
```typescript
@Action({
cache: true,
cacheTTL: 60,
})
async getUser() {}
```
**2.x 版本**:
```typescript
// 缓存功能独立为 Cache 插件
@Action({ ... })
@Cache({ ttl: 60000 })
async getUser() {}
```
#### 4. Page 装饰器变化
**1.x 版本**:
- `Page` 装饰器需要 `PageRenderPlugin`
**2.x 版本**:
- `Page` 装饰器是 `Route` 的别名,由 `RoutePlugin` 提供
- 不再需要单独的 `PageRenderPlugin`
```typescript
// 1.x
import { Page, PageRenderPlugin } from "imean-service-engine";
const service = new Microservice({
plugins: [new PageRenderPlugin()],
});
// 2.x
import { RoutePlugin, Page } from "imean-service-engine";
const { Module } = Factory.create(new RoutePlugin());
```
#### 5. 客户端生成变化
**1.x 版本**:
```typescript
const service = new Microservice({
generateClient: new URL("./client.ts", import.meta.url),
});
```
**2.x 版本**:
```typescript
const { Module, Microservice } = Factory.create(
new ActionPlugin(),
new ClientCodePlugin({
clientSavePath: "./generated/client.ts", // 可选
})
);
// 客户端代码自动在 {prefix}/client.ts 提供下载
```
#### 6. 启动方式变化
**1.x 版本**:
```typescript
const service = new Microservice({ ... });
await service.init();
service.start(3000);
```
**2.x 版本**:
```typescript
const engine = new Microservice({ ... });
const port = await engine.start(); // 返回实际使用的端口
// 或指定端口
const port = await engine.start(3000);
```
#### 7. 已移除的功能
以下功能在 2.x 版本中已被移除:
- **WebSocket 支持**:1.x 版本中的 WebSocket 功能已移除
- **startCheck**:启动前检查功能已整合到主包,可以直接从 `imean-service-engine` 导入
- **内置 PageRenderPlugin**:页面渲染功能已整合到 RoutePlugin 中
### 迁移步骤
1. **更新依赖**:
```bash
npm install imean-service-engine@^2.0.0
```
2. **重构引擎创建**:
```typescript
// 旧代码
const service = new Microservice({
modules: [UserService],
plugins: [new CachePlugin()],
});
// 新代码
const { Module, Microservice } = Factory.create(
new ActionPlugin(),
new RoutePlugin(),
new CachePlugin()
);
const engine = new Microservice({
name: "user-service",
version: "1.0.0",
});
```
3. **更新模块定义**:
```typescript
// 确保使用 Factory.create 返回的 Module
const { Module } = Factory.create(...plugins);
@Module("users")
class UserService {}
```
4. **分离缓存装饰器**:
```typescript
// 旧代码
@Action({ cache: true, cacheTTL: 60 })
// 新代码
@Action({ ... })
@Cache({ ttl: 60000 })
```
5. **更新客户端生成**:
```typescript
// 添加 ClientCodePlugin
const { Module, Microservice } = Factory.create(
new ActionPlugin(),
new ClientCodePlugin()
);
```
6. **更新启动代码**:
```typescript
// 旧代码
await service.init();
service.start(3000);
// 新代码
const port = await engine.start(3000);
```
### 兼容性说明
- **装饰器 API**:`@Action`、`@Route`、`@Cache` 等装饰器的 API 基本保持不变
- **类型系统**:类型推断机制保持不变,但需要显式注册插件
- **中间件系统**:中间件 API 保持不变,但配置方式有所变化
## 开发
### 项目结构
```
src/
core/ # 核心框架代码
factory.ts # 工厂类
engine.ts # 引擎核心
types.ts # 类型定义
decorators.ts # 装饰器实现
plugins/ # 插件实现
action/ # Action 插件
route/ # Route 插件
cache/ # Cache 插件
client-code/ # ClientCode 插件
schedule/ # Schedule 插件
graceful-shutdown/ # GracefulShutdown 插件
index.ts # 入口文件
```
### 开发命令
```bash
# 安装依赖
npm install
# 运行测试
npm test
# 编译
npm run build
# 开发模式(启动集成测试服务)
npm run dev
```
### 贡献指南
欢迎提交 Issue 和 Pull Request!
## License
MIT