# 二十、进程服务（Daemon）

## 关于进程服务

进程服务使你能够创建在它们自己的 操作系统（如Windows/Linux） 会话中可长时间运行的可执行应用程序。

这些服务可以在计算机启动时自动启动，可以暂停和重新启动而且不显示任何用户界面。

这种服务非常适合在服务器上使用，或任何时候，为了不影响在同一台计算机上工作的其他用户，需要长时间运行功能时使用。还可以在不同于登录用户的特定用户帐户或默认计算机帐户的安全上下文中运行服务。

## 有几种类型

* Windows 服务
* Linux 守护进程

## 如何使用

在 Hoa Framework 中已经集成了非常方便强大的进程服务创建方式，只需要简简单单几行代码，即可创建进程服务。

### 第一步

在 `Hoa.Worker.Projects` 项目层中的 `Workers` 目录中新建 `ProjectWorker.cs` 类，**并继承 `BackgroundService`父类。**&#x5982;基础模板：

```csharp
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Hoa.Worker.Projects.Workers
{
    public class ProjectWorker : BackgroundService
    {
        // 可以使用依赖注入服务，
        // 如配置文件读取、数据库操作、上下文操作、仓储等等
        private readonly IConfiguration _configuration;
        private readonly ILogger<ProjectWorker> _logger;
        private readonly HoaDbContext _dbContext;
        private readonly IRepository<Test> _testRepository;

        public ProjectWorker(
            IConfiguration configuration
            , ILogger<ProjectWorker> logger
            , HoaDbContext dbContext
            , IRepository<Test> testRepository
            )
        {
            _configuration = configuration;
            _logger = logger;
            _dbContext = dbContext;
            _testRepository = testRepository;
        }

        // 服务启动时配置
        public override Task StartAsync(CancellationToken cancellationToken)
        {
            // 业务代码写在这里

            return base.StartAsync(cancellationToken);
        }

        // 服务正常运行执行功能，也就是我们的核心代码
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);

                // 业务代码写在这里！！！！！！！！

                await Task.CompletedTask;
            }
        }

        // 服务停止时配置
        public override Task StopAsync(CancellationToken cancellationToken)
        {
            // 业务代码写在这里

            return base.StopAsync(cancellationToken);
        }
    }
}
```

### 第二步

在 `Hoa.Worker.Projects` 项目层的 `WorkerStartup.cs` 注册你的工作服务，如：

```csharp
using Hoa.Worker.Projects.Workers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace Hoa.Worker.Projects
{
    public class WorkerStartup
    {
        internal static void ConfigureWorkers(HostBuilderContext hostContext, IServiceCollection services)
        {
            // 注册你的工作服务
            services.AddHostedService<ProjectWorker>();
            
            // 注册更多工作服务
        }
    }
}

```

### 第三步

以管理员的方式打开 `PowerShell`，并切换到 `Hoa.Worker.Projects` 绝对路径下，并执行 `create_system_services.ps1` 脚本，如：

