Add XCode skills for entity caching, ORM, and sharding ETL
大石头 authored at 2026-04-02 18:30:07
8.90 KiB
NewLife.Skills
--- name: agent-service description: > 使用 NewLife.Agent 将 .NET 应用注册为跨平台系统服务(Windows Service、Linux systemd、macOS LaunchAgent), 涵盖 ServiceBase 生命周期钩子(StartWork/StopWork/DoLoop)、IHost 平台实现、 命令行管理(-install/-start/-stop/-uninstall)、自定义命令处理器、 看门狗/内存监控/自动重启,以及 ASP.NET Core UseAgentService 集成。 适用于后台服务、数据采集守护进程、定时任务服务的系统级部署。 argument-hint: > 说明你的服务场景:新建独立后台服务还是将 ASP.NET Core 应用注册为系统服务; 目标平台(Windows/Linux/macOS);是否需要自定义命令; 是否需要看门狗保护其他进程;是否需要内存/线程超限自动重启。 --- # 跨平台系统服务技能(NewLife.Agent) ## 适用场景 - 将控制台应用一行代码注册为 Windows Service 或 Linux systemd 服务,实现开机自启。 - 数据采集、定时任务、消息消费等长期运行后台服务的系统级部署与管理。 - 需要在服务内同时启动多个子任务(`TimerX`/`NetServer` 等),统一生命周期管理。 - 生产环境需要进程自我保护:内存超限自动重启、崩溃自动恢复、看门狗保护其他服务。 - ASP.NET Core 应用需要以系统服务方式运行(替代 `UseWindowsService`/`UseSystemd`)。 ## 核心原则 1. **继承 `ServiceBase`,覆写 `StartWork`/`StopWork`**:这是唯一的业务接入点;`Starting` 阶段初始化资源、启动后台任务;`Stopping` 阶段释放资源、等待任务完成;两个方法都**必须调用 `base.xxx(reason)`**,否则框架状态标志不正确。 2. **`Main(args)` 是统一入口**:一行代码 `new MyService().Main(args)` 处理全部逻辑——命令行参数解析(`-install`/`-run` 等)、系统服务注册、交互式菜单;无需多个 `Main` 函数。 3. **`-run` 参数用于控制台调试**:开发阶段用 `dotnet run -- -run` 在控制台中模拟服务运行(前台执行),无需安装;CI/CD 中通过 `-install` 注册到系统。 4. **平台由框架自动选择**:无需编写平台判断代码;`IHost` 的实现(`WindowsService`/`Systemd`/`OSXLaunch`/`DefaultHost`)由运行时环境自动决定,同一份代码跨平台运行。 5. **看门狗和内存监控通过 `Setting` 配置**:在 `Agent.config` 或代码中设置 `WatchDog`/`MaxMemory`/`AutoRestart` 属性,不需要修改 `DoLoop` 逻辑;框架在 `DoLoop` 内自动检查并执行动作。 6. **自定义命令继承 `BaseCommandHandler`**:在 `Process(args)` 中实现命令逻辑,设置 `Cmd`、`ShortcutKey`、`Description`,框架自动发现并挂入命令行和交互菜单。 ## 执行步骤 ### 一、创建最简系统服务 ```csharp using NewLife.Agent; // 1. 定义服务类 public class MyService : ServiceBase { public MyService() { ServiceName = "MyAwesomeService"; DisplayName = "我的后台服务"; Description = "数据采集与同步服务"; } // 服务启动时调用(初始化资源、启动后台任务) protected override void StartWork(String reason) { base.StartWork(reason); // 必须调用 // 启动定时采集任务 _timer = new TimerX(CollectDataAsync, null, 1_000, 30_000); WriteLog("服务启动,原因:{0}", reason); } // 服务停止时调用(释放资源) protected override void StopWork(String reason) { _timer?.Dispose(); WriteLog("服务停止,原因:{0}", reason); base.StopWork(reason); // 必须调用 } private TimerX _timer; private async Task CollectDataAsync(Object? state) { // 采集逻辑... await Task.Delay(100); } } // 2. Program.cs 入口(一行代码) static void Main(String[] args) => new MyService().Main(args); ``` ### 二、命令行管理 ```bash # 安装并自动启动系统服务 dotnet MyService.dll -install # 仅安装,不启动 dotnet MyService.dll -install -ns # 卸载服务 dotnet MyService.dll -uninstall # 或短命令 dotnet MyService.dll -u # 启动 / 停止 / 重启已安装的服务 dotnet MyService.dll -start dotnet MyService.dll -stop dotnet MyService.dll -restart # 查询服务状态 dotnet MyService.dll -status # 控制台前台运行(开发调试) dotnet MyService.dll -run ``` ### 三、配置看门狗和资源限制 ```csharp // 方式 1:在构造函数中设置(代码级配置,高优先级) public MyService() { ServiceName = "MyService"; // 内存超过 512MB 自动重启 // (通过 Setting 配置,见方式 2) } // 方式 2:通过 Agent.config 配置(推荐生产环境) // Agent.config 位于应用目录,首次运行自动生成 // 也可在 Setting.Current 中修改 var setting = Setting.Current; setting.MaxMemory = 512; // 最大内存(MB),超过自动重启 setting.MaxThread = 1000; // 最大线程数,超过告警 setting.MaxHandle = 5000; // 最大句柄数,超过告警 setting.AutoRestart = 360; // 运行满 N 分钟自动重启(0=禁用) setting.WatchDog = "OtherService"; // 监视其他服务名,停止则启动 setting.Save(); ``` ### 四、自定义命令与菜单 ```csharp // 添加自定义命令(例如:手动触发数据同步) public class SyncCommandHandler : BaseCommandHandler { private readonly MyService _service; public SyncCommandHandler(ServiceBase service) : base(service) { Cmd = "-sync"; // 命令行参数 Description = "立即执行数据同步"; ShortcutKey = 'S'; // 交互菜单快捷键(大小写不敏感) } public override void Process(String[] args) { XTrace.WriteLine("开始手动同步..."); // 执行同步逻辑 (_service as MyService)?.SyncNow(); } // 是否在交互菜单中显示 public override Boolean IsShowMenu() => true; } // 在服务中注册(框架通过反射自动发现同 Assembly 内的 BaseCommandHandler) // 无需手动注册,只要类在同一程序集中即自动生效 ``` ### 五、ASP.NET Core 集成 ```csharp // Program.cs var builder = WebApplication.CreateBuilder(args); // 替代 builder.Host.UseWindowsService() 或 UseSystemd() builder.Host.UseAgentService(options => { options.ServiceName = "MyWebService"; options.DisplayName = "我的 Web 服务"; options.Description = "ASP.NET Core 应用系统服务"; }); builder.Services.AddControllers(); var app = builder.Build(); app.MapControllers(); app.Run(); ``` ### 六、Linux(systemd)服务文件 ```bash # 安装后,框架自动在 /etc/systemd/system/ 生成 .service 文件 # 内容大致如下(框架自动生成,无需手动编写): # [Unit] # Description=我的后台服务 # After=network.target # # [Service] # Type=simple # ExecStart=/usr/bin/dotnet /opt/myservice/MyService.dll -run # Restart=on-failure # RestartSec=5 # # [Install] # WantedBy=multi-user.target # 安装命令 sudo dotnet MyService.dll -install # 验证 sudo systemctl status MyService sudo journalctl -u MyService -f ``` ## ServiceBase 生命周期钩子速查 | 方法 | 调用时机 | 常见用途 | |------|---------|---------| | `StartWork(reason)` | 服务启动后(平台服务线程中) | 初始化资源,启动 TimerX/NetServer 等 | | `DoLoop()` | 服务运行期间(阻塞循环中) | 一般不需重写;框架在此做资源监控、WatchDog | | `StopWork(reason)` | 服务停止前 | 取消任务,释放资源(Dispose),等待 I/O 完成 | ## 命令行参数速查 | 参数 | 功能 | |------|------| | `-install` | 安装并启动系统服务 | | `-uninstall` / `-u` | 卸载系统服务 | | `-start` | 启动已安装服务 | | `-stop` | 停止已安装服务 | | `-restart` | 重启已安装服务 | | `-status` | 查询服务状态 | | `-run` | 前台控制台运行(调试) | | `-watch` | 看门狗模式(监视并守护目标服务) | ## 常见错误与注意事项 - **`StartWork`/`StopWork` 忘记调用 `base.xxx(reason)`**:基类维护 `Running` 标志和日志,遗漏调用会导致状态不一致,`DoLoop` 可能不退出。 - **在 `StartWork` 中做长时间 I/O(如等待数据库连接)**:SCM(Windows 服务控制器)对启动时间有限制(默认 30 秒),超时会被强制终止;应在 `StartWork` 中仅启动后台任务,不等待完成。 - **`StopWork` 中释放资源后还持有引用**:`Dispose` 后将字段置 `null`,避免定时器回调在停止后仍然执行导致 `ObjectDisposedException`。 - **Linux 下用 `UseAutorun=true` 与 systemd 冲突**:`UseAutorun` 是 Windows 注册表自启方式;Linux 上应由 systemd 管理重启策略,不要混用。 - **多实例部署需不同 `ServiceName`**:同一程序部署多份时,每份实例的 `ServiceName` 必须唯一,否则系统服务注册冲突。