自己造轮子是一件苦差事。 现在,您可以专注于业务开发,仅需集成 ⭐️Furion⭐️ 即可。
Skip to main content

25. 辅助角色服务

25.1 关于辅助角色服务

.NET Core 3.0 新增了 Worker Service 的新项目模板,可以编写长时间运行的后台服务,并且能轻松的部署成 Windows服务Linux 守护程序

25.2 如何创建 Worker Service

通过 Visual Studio 2019 提供的 Worker Service 可直接创建。如图:

25.3 创建 Worker

当我们创建好 Worker Service 项目时,已经自带了一个 Worker 类并继承自 BackgroundService 基类。

Worker 正是我们辅助角色的主要工作类,在这里我们编写我们所有的业务逻辑。通常 Worker 默认格式为:

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace FurionWorkers
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;

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

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

当我们创建了 Worker 类之后,需要在 Program.cs 中进行注册,如:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace FurionWorkers
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
});
}
}
小知识

如果使用了 Furion 包后可实现自动注册。

25.3.1 多个 Worker

Worker Service 是支持定义多个 Worker 进行协调工作的,每个 Worker 是完全独立的工作环境,但可共享同一主进程信息。

25.3.2 生命周期

Worker ServiceWorker 提供了三个执行方法,分别代表三个生命周期:

  • StartAsync:负责启动 Worker Service,如果调用 StartAsync 方法的线程被一直阻塞了,那么 Worker Service 的启动就一直完成不了
  • ExecuteAsyncWorker Service 真正实现业务逻辑的地方,这里不能调用阻塞代码!!!
  • StopAsync:负责结束 Worker Service,如果调用 StopAsync 方法的线程被一直阻塞了,那么 Worker Service 的结束就一直完成不了
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace FurionWorkers
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;

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

// 启动
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.Delay(1000, stoppingToken);
}
}

// 停止
public override Task StopAsync(CancellationToken cancellationToken)
{
return base.StopAsync(cancellationToken);
}
}
}

25.4 集成 Furion

Worker Service 集成 Furion 非常方便,只需要安装 Furion 的包即可,并在 Program.cs 中调用 .Inject() 方法,如:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace FurionWorkers
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.Inject()
.ConfigureServices((hostContext, services) =>
{
// 以下代码可不用编写,Furion 已实现自动注册 Worker;
// services.AddHostedService<Worker>();
});
}
}

默认情况下,Inject() 方法注册了 日志、缓存、依赖注入、加载配置、自定义 Startup 功能。

小知识

集成 Furion 后会自动扫描 Worker 类并实现自动注册。

25.5 注册服务

Worker Service 注册服务和 Web 略有不同,Web 主要在 Starup.cs 类中注册,Worker ServiceProgram.cs 启动类的 ConfigureServices 方法中注册,如:

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace FurionWorkers
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.Inject()
.ConfigureServices((hostContext, services) =>
{
// 注册数据库服务
services.AddDatabaseAccessor(options =>
{
options.AddDb<DefaultDbContext>();
});

// 注册远程请求
services.AddRemoteRequest();

// 等等其他服务注册
});
}
}

25.6 实现定时任务

Furion 框架同时也为 Worker Service 提供了定时任务的支持。

26.6.1 间隔执行方式

using Furion.TaskScheduler;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace WorkerService1
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;

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

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
// 间隔执行任务
await SpareTime.DoAsync(1000, () =>
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
}, stoppingToken);
}
}
}
}

26.6.2 Cron 表达式格式化方式,

using Furion.TaskScheduler;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace WorkerService1
{
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;

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

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
// 执行 Cron 表达式任务
await SpareTime.DoAsync("*/5 * * * * *", () =>
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
}, stoppingToken, CronFormat.IncludeSeconds);
}
}
}
}

25.7 依赖注入使用

Worker Service 只为 Worker 提供了单例作用域的服务注入,如果需要注入瞬时或作用域对象,需手动创建作用域,如:

public class Worker : BackgroundService
{
// 日志对象
private readonly ILogger<JobService> _logger;

// 服务工厂
private readonly IServiceScopeFactory _scopeFactory;
public Worker(ILogger<Worker> logger
, IServiceScopeFactory scopeFactory)
{
_logger = logger;
_scopeFactory = scopeFactory;
}

protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
using (var scope = _scopeFactory.CreateScope())
{
var services = scope.ServiceProvider;

// 获取数据库上下文
var dbContext = Db.GetDbContext(services);

// 获取仓储
var respository = Db.GetRepository<Person>(services);

// 解析其他服务
var otherService = services.GetService<XXX>();
}
}

return Task.CompletedTask;
}
}

25.8 部署到操作系统

Worker Service 支持部署到 Windows Service 中 或 Linux 守护进程中

25.8.1 部署到 Windows Service

  • 第一步

安装 Microsoft.Extensions.Hosting.WindowsServices 拓展包

  • 第二步

Program.cs 中添加 .UseWindowsService()

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace FurionWorkers
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseWindowsService()
.Inject()
.ConfigureServices((hostContext, services) =>
{
// 以下代码可不用编写,Furion 已实现自动注册 Worker;
// services.AddHostedService<Worker>();
});
}
}
  • 第三步

发布 Worker Service,可通过 dotnet publish -c Release -o C:\FurionWorker 命令发布或通过 Visual Studio 2019 发布。

  • 第四步

通过 sc.exe 工具来管理并创建 Windows 服务,通过 管理员模式 并打开控制台,输入:

sc.exe create FurionWorkerServices binPath= C:\FurionWorker\FurionWorker.exe

注意=后面要有一个空格
创建成功后可通过 sc.exe query FurionWorkerServices 查看服务状态。

  • 第五步

启动服务:sc.exe start FurionWorkerServices,启动之后就可以在 Windows 服务工具中查看了。

停止服务:sc.exe stop NETCoreDemoWorkerService

删除服务:sc.exe delete NETCoreDemoWorkerService

特别提醒

以上所有 sc.exe 命令必须在 管理员 模式下进行。 sc.exe delete NETCoreDemoWorkerService, 执行删除时候, 把Windows 服务工具关闭, 否则, 电脑重启后才会显示删除;

25.8.2 部署到 Linux 守护程序

  • 第一步

安装 Microsoft.Extensions.Hosting.Systemd 拓展包

  • 第二步

Program.cs 中添加 .UseSystemd()

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

namespace FurionWorkers
{
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSystemd()
.Inject()
.ConfigureServices((hostContext, services) =>
{
// 以下代码可不用编写,Furion 已实现自动注册 Worker;
// services.AddHostedService<Worker>();
});
}
}

部署到 Linux 守护进程 就是这么简单。

25.9 反馈与建议

与我们交流

给 Furion 提 Issue