![](https://3801536837-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M7kU_Hr8e2bVvygGB-6%2F-M9av87tGDNXWhgtH3A7%2F-M9azQ-gOAuSx1ifIA7w%2Fqidong.png?alt=media\&token=32afc614-398a-4bdd-b59f-3769022497f7)

#### 命令代码如下：

```bash
Windows PowerShell
版权所有 (C) Microsoft Corporation。保留所有权利。

尝试新的跨平台 PowerShell https://aka.ms/pscore6

PS C:\Windows\system32> pushd D:\MONK\WORKPLACE\HOA\aspnetcore\Hoa.Workers\Hoa.Worker.Projects
PS D:\MONK\WORKPLACE\HOA\aspnetcore\Hoa.Workers\Hoa.Worker.Projects> & "./create_system_services.ps1" -Name HoaWorkerService -Description "这是一个Hoa框架Windowsu服务"
Hoa 系统服务 生成器 v1.0.0
警告: 如需要生成 Linux 守护进程服务，只需要修改
警告: Program.cs 文件下的 .UseWindowsService() 修改为 .UseSystemd() 即可！
==============================
======正在打包Windows Services======
用于 .NET Core 的 Microsoft (R) 生成引擎版本 16.6.0-preview-20181-02+9f3e4e725
版权所有(C) Microsoft Corporation。保留所有权利。

  正在确定要还原的项目…
  已还原 D:\MONK\WORKPLACE\HOA\aspnetcore\Hoa.Workers\Hoa.Worker.Projects\Hoa.Worker.Projects.csproj (在 692 ms 中)。
  已还原 D:\MONK\WORKPLACE\HOA\aspnetcore\Hoa.Core\Hoa.Core.csproj (在 692 ms 中)。
  已还原 D:\MONK\WORKPLACE\HOA\aspnetcore\Hoa.EntityFrameworkCore\Hoa.EntityFrameworkCore.csproj (在 692 ms 中)。
  1 个项目(共 4 个)是最新的，无法还原。
  你正在使用 .NET Core 的预览版。请查看 https://aka.ms/dotnet-core-preview
  你正在使用 .NET Core 的预览版。请查看 https://aka.ms/dotnet-core-preview
  你正在使用 .NET Core 的预览版。请查看 https://aka.ms/dotnet-core-preview
  Hoa -> D:\MONK\WORKPLACE\HOA\aspnetcore\Hoa\bin\Release\netcoreapp3.1\Hoa.dll
  Hoa.Core -> D:\MONK\WORKPLACE\HOA\aspnetcore\Hoa.Core\bin\Release\netcoreapp3.1\Hoa.Core.dll
  Hoa.EntityFrameworkCore -> D:\MONK\WORKPLACE\HOA\aspnetcore\Hoa.EntityFrameworkCore\bin\Release\netcoreapp3.1\Hoa.EntityFrameworkCore.dll
  Hoa.Worker.Projects -> D:\MONK\WORKPLACE\HOA\aspnetcore\Hoa.Workers\Hoa.Worker.Projects\bin\Release\netcoreapp3.1\Hoa.Workers\Hoa.Worker.Projects.dll
  Hoa.Worker.Projects -> D:\MONK\WORKPLACE\HOA\aspnetcore\Hoa.Workers\Hoa.Worker.Projects\Install\
======打包成功======
======正在生成Windows 服务进程======
[SC] CreateService 成功
[SC] ChangeServiceConfig2 成功
======生成成功======
======查看服务运行情况======

SERVICE_NAME: HoaWorkerService
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 1  STOPPED
        WIN32_EXIT_CODE    : 1077  (0x435)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x0
警告: ======开始启动Windows服务======

SERVICE_NAME: HoaWorkerService
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 2  START_PENDING
                                (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x7d0
        PID                : 9996
        FLAGS              :
警告: ======启动成功
======查看服务运行情况======

SERVICE_NAME: HoaWorkerService
        TYPE               : 10  WIN32_OWN_PROCESS
        STATE              : 2  START_PENDING
                                (NOT_STOPPABLE, NOT_PAUSABLE, IGNORES_SHUTDOWN)
        WIN32_EXIT_CODE    : 0  (0x0)
        SERVICE_EXIT_CODE  : 0  (0x0)
        CHECKPOINT         : 0x0
        WAIT_HINT          : 0x7d0
PS D:\MONK\WORKPLACE\HOA\aspnetcore\Hoa.Workers\Hoa.Worker.Projects>
```

### 第四步

查看系统服务是否创建，通过 `Windows + R` 打开 `运行`，并输入 `services.msc`，打开系统服务界面：

![](https://3801536837-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M7kU_Hr8e2bVvygGB-6%2F-M9av87tGDNXWhgtH3A7%2F-M9b-XdKr4jmKu88dC5L%2Fbuz.png?alt=media\&token=e037ab9d-325f-4318-a62c-af2507a14e32)

大功告成！！！😁

## 脚本说明

Hoa Framework 针对系统进程服务创建了两个初始化脚本，可以非常方便的创建和管理进程服务：

* **create\_system\_services.ps1**：创建并生成系统服务
* **delete\_system\_services.ps1**：删除用户自定义的系统服务

### create\_system\_services.ps1 参数说明

用于创建进程服务，如Window服务，Linux守护进程。

#### 执行命令：

```bash
& "./create_system_services.ps1" -Name HoaWorkerService -Description "这是一个Hoa框架Windowsu服务"
```

**以管理员的方式打开 `PowerShell`，并切换到 `Hoa.Worker.Projects` 绝对路径下才能执行😥！**

#### **参数配置**

* `-Name`：设置服务运行名称，必填，字符串类型
* `-Description`：设置服务描述，必填，字符串类型
* `-PackagePath`：**设置生成服务的包发布文件，用于生成环境创建Windows服务，字符串类型，选填！！！！只有生成环境才需要！**

### delete\_system\_services.ps1 参数说明

用于删除进程服务

#### 执行命令

```bash
& "./delete_system_services.ps1" -Name HoaWorkerService
```

**以管理员的方式打开 `PowerShell`，并切换到 `Hoa.Worker.Projects` 绝对路径下才能执行😥！**

#### **参数配置**

* `-Name`：设置服务运行名称，必填，字符串类型

## 多个工作进程

在过去我们构建Windows服务的时候，通常是一个服务只有一个功能，在Hoa Framework框架中，创建了新的 `Worker Service`的概念，也就是一个服务可以有多个服务进程。

在需要在 `Workers` 目录中创建新的 Worker类即可，并继承 `BackgroundService`，同时在 `WorkerStartup.cs`中注册，如发送短信的：**SMSWorker**

```csharp
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace Hoa.Worker.Projects.Workers
{
    public class SMSWorker : BackgroundService
    {
        private readonly ILogger<ProjectWorker> _logger;

        public SMSWorker(
            ILogger<ProjectWorker> logger
            )
        {
            _logger = logger;
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);

                // 发送短信代码

                await Task.CompletedTask;
            }
        }

    }
}
```

注册工作服务

```csharp
using Hoa.Worker.Projects.Workers;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace Hoa.Worker.Projects
{
    public class WorkerStartup
    {
        internal static void ConfigureWorkers(HostBuilderContext hostContext, IServiceCollection services)
        {
            services.AddHostedService<ProjectWorker>();
            // 注册SMS服务
            services.AddHostedServiceSMSWorker>();
        }
    }
}

```

## 调试代码

Hoa Framework 采用特殊的方式创建 进程服务，方便大家可以在开发过程中进行调试、打断点等等。只需要将 `Hoa.Worker.Projects` 设置为启动项即可，即可和常规代码一样调试。

![](https://3801536837-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M7kU_Hr8e2bVvygGB-6%2F-M9b1je2jYFs_chntShg%2F-M9b2fauXPO9geCoZZYF%2Ftiaos.png?alt=media\&token=ec0fb48d-a73a-43be-8a80-2fa9aea45430)

## 部署到生产环境

执行 `create_system_services.ps1`完脚本后，将会在 `Hoa.Worker.Projects` 目录下生成  `install` 目录，只需要将 `install`目录和 `create_system_services.ps1`拷贝到 生产环境中中执行即可，如：

```bash
& "./create_system_services.ps1" -PackagePath "拷贝到服务器的install目录"  -Name HoaWorkerService -Description "这是一个Hoa框架Windowsu服务"
```

这时只需要指定 `-PackagePath` 目录即可。

### 查看生产环境日志

通过 **Windows + R** 打开 **运行**，并输入 **eventvwr**，打开事件查看器，并点击 `Windows 日志 - 应用程序`，然后在右边 `操作`中选择 `筛选当前日志` 即可

![](https://3801536837-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M7kU_Hr8e2bVvygGB-6%2F-M9b7lZ4SNh0FUwlEupS%2F-M9bDXnJJoJ5MkGAERMl%2Fbz.png?alt=media\&token=4ab311e2-d845-431e-a181-3b703dc68227)

## 部署到Linux服务器

默认情况下，`create_system_services` 会生成 **Windows 服务**，如需生成 **Linux 守护进程，只需要将 `Hoa.Worker.Projects.Program.cs`** 中的 `CreateHostBuilder` 方法修改即可：

```csharp
public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseServiceProviderFactory(new AutofacServiceProviderFactory())
                .ConfigureContainer<ContainerBuilder>(builder => ConfigureContainer(builder))
                // .UseWindowsService() // 注释这个代码
                .UseSystemd() // 添加这个代码，
                .ConfigureServices((hostContext, services) =>
                {
                    AppGlobal.Configuration = hostContext.Configuration;
                    ConfigureServices(hostContext, services);
                    WorkerStartup.ConfigureWorkers(hostContext, services);
                });
```

## 注意事项

在 `Hoa.Worker.Projects`中的 `Worker`里：

1. **所有的方法都应该采用异步执行，也就是 `async/await`，IO/数据库操作严禁使用同步操作！！！！😕**
2. 执行的代码中将每一块都应该**使用日志记录输入**，方便生成环境查看，如 `_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);`
3. **尽量不要引入第三方包，进程服务应该是简单、单一的功能操作！**

## 关于数据库增删改操作🤷‍♀️

由于 Windows Services 和 Linux 守护进程实现原理和 Hoa.Web.Core有点不太一样，导致数据库操作不能自动 `SaveChanges`，所以，**如果有增删改操作，必须手动调用 `SaveChanges/SaveChangesAsync`**，如：

```
await _testRepository.SaveChangesAsync();
// 或者
await _dbContext.SaveChagnesAsync();
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://monksoul.gitbook.io/hoa/xitongjingchengfuwu.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
