@dangao/bun-server 1.1.2 → 1.1.4

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.
@@ -0,0 +1,451 @@
1
+ # 性能优化指南
2
+
3
+ 本文档介绍 Bun Server Framework 应用的性能优化技术。
4
+
5
+ ## 目录
6
+
7
+ - [基准测试](#基准测试)
8
+ - [缓存策略](#缓存策略)
9
+ - [数据库优化](#数据库优化)
10
+ - [中间件优化](#中间件优化)
11
+ - [响应优化](#响应优化)
12
+ - [内存管理](#内存管理)
13
+ - [并发处理](#并发处理)
14
+
15
+ ## 基准测试
16
+
17
+ ### 使用 Performance Harness
18
+
19
+ 使用内置的 `PerformanceHarness` 进行基准测试:
20
+
21
+ ```typescript
22
+ import { PerformanceHarness } from "@dangao/bun-server/testing";
23
+
24
+ const result = await PerformanceHarness.benchmark(
25
+ "my-operation",
26
+ 1000,
27
+ async () => {
28
+ // Your code to benchmark
29
+ await doSomething();
30
+ },
31
+ );
32
+
33
+ console.log(`Operations per second: ${result.opsPerSecond}`);
34
+ console.log(`Total duration: ${result.durationMs}ms`);
35
+ ```
36
+
37
+ ### 压力测试
38
+
39
+ 使用 `StressTester` 进行并发负载测试:
40
+
41
+ ```typescript
42
+ import { StressTester } from "@dangao/bun-server/testing";
43
+
44
+ const result = await StressTester.run(
45
+ "my-endpoint",
46
+ 1000, // iterations
47
+ 10, // concurrency
48
+ async (iteration) => {
49
+ const response = await fetch("http://localhost:3000/api/test");
50
+ await response.text();
51
+ },
52
+ );
53
+
54
+ console.log(`Errors: ${result.errors}`);
55
+ console.log(`Duration: ${result.durationMs}ms`);
56
+ ```
57
+
58
+ ## 缓存策略
59
+
60
+ ### 使用 CacheModule
61
+
62
+ 为频繁访问的数据启用缓存:
63
+
64
+ ```typescript
65
+ import {
66
+ CACHE_SERVICE_TOKEN,
67
+ CacheModule,
68
+ CacheService,
69
+ } from "@dangao/bun-server";
70
+
71
+ CacheModule.forRoot({
72
+ store: new RedisCacheStore(redisClient), // Use Redis for distributed caching
73
+ defaultTtl: 3600000, // 1 hour
74
+ });
75
+
76
+ @Injectable()
77
+ class ProductService {
78
+ public constructor(
79
+ @Inject(CACHE_SERVICE_TOKEN) private readonly cache: CacheService,
80
+ ) {}
81
+
82
+ public async getProduct(id: string) {
83
+ return await this.cache.getOrSet(
84
+ `product:${id}`,
85
+ async () => {
86
+ return await this.db.findProduct(id);
87
+ },
88
+ 3600000, // Cache for 1 hour
89
+ );
90
+ }
91
+ }
92
+ ```
93
+
94
+ ### 缓存装饰器
95
+
96
+ 使用缓存装饰器实现自动缓存:
97
+
98
+ ```typescript
99
+ import { Cacheable, CacheEvict, CachePut } from "@dangao/bun-server";
100
+
101
+ @Controller("/api/products")
102
+ class ProductController {
103
+ @GET("/:id")
104
+ @Cacheable({ key: "product", ttl: 3600000 })
105
+ public async getProduct(@Param("id") id: string) {
106
+ return await this.productService.find(id);
107
+ }
108
+
109
+ @PUT("/:id")
110
+ @CachePut({ key: "product" })
111
+ @CacheEvict({ key: "products:list" })
112
+ public async updateProduct(@Param("id") id: string, @Body() data: any) {
113
+ return await this.productService.update(id, data);
114
+ }
115
+ }
116
+ ```
117
+
118
+ ### 缓存失效
119
+
120
+ 实现缓存失效策略:
121
+
122
+ ```typescript
123
+ // Invalidate on update
124
+ @PUT('/:id')
125
+ @CacheEvict({ key: 'product', pattern: true })
126
+ public async updateProduct(@Param('id') id: string) {
127
+ // Cache for 'product:*' will be invalidated
128
+ }
129
+
130
+ // Invalidate multiple keys
131
+ @DELETE('/:id')
132
+ @CacheEvict({ keys: ['product', 'products:list'] })
133
+ public async deleteProduct(@Param('id') id: string) {
134
+ // Both caches invalidated
135
+ }
136
+ ```
137
+
138
+ ## 数据库优化
139
+
140
+ ### 连接池
141
+
142
+ 配置适当的连接池大小:
143
+
144
+ ```typescript
145
+ DatabaseModule.forRoot({
146
+ database: {
147
+ type: "postgres",
148
+ config: {
149
+ connectionString: process.env.DATABASE_URL,
150
+ max: 20, // Maximum connections
151
+ min: 5, // Minimum connections
152
+ idleTimeoutMillis: 30000,
153
+ connectionTimeoutMillis: 2000,
154
+ },
155
+ },
156
+ });
157
+ ```
158
+
159
+ ### 查询优化
160
+
161
+ 使用索引并优化查询:
162
+
163
+ ```typescript
164
+ // Use indexes
165
+ @Table("users")
166
+ class User {
167
+ @Column({ primaryKey: true })
168
+ public id!: number;
169
+
170
+ @Column({ index: true }) // Add index
171
+ public email!: string;
172
+ }
173
+
174
+ // Use select specific fields
175
+ const users = await db.select("id", "name").from("users").where("active", true);
176
+
177
+ // Use pagination
178
+ const users = await db
179
+ .select()
180
+ .from("users")
181
+ .limit(20)
182
+ .offset((page - 1) * 20);
183
+ ```
184
+
185
+ ### 批量操作
186
+
187
+ 对多个插入/更新使用批量操作:
188
+
189
+ ```typescript
190
+ // Batch insert
191
+ await db.batchInsert("users", [
192
+ { name: "User 1", email: "user1@example.com" },
193
+ { name: "User 2", email: "user2@example.com" },
194
+ ]);
195
+
196
+ // Batch update
197
+ await db.transaction(async (trx) => {
198
+ for (const user of users) {
199
+ await trx("users").where("id", user.id).update(user);
200
+ }
201
+ });
202
+ ```
203
+
204
+ ## 中间件优化
205
+
206
+ ### 最小化中间件
207
+
208
+ 只使用必要的中间件:
209
+
210
+ ```typescript
211
+ // ❌ Too many middleware
212
+ app.use(middleware1);
213
+ app.use(middleware2);
214
+ app.use(middleware3);
215
+ app.use(middleware4);
216
+ app.use(middleware5);
217
+
218
+ // ✅ Combine or remove unnecessary middleware
219
+ app.use(combinedMiddleware);
220
+ ```
221
+
222
+ ### 条件中间件
223
+
224
+ 使用条件中间件以获得更好的性能:
225
+
226
+ ```typescript
227
+ app.use(async (ctx, next) => {
228
+ // Skip middleware for static files
229
+ if (ctx.path.startsWith("/static")) {
230
+ return await next();
231
+ }
232
+ // Apply middleware
233
+ return await middleware(ctx, next);
234
+ });
235
+ ```
236
+
237
+ ### 异步中间件
238
+
239
+ 确保中间件正确使用异步:
240
+
241
+ ```typescript
242
+ // ✅ Correct: Properly await next()
243
+ app.use(async (ctx, next) => {
244
+ const start = Date.now();
245
+ const response = await next();
246
+ const duration = Date.now() - start;
247
+ console.log(`Request took ${duration}ms`);
248
+ return response;
249
+ });
250
+
251
+ // ❌ Wrong: Not awaiting next()
252
+ app.use(async (ctx, next) => {
253
+ next(); // Missing await
254
+ return new Response("ok");
255
+ });
256
+ ```
257
+
258
+ ## 响应优化
259
+
260
+ ### 压缩
261
+
262
+ 在反向代理(Nginx、Caddy)中启用 gzip 压缩:
263
+
264
+ ```nginx
265
+ # Nginx
266
+ gzip on;
267
+ gzip_vary on;
268
+ gzip_min_length 1024;
269
+ gzip_types text/plain text/css application/json application/javascript;
270
+ ```
271
+
272
+ ### 响应流
273
+
274
+ 对大型响应使用流式传输:
275
+
276
+ ```typescript
277
+ @GET('/large-data')
278
+ public async getLargeData() {
279
+ const stream = new ReadableStream({
280
+ async start(controller) {
281
+ // Stream data in chunks
282
+ for (const chunk of largeData) {
283
+ controller.enqueue(JSON.stringify(chunk));
284
+ }
285
+ controller.close();
286
+ },
287
+ });
288
+
289
+ return new Response(stream, {
290
+ headers: {
291
+ 'Content-Type': 'application/json',
292
+ },
293
+ });
294
+ }
295
+ ```
296
+
297
+ ### 分页
298
+
299
+ 始终对大型数据集进行分页:
300
+
301
+ ```typescript
302
+ @GET('/users')
303
+ public async getUsers(
304
+ @Query('page') page: number = 1,
305
+ @Query('limit') limit: number = 20,
306
+ ) {
307
+ const offset = (page - 1) * limit;
308
+ const users = await this.userService.findAll({ limit, offset });
309
+ return {
310
+ data: users,
311
+ pagination: {
312
+ page,
313
+ limit,
314
+ total: await this.userService.count(),
315
+ },
316
+ };
317
+ }
318
+ ```
319
+
320
+ ## 内存管理
321
+
322
+ ### 避免内存泄漏
323
+
324
+ 正确清理资源:
325
+
326
+ ```typescript
327
+ // Clean up in afterEach/afterAll
328
+ afterEach(async () => {
329
+ await app.stop();
330
+ ModuleRegistry.getInstance().clear();
331
+ RouteRegistry.getInstance().clear();
332
+ });
333
+
334
+ // Clear intervals/timeouts
335
+ const intervalId = setInterval(() => {}, 1000);
336
+ // ... later
337
+ clearInterval(intervalId);
338
+ ```
339
+
340
+ ### 使用 TTL 缓存
341
+
342
+ 为缓存条目设置适当的 TTL:
343
+
344
+ ```typescript
345
+ CacheModule.forRoot({
346
+ defaultTtl: 3600000, // 1 hour
347
+ });
348
+
349
+ // Override for specific entries
350
+ await cache.set("key", "value", 60000); // 1 minute
351
+ ```
352
+
353
+ ### 监控内存使用
354
+
355
+ 在生产环境中监控内存使用:
356
+
357
+ ```typescript
358
+ import { performance } from "node:perf_hooks";
359
+
360
+ setInterval(() => {
361
+ const usage = process.memoryUsage();
362
+ console.log({
363
+ heapUsed: `${Math.round(usage.heapUsed / 1024 / 1024)}MB`,
364
+ heapTotal: `${Math.round(usage.heapTotal / 1024 / 1024)}MB`,
365
+ rss: `${Math.round(usage.rss / 1024 / 1024)}MB`,
366
+ });
367
+ }, 60000); // Every minute
368
+ ```
369
+
370
+ ## 并发处理
371
+
372
+ ### 使用 Async/Await
373
+
374
+ 对 I/O 操作始终使用 async/await:
375
+
376
+ ```typescript
377
+ // ✅ Correct: Async
378
+ @GET('/users')
379
+ public async getUsers() {
380
+ return await this.userService.findAll();
381
+ }
382
+
383
+ // ❌ Wrong: Synchronous
384
+ @GET('/users')
385
+ public getUsers() {
386
+ return this.userService.findAllSync(); // Blocks event loop
387
+ }
388
+ ```
389
+
390
+ ### 并行操作
391
+
392
+ 对并行操作使用 `Promise.all()`:
393
+
394
+ ```typescript
395
+ // ✅ Parallel execution
396
+ const [user, profile, settings] = await Promise.all([
397
+ this.userService.find(id),
398
+ this.profileService.find(id),
399
+ this.settingsService.find(id),
400
+ ]);
401
+
402
+ // ❌ Sequential execution
403
+ const user = await this.userService.find(id);
404
+ const profile = await this.profileService.find(id);
405
+ const settings = await this.settingsService.find(id);
406
+ ```
407
+
408
+ ### Worker 线程
409
+
410
+ 对 CPU 密集型任务使用 worker 线程:
411
+
412
+ ```typescript
413
+ import { Worker } from 'worker_threads';
414
+
415
+ @POST('/process')
416
+ public async processData(@Body() data: any) {
417
+ return await new Promise((resolve, reject) => {
418
+ const worker = new Worker('./worker.js', {
419
+ workerData: data,
420
+ });
421
+ worker.on('message', resolve);
422
+ worker.on('error', reject);
423
+ });
424
+ }
425
+ ```
426
+
427
+ ## 最佳实践总结
428
+
429
+ 1. **缓存频繁访问的数据** - 使用 CacheModule 并设置适当的 TTL
430
+ 2. **优化数据库查询** - 使用索引、分页、批量操作
431
+ 3. **最小化中间件** - 只使用必要的中间件
432
+ 4. **使用 async/await** - 永远不要阻塞事件循环
433
+ 5. **启用压缩** - 使用 gzip/brotli 压缩
434
+ 6. **监控性能** - 使用 PerformanceHarness 和监控工具
435
+ 7. **清理资源** - 在测试和关闭处理程序中正确清理
436
+ 8. **使用连接池** - 配置适当的池大小
437
+ 9. **流式传输大型响应** - 对大型数据使用流式传输
438
+ 10. **并行化操作** - 对独立操作使用 Promise.all()
439
+
440
+ ## 性能检查清单
441
+
442
+ - [ ] 为频繁访问的数据启用了缓存
443
+ - [ ] 配置了数据库连接池
444
+ - [ ] 优化了数据库查询(索引、分页)
445
+ - [ ] 最小化并优化了中间件
446
+ - [ ] 启用了压缩(gzip/brotli)
447
+ - [ ] 所有 I/O 操作都使用了 async/await
448
+ - [ ] 防止了内存泄漏(清理处理程序)
449
+ - [ ] 启用了性能监控
450
+ - [ ] 大型响应已分页或流式传输
451
+ - [ ] 在可能的情况下使用了并行操作
@@ -0,0 +1,279 @@
1
+ # 故障排查指南
2
+
3
+ 本文档帮助你在使用 Bun Server Framework 时诊断和解决常见问题。
4
+
5
+ ## 目录
6
+
7
+ - [常见问题](#常见问题)
8
+ - [模块注册](#模块注册)
9
+ - [依赖注入](#依赖注入)
10
+ - [路由问题](#路由问题)
11
+ - [性能问题](#性能问题)
12
+ - [调试技巧](#调试技巧)
13
+
14
+ ## 常见问题
15
+
16
+ ### Provider 未找到
17
+
18
+ **错误**:`Provider not found for token: Symbol(...)`
19
+
20
+ **原因**:模块的 `forRoot()` 方法在 `Application`
21
+ 实例创建之前被调用,或者模块未通过 `app.registerModule()` 注册。
22
+
23
+ **解决方案**:
24
+
25
+ ```typescript
26
+ // ❌ Wrong: Module.forRoot() before Application
27
+ ConfigModule.forRoot({ defaultConfig: {} });
28
+ const app = new Application({ port: 3000 });
29
+
30
+ // ✅ Correct: Module.forRoot() before Application, then register
31
+ ConfigModule.forRoot({ defaultConfig: {} });
32
+ const app = new Application({ port: 3000 });
33
+ app.registerModule(ConfigModule);
34
+ ```
35
+
36
+ ### 模块元数据未清除
37
+
38
+ **错误**:测试失败,提示 "Module already registered" 或测试中出现意外行为。
39
+
40
+ **原因**:模块元数据在测试之间持续存在。
41
+
42
+ **解决方案**:在 `beforeEach` 中清除模块元数据:
43
+
44
+ ```typescript
45
+ import { MODULE_METADATA_KEY } from "../../src/di/module";
46
+
47
+ beforeEach(() => {
48
+ Reflect.deleteMetadata(MODULE_METADATA_KEY, YourModule);
49
+ });
50
+ ```
51
+
52
+ ### 端口已被使用
53
+
54
+ **错误**:`Error: listen EADDRINUSE: address already in use`
55
+
56
+ **原因**:另一个进程正在使用该端口,或者之前的测试未清理。
57
+
58
+ **解决方案**:在测试中使用 `getTestPort()` 工具:
59
+
60
+ ```typescript
61
+ import { getTestPort } from "../utils/test-port";
62
+
63
+ const port = getTestPort();
64
+ const app = new Application({ port });
65
+ ```
66
+
67
+ ## 模块注册
68
+
69
+ ### 模块顺序很重要
70
+
71
+ 模块应按依赖顺序注册。如果 `AppModule` 依赖于 `ConfigModule`,请先注册
72
+ `ConfigModule`:
73
+
74
+ ```typescript
75
+ ConfigModule.forRoot({ defaultConfig: {} });
76
+ CacheModule.forRoot({ defaultTtl: 60000 });
77
+ const app = new Application({ port: 3000 });
78
+ app.registerModule(ConfigModule);
79
+ app.registerModule(CacheModule);
80
+ app.registerModule(AppModule);
81
+ ```
82
+
83
+ ### 循环依赖
84
+
85
+ **错误**:检测到循环依赖或模块初始化期间出现无限循环。
86
+
87
+ **解决方案**:重构模块以移除循环依赖,或使用延迟加载:
88
+
89
+ ```typescript
90
+ // Use dynamic import for circular dependencies
91
+ const { SomeService } = await import("./some-service");
92
+ ```
93
+
94
+ ## 依赖注入
95
+
96
+ ### 服务不可注入
97
+
98
+ **错误**:`Cannot resolve dependency` 或找不到服务。
99
+
100
+ **解决方案**:确保服务使用 `@Injectable()` 装饰器:
101
+
102
+ ```typescript
103
+ @Injectable()
104
+ class UserService {
105
+ // ...
106
+ }
107
+ ```
108
+
109
+ ### Token 未找到
110
+
111
+ **错误**:`Provider not found for token`
112
+
113
+ **解决方案**:确保 token 从模块中导出且模块已注册:
114
+
115
+ ```typescript
116
+ // In module
117
+ @Module({
118
+ providers: [
119
+ { provide: MY_TOKEN, useValue: myService },
120
+ ],
121
+ exports: [MY_TOKEN],
122
+ })
123
+ class MyModule {}
124
+ ```
125
+
126
+ ## 路由问题
127
+
128
+ ### 路由未找到 (404)
129
+
130
+ **可能原因**:
131
+
132
+ 1. 控制器未注册
133
+ 2. 路由路径不匹配
134
+ 3. HTTP 方法不匹配
135
+
136
+ **解决方案**:
137
+
138
+ ```typescript
139
+ // Ensure controller is registered
140
+ @Module({
141
+ controllers: [UserController],
142
+ })
143
+ class AppModule {}
144
+
145
+ // Check route path
146
+ @Controller("/api/users")
147
+ class UserController {
148
+ @GET("/:id") // Matches /api/users/:id
149
+ public getUser(@Param("id") id: string) {}
150
+ }
151
+ ```
152
+
153
+ ### 参数未绑定
154
+
155
+ **错误**:`@Param('id')` 返回 `undefined`
156
+
157
+ **解决方案**:确保参数名称与路由参数匹配:
158
+
159
+ ```typescript
160
+ // Route: /api/users/:id
161
+ @GET('/:id')
162
+ public getUser(@Param('id') id: string) { // ✅ Correct
163
+ // ...
164
+ }
165
+
166
+ @GET('/:id')
167
+ public getUser(@Param('userId') userId: string) { // ❌ Wrong
168
+ // ...
169
+ }
170
+ ```
171
+
172
+ ## 性能问题
173
+
174
+ ### 请求处理缓慢
175
+
176
+ **可能原因**:
177
+
178
+ 1. 请求处理程序中的重计算
179
+ 2. 同步阻塞操作
180
+ 3. 大型响应负载
181
+
182
+ **解决方案**:
183
+
184
+ ```typescript
185
+ // Use async/await for I/O operations
186
+ @GET('/users')
187
+ public async getUsers() {
188
+ return await this.userService.findAll(); // ✅ Async
189
+ }
190
+
191
+ // Avoid blocking operations
192
+ @GET('/users')
193
+ public getUsers() {
194
+ return this.userService.findAllSync(); // ❌ Blocking
195
+ }
196
+ ```
197
+
198
+ ### 内存泄漏
199
+
200
+ **症状**:内存使用量随时间增加。
201
+
202
+ **可能原因**:
203
+
204
+ 1. 事件监听器未清理
205
+ 2. 定时器/间隔未清除
206
+ 3. 缓存未过期
207
+
208
+ **解决方案**:
209
+
210
+ ```typescript
211
+ // Clean up in afterEach/afterAll
212
+ afterEach(async () => {
213
+ await app.stop();
214
+ ModuleRegistry.getInstance().clear();
215
+ });
216
+
217
+ // Use TTL for cache
218
+ CacheModule.forRoot({
219
+ defaultTtl: 3600000, // 1 hour
220
+ });
221
+ ```
222
+
223
+ ## 调试技巧
224
+
225
+ ### 启用调试日志
226
+
227
+ 将日志级别设置为 `DEBUG`:
228
+
229
+ ```typescript
230
+ LoggerModule.forRoot({
231
+ logger: {
232
+ level: LogLevel.DEBUG,
233
+ },
234
+ });
235
+ ```
236
+
237
+ ### 检查模块元数据
238
+
239
+ 检查模块元数据:
240
+
241
+ ```typescript
242
+ import { MODULE_METADATA_KEY } from "../../src/di/module";
243
+
244
+ const metadata = Reflect.getMetadata(MODULE_METADATA_KEY, YourModule);
245
+ console.log("Module metadata:", metadata);
246
+ ```
247
+
248
+ ### 验证容器状态
249
+
250
+ 检查容器中注册的内容:
251
+
252
+ ```typescript
253
+ const container = app.getContainer();
254
+ // Inspect container internals (implementation-dependent)
255
+ ```
256
+
257
+ ### 使用测试工具
258
+
259
+ 使用测试工具进行一致的测试设置:
260
+
261
+ ```typescript
262
+ import { getTestPort } from "../utils/test-port";
263
+
264
+ const port = getTestPort();
265
+ const app = new Application({ port });
266
+ ```
267
+
268
+ ## 获取帮助
269
+
270
+ 如果仍然遇到问题:
271
+
272
+ 1. 查看 [API 文档](./api.md)
273
+ 2. 查看 [示例](../examples/)
274
+ 3. 搜索现有的 [GitHub Issues](https://github.com/your-repo/issues)
275
+ 4. 创建新 issue,包含:
276
+ - Bun 版本
277
+ - 框架版本
278
+ - 最小复现代码
279
+ - 错误消息和堆栈跟踪