Add XCode skills for entity caching, ORM, and sharding ETL
|
---
name: hosted-services-lifecycle
description: >
设计轻é‡çº§åº”用主机,管ç†åŽå°æœåŠ¡ï¼ˆIHostedService)的å¯åЍã€åœæ¢é¡ºåºä¸Žç”Ÿå‘½å‘¨æœŸã€‚
涵盖宿主信å·å¤„ç†ï¼ˆCtrl+C/SIGTERM)ã€ä¼˜é›…åœæœºã€ä¾èµ–注入集æˆï¼Œä»¥åŠæœ€å¤§æ‰§è¡Œæ—¶é—´é™åˆ¶ã€‚
适用于控制å°åº”用ã€åŽå°æœåŠ¡ã€å¾®æœåŠ¡ã€å®ˆæŠ¤è¿›ç¨‹ç‰åœºæ™¯çš„主机设计与代ç 审查。
argument-hint: >
è¯´æ˜Žä½ çš„å®¿ä¸»åœºæ™¯ï¼šå¸¸é©»è¿›ç¨‹è¿˜æ˜¯é™æ—¶ä»»åŠ¡ï¼›æ˜¯å¦éœ€è¦å¤šæœåŠ¡å¹¶è¡Œï¼›
如何åšä¾èµ–注入(ObjectContainer 还是 MS DI);是å¦éœ€è¦å®šæ—¶åœæ¢æˆ–ä¼˜é›…åœæœºé’©å。
---
# åº”ç”¨ä¸»æœºç”Ÿå‘½å‘¨æœŸç®¡ç†æŠ€èƒ½
## 适用场景
- 控制å°åº”用需è¦åŒæ—¶è¿è¡Œå¤šä¸ªåŽå°æœåŠ¡ï¼ˆæ¶ˆæ¯æ¶ˆè´¹ã€å®šæ—¶ä»»åŠ¡ã€å¥åº·æ£€æŸ¥ç‰ï¼‰ï¼Œå¹¶åœ¨æ”¶åˆ° Ctrl+C æˆ–ç³»ç»Ÿä¿¡å·æ—¶ä¼˜é›…åœæœºã€‚
- å¾®æœåŠ¡/守护进程需è¦ç»Ÿä¸€ç®¡ç†å„组件的å¯åЍ/åœæ¢é¡ºåºï¼Œç¡®ä¿ä¾èµ–关系和资æºé‡Šæ”¾æ£ç¡®ã€‚
- 需è¦åœ¨ä¾èµ–注入容器åˆå§‹åŒ–完æˆåŽï¼Œç»Ÿä¸€æ‹‰èµ·å…¨éƒ¨å·²æ³¨å†Œçš„åŽå°æœåŠ¡ã€‚
- 定时测试或 CI 场景需è¦è¿è¡Œä¸€æ®µæ—¶é—´åŽè‡ªåŠ¨é€€å‡ºï¼ˆ`MaxTime` é™åˆ¶ï¼‰ã€‚
## æ ¸å¿ƒåŽŸåˆ™
1. **`IHostedService` 契约**:æ¯ä¸ªåŽå°æœåŠ¡ç‹¬ç«‹å®žçŽ° `StartAsync(CancellationToken)` å’Œ `StopAsync(CancellationToken)`ï¼›å¯åŠ¨æ—¶ä¸é˜»å¡žï¼ˆè€—时任务在åŽå° `Task` ä¸è¿è¡Œï¼‰ï¼›åœæ¢æ—¶ç‰å¾…当å‰å·¥ä½œå•元完æˆï¼Œä½†è¦éµå®ˆ `CancellationToken`。
2. **å•å‘生命周期**:`Host` 顺åºå¯åŠ¨æ‰€æœ‰æœåŠ¡ï¼Œé€†åºåœæ¢ï¼›ä¸è¦ç›´æŽ¥æŒæœ‰ `Host` 引用在æœåŠ¡å†…éƒ¨è°ƒç”¨ `Close`,应通过 `CancellationToken` å作退出。
3. **ä¾èµ–注入优先**:通过 `IObjectContainer.AddHostedService<T>()` 注册æœåŠ¡ï¼Œè®©å®¹å™¨è´Ÿè´£å®žä¾‹åŒ–å’Œä¾èµ–è§£æžï¼›é¿å…在 `Add<TService>()` 之剿‰‹å·¥ `new`。
4. **ä¼˜é›…åœæœºæ—¶é™**:`StopAsync` 应设置åˆç†è¶…时(比如 5 秒);超时åŽå¼ºåˆ¶é€€å‡ºï¼Œé˜²æ¢åº”ç”¨å¡æ»ï¼›`MaxTime` ç”¨äºŽæµ‹è¯•æˆ–é™æ—¶ä»»åŠ¡ï¼Œç”Ÿäº§çŽ¯å¢ƒé€šå¸¸ä¿æŒ `-1`。
5. **ä¿¡å·å¤„ç†ç»Ÿä¸€**:通过 `Host.RegisterExit` 注册清ç†å›žè°ƒï¼Œè€Œéžæ•£è½åœ¨å„å¤„ç›‘å¬ `AppDomain.ProcessExit` / `Console.CancelKeyPress`。
## 执行æ¥éª¤
### 一ã€å®šä¹‰åŽå°æœåŠ¡
```csharp
public class WorkerService : IHostedService
{
private CancellationTokenSource? _cts;
private Task? _task;
public Task StartAsync(CancellationToken cancellationToken)
{
_cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
_task = RunAsync(_cts.Token);
return Task.CompletedTask; // StartAsync ç«‹å³è¿”回,ä¸é˜»å¡ž
}
public async Task StopAsync(CancellationToken cancellationToken)
{
_cts?.Cancel();
if (_task != null)
await Task.WhenAny(_task, Task.Delay(5_000, cancellationToken));
}
private async Task RunAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
// 执行业务逻辑
await Task.Delay(1_000, stoppingToken);
}
}
}
```
### äºŒã€æ³¨å†ŒæœåС并å¯åŠ¨ä¸»æœº
```csharp
// æŽ¨èæ–¹å¼ï¼šé€šè¿‡ IoC 注册
var ioc = ObjectContainer.Current;
ioc.AddSingleton<IMessageQueue, RedisQueue>();
ioc.AddHostedService<WorkerService>();
ioc.AddHostedService<HealthCheckService>();
var host = new Host(ioc.BuildServiceProvider());
host.Run(); // 阻塞,直到 Ctrl+C / SIGTERM / Close()
```
- `AddHostedService<T>()` ç‰ä»·äºŽ `ioc.AddSingleton<IHostedService, T>()`,æœåŠ¡å®žä¾‹ç”±å®¹å™¨ç®¡ç†ã€‚
- è‹¥éœ€æ‰‹åŠ¨ä¼ å…¥å·²æž„å»ºçš„å®žä¾‹ï¼š`host.Add(myServiceInstance)`。
### 三ã€ä¿¡å·ä¸Žé€€å‡ºé’©å
```csharp
// 注册清ç†å›žè°ƒï¼ˆæŒä¹…注册,å¯å¤šæ¬¡è°ƒç”¨ï¼›ç”Ÿå‘½å‘¨æœŸä¸Žè¿›ç¨‹ä¸€è‡´ï¼‰
Host.RegisterExit(() =>
{
flushLogs();
releaseConnections();
});
```
- `RegisterExit` 内部使用 `AppDomain.ProcessExit` + `Console.CancelKeyPress`ï¼Œæ— éœ€é‡å¤æ³¨å†Œã€‚
### å››ã€é™æ—¶è¿è¡Œï¼ˆæµ‹è¯•/CI)
```csharp
var host = new Host(ioc.BuildServiceProvider());
host.MaxTime = 30_000; // 30 ç§’åŽè‡ªåŠ¨åœæ¢
await host.RunAsync();
```
### äº”ã€æ‰‹åŠ¨æŽ§åˆ¶å¯åœï¼ˆé›†æˆæµ‹è¯•)
```csharp
using var cts = new CancellationTokenSource();
await host.StartAsync(cts.Token);
// åšæ–言...
await Task.Delay(500);
await host.StopAsync(cts.Token);
```
### å…ã€ä¸Ž `Microsoft.Extensions.Hosting` 的对比定ä½
| 特性 | `NewLife.Model.Host` | `Microsoft.Extensions.Hosting` |
|------|---------------------|-------------------------------|
| æŽ¥å£ | `IHostedService`(相åŒè¯ä¹‰ï¼‰| `IHostedService` |
| 容器 | `ObjectContainer`(`IObjectContainer`)| `IServiceCollection` |
| ä¿¡å·å¤„ç† | `Host.RegisterExit` | 内置 |
| ä¾èµ– | ä»… `NewLife.Core` | 需 `Microsoft.Extensions.Hosting` |
> 两套 `IHostedService` 接å£ç¾å相åŒï¼Œåœ¨ .NET 5+ 下å¯äº’相兼容;容器层ä¸äº’通。
## é‡ç‚¹æ£€æŸ¥é¡¹
- [ ] `StartAsync` 是å¦ç«‹å³è¿”回?长任务是å¦åœ¨åŽå° `Task` ä¸è¿è¡Œï¼Œè€Œéžåœ¨ `StartAsync` 内 `await` 阻塞?
- [ ] `StopAsync` æ˜¯å¦æœ‰åˆç†è¶…时?是å¦åœ¨ `CancellationToken` 被触å‘åŽå°½å¿«é€€å‡ºï¼Ÿ
- [ ] æœåŠ¡å†…æ˜¯å¦æŒæœ‰ `Host` 引用并直接调用 `Close`(应改为通过 `CancellationToken` å作)?
- [ ] æ˜¯å¦æ•£è½å¤šå¤„ `Console.CancelKeyPress` / `AppDomain.ProcessExit`(应改用 `RegisterExit`)?
- [ ] æœåŠ¡çš„æž„é€ å‡½æ•°æ˜¯å¦æ³¨å…¥äº†è¿‡å¤šä¾èµ–,或ä¾èµ–了"还没å¯åŠ¨çš„æœåŠ¡"?
- [ ] 多æœåŠ¡ä¹‹é—´çš„å¯åœé¡ºåºæ˜¯å¦æœ‰ä¾èµ–?如有,应通过å¯åŠ¨é¡ºåºæŽ’åˆ—æˆ–å调令牌解决。
## è¾“å‡ºè¦æ±‚
- **æœåŠ¡ç±»**:实现 `IHostedService`ï¼›`StartAsync` ç«‹å³è¿”回并å¯åЍåŽå° `Task`ï¼›`StopAsync` å–æ¶ˆå¹¶ç‰å¾…。
- **å…¥å£**:`Main` 或 `Program.cs` ä¸ç”¨ `AddHostedService<T>()` 注册所有æœåŠ¡ï¼Œæž„å»º `Host` 并调用 `Run()`。
- **清ç†**:通过 `Host.RegisterExit` 统一注册清ç†é’©åï¼Œä¸æ•£è½å„处。
- **测试**:能通过 `StartAsync` + ç‰å¾… + `StopAsync` 独立测试æ¯ä¸ªæœåŠ¡ï¼Œæ— é¡»å¯åŠ¨å®Œæ•´ä¸»æœºã€‚
## å‚考资料
å‚考示例与模å¼è¯æ®è§ `references/newlife-host-patterns.md`。
|