如何在 ASP.NET Core 中使用 Hangfire

本文演示如何使用 HangFire,这是一个流行的开源库,用于 .NET Core 应用程序中的后台处理。本文构建了一个简单的应用程序,该应用程序执行一些后台处理,以演示使用 PostgreSql 作为数据库来存储数据和 dotConnect for PostgreSQL 作为 PostgreSQL 的数据提供程序所涵盖的概念。

为了从数据库中存储和检索数据,我们将使用dotConnect for PostgreSQL,这是一个构建在 ADO.NET 之上的高性能增强型 PostgreSQL 数据提供程序,可以在连接和断开连接模式下工作。

您将需要以下工具来处理代码示例:Qqun:765665608 

dotConnect for PostgreSQL

我们将构造什么?

在本文中,我们将构建一个简单的应用程序,该应用程序将演示如何在 ASP.NET 6 中自定义 HTTP 400 和 404 错误响应。以下是我们在本文中将遵循的步骤来完成此操作:

  1. 了解 ASP.NET Core 中的 Hangfire
  2. 在 Visual Studio 2022 中创建 ASP.NET 6 Core Web API 项目
  3. 将 Devart.Data.PostgreSql NuGet 包添加到 API 项目
  4. 创建一个简单的 WebAPI 控制器
  5. 在控制器内创建方法来检索资源使用信息并将其保存在数据库表中
  6. 创建即发即忘和重复性工作
  7. 运行应用程序
什么是后台任务?

在当今的应用程序中,某些操作(例如发送电子邮件、创建报告或上传大文件)可能需要很长时间才能完成。如果您的应用程序在请求-响应周期内同步执行这些操作,则可能会导致超时和延迟。这就是后台任务可以提供帮助的地方。

后台任务是在后台执行操作的任务,这样应用程序的工作流程就不会被中断,最重要的是,当前正在执行的线程不会被阻塞。换句话说,后台任务允许您将耗时或非关键操作与应用程序的主要逻辑分开。

后台任务通常异步运行,用于从应用程序的实际请求响应周期中卸载耗时或资源密集型进程,从而允许它们在后台运行。通过将后台任务与即时用户交互分离,后台任务允许您发送电子邮件、修改数据或对警报进行排队,而无需立即用户响应。

您可以将后台作业配置为定期触发。这对于自动化正常流程(例如备份、同步和生成每日报告)非常有用。您可以专注于核心功能,并将某些后台任务卸载到其他组件,从而提高代码的可维护性。

什么是Hangfire?

Hangfire是一个开源库,用于处理.NET和ASP.NET Core应用程序中的后台任务。它提供了一种简单有效的方法,用于在正常的请求-响应周期之外执行长时间、耗时或重复的任务。

通过提供统一且易于使用的API,Hangfire简化了任务后台处理的实现。它无需编写复杂的线程代码或手动管理后台处理基础设施。由于其直观的 API 和丰富的功能,Hangfire 在管理 NET 应用程序中的辅助任务方面受到了开发人员的欢迎。

Hangfire的特点

以下是 Hangfire 的主要特点:

  • 后台作业:Hangfire 允许您定义需要在后台运行的作业或活动,而与用户的请求无关。这些活动可能包括需要中央处理单元 (CPU) 大量处理能力的操作,例如生成报告、发送电子邮件或可以从异步处理中受益的任何其他任务。
  • 持久存储:Hangfire 使用持久存储来存储有关已排队、计划和处理的任务的信息。这可以防止在应用程序重新启动或出现故障时丢失任务。Hangfire 支持各种存储选项,包括 SQL Server、PostgreSQL 和 Redis。
  • 作业调度:Hangfire 可以轻松地在特定时间或重复间隔安排任务。您可以安排任务运行一次或重复运行,或者使用类似 cron 的表达式定义复杂的计划。
  • 仪表板:Hangfire 提供基于 Web 的界面,允许您监控和管理辅助任务。仪表板提供任务状态、执行历史记录、处理统计数据的可见性以及手动启动或终止作业的能力。
  • 容错:Hangfire 通过自动重试失败的任务来确保容错。如果作业因异常而失败,Hangfire 可以配置为重试该作业指定的次数,并且重试之间有一定的时间间隔。
  • 分布式处理:Hangfire 通过允许应用程序跨多个服务器或工作进程进行扩展来促进分布式处理。这可以实现任务的并行处理并提高整体系统效率。
Hangfire 的组件

Hangfire 使用三个主要组件来管理作业。其中包括存储、客户端和服务器。存储组件负责存储作业信息。客户端组件用于创建后台作业并根据配置的存储提供程序将作业存储在数据存储中。服务器组件负责执行所有后台作业。

创建新的 ASP.NET 6 Core Web API 项目

在本部分中,我们将学习如何在 Visual Studio 2022 中创建新的 ASP.NET 6 Core Web API 项目。

现在,请按照下列步骤操作:

  1. 打开 Visual Studio 2022。
  2. 单击创建新项目
  3. 选择ASP.NET Core Web API,然后单击下一步。
  4. 指定项目名称以及该项目在系统中的存储位置。或者,选中“将解决方案和项目放在同一目录中”复选框。
  5. 单击“下一步”。
  6. “附加信息”窗口中,选择“.NET 6.0(长期支持)”作为项目版本。
  7. 禁用“配置 HTTPS”“启用 Docker 支持”选项(取消选中它们)。
  8. 由于我们在此示例中不会使用身份验证,因此将身份验证类型选择为None
  9. 由于我们在此示例中不会使用 Open API,因此请取消选中启用 OpenAPI 支持复选框。
  10. 由于我们在此示例中不会使用最小 API,因此请确保选中“使用控制器”(取消选中以使用最小 API)
  11. 不选中“不使用顶级语句”复选框。
  12. 单击“创建”完成该过程。

我们将在本文中使用该项目。

实现自动资源监视器

在此示例中,我们将研究如何在 ASP.NET Core 中使用 Hangfire 实现自动资源监视器。该应用程序以预定义的时间间隔在后台创建并运行重复任务,然后检索 CPU 和内存使用信息并将其存储在 PostgreSQL 数据库表中。

创建数据库

您可以使用 pgadmin 工具创建数据库。要使用此启动此工具创建数据库,请按照以下步骤操作:

  1. 启动 pgadmin 工具
  2. 展开服务器部分
  3. 选择数据库
  4. 右键单击并单击创建 -> 数据库...
  5. 指定数据库名称并将其他选项保留为默认值
  6. 单击“保存”完成该过程

现在按照下面给出的步骤在刚刚创建的数据库中创建一个表:

  1. 选择并展开您刚刚创建的数据库
  2. 选择模式 -> 表
  3. 右键单击“表”并选择“创建”->“表...”

下面给出表格脚本供您参考:

CREATE TABLE perfdata (
id serial PRIMARY KEY,
job_Id VARCHAR ( 255 ) NOT NULL,
cpu_usage VARCHAR ( 255 ) NOT NULL,
memory_usage VARCHAR ( 255 ) NOT NULL
);

我们将在本文的后续部分中使用此表来演示如何使用 dotConnect for PostgreSQL 在 ASP.NET Core 中进行集成测试。

将 NuGet 包安装到 API 项目中

接下来,您应该将所需的 NuGet 包安装到您的项目中。您可以从 Visual Studio 内的 NuGet 包管理器工具安装它们,也可以使用以下命令从 NuGet 包管理器控制台安装它们:

PM> Install-Package Devart.Data.PostgreSql
PM> Install-Package Hangfire
PM> Install-Package Hangfire.MemoryStorage

dotConnect for PostgreSQL 是基于 ADO.NET 技术构建的 PostgreSQL 高性能数据提供程序,为构建基于 PostgreSQL 的数据库应用程序提供了全面的解决方案。

在 ASP.NET Core 中配置 Hangfire

将 Hangfire 安装到您的项目中后,下一步就是对其进行配置。您可以使用以下代码将 Hangfire 服务添加到服务集合中:

builder.Services.AddHangfire(c => c.UseMemoryStorage());
builder.Services.AddHangfireServer();

您可以在仪表板中查看与正在运行或已完成的作业相关的实时数据。您可以通过下面给出的代码片段来开启此功能:

app.UseHangfireDashboard();
在 Hangfire 创建作业

在本节中,我们将研究如何在 Hangfire 中创建作业以在后台执行处理,同时我们的应用程序继续响应。

Hangfire 为以下作业类型提供支持:

  • 即发即忘作业:这些作业仅执行一次
  • 延迟作业:这些作业在经过特定时间间隔后执行
  • 连续作业:这些作业在父作业执行完成后立即执行
  • 周期性作业:这些作业根据计划定期执行
创建一劳永逸的作业

您可以使用BackgroundJob.Enqueue 方法在Hangfire 中创建后台作业。即发即忘作业是指仅被触发或执行一次的作业。创建一个名为 CustomJobController 的新 API 控制器,并在其中写入以下代码:

[Route("api/[controller]")]
[ApiController]
public class CustomJobController : ControllerBase
{
private readonly ILogger _logger;
private readonly IBackgroundJobClient _backgroundJobClient;
public CustomJobController(ILogger<CustomJobController> logger, IBackgroundJobClient backgroundJobClient)
{
_logger = logger;
_backgroundJobClient = backgroundJobClient;
}

[HttpGet]
public IActionResult Get()
{
var jobId = _backgroundJobClient.Enqueue(() => FireAndForgetJob(null));
Thread.Sleep(5000);
return Ok($"Job Id: {jobId} completed...");
}

public Task FireAndForgetJob(PerformContext context)
{
var jobId = context.BackgroundJob.Id;
_logger.LogInformation($"Executing Job Id: {jobId}...");
return Task.CompletedTask;
}
}

当您执行应用程序并点击 CustomJobController 的 HttpGet 端点时,您可以看到即发即弃作业执行一次,如图 1 所示:

创建延迟作业

以下代码片段展示了如何在 Hangfire 中创建延迟作业:

var jobId = BackgroundJob.Schedule(() => Console.WriteLine("This is an example of a delayed job"), TimeSpan.FromDays(1));
创建延续作业

在 Hangfire 中,连续作业是指父作业执行完成后立即运行的作业。您还可以在 Hangfire 中创建和管理延续任务或作业。以下代码片段显示了如何在 Hangfire 中创建延续任务:

var id = BackgroundJob.Enqueue(() => InitializeInputData());
BackgroundJob.ContinueWith(id, () => ValidateInputData());
创建重复作业

重复性作业是根据计划执行的作业。例如,您可以有一个每周每分钟、每小时或每小时执行一次的重复作业。要在 Hangfire 中处理重复作业,您应该使用 IRecurringJobManager 接口。实际上,您应该在 CustomJobController 类的构造函数中注入 IRecurringJobManager 接口类型的实例,如下面给出的代码清单所示:

[Route("api/[controller]")]
[ApiController]
public class CustomJobController : ControllerBase
{
private readonly ILogger _logger;
private readonly IRecurringJobManager _recurringJobManager;
public CustomJobController(ILogger<CustomJobController> logger,
IRecurringJobManager recurringJobManager)
{
_logger = logger;
_recurringJobManager = recurringJobManager;
}

[HttpGet]
public IActionResult Get()
{
var jobId = Guid.NewGuid().ToString();
_recurringJobManager.AddOrUpdate(jobId, () => CustomRecurringJob(jobId),
Cron.Minutely);
Thread.Sleep(5000);
return Ok($"Job Id: {jobId} completed...");
}

public Task CustomRecurringJob(string jobId)
{
_logger.LogInformation($"Executing Job Id: {jobId} at {DateTime.Now}");
return Task.CompletedTask;
}
}

当您执行应用程序并访问 HttpGet 端点时,您可以看到作业每分钟执行一次,如图 2 所示:

检索资源消耗信息

现在让我们在 Hangfire 中创建一个重复作业并使用它来检索 CPU 和内存使用数据。以下代码应该如何获取计算机上的资源使用信息:

private dynamic GetResourceUsageForProcess()
{
string currentProcessName = Process.GetCurrentProcess().ProcessName;
PerformanceCounter cpuCounter = new PerformanceCounter("Process",
"% Processor Time", currentProcessName, true);
PerformanceCounter memoryCounter = new PerformanceCounter("Process",
"Private Bytes", currentProcessName, true);
cpuCounter.NextValue();
memoryCounter.NextValue();
Task.Delay(500);
dynamic result = new ExpandoObject();
result.CPU = Math.Round(cpuCounter.NextValue() /
Environment.ProcessorCount, 2);
result.RAM = Math.Round(memoryCounter.NextValue() / 1024 / 1024, 2);
return result;
}
将资源消耗信息存储在数据库中

现在资源使用信息已可用,您应该将此信息存储在数据库中。

下面给出的 StoreResourceUsageData 方法显示了如何在 PostgreSql 数据库中保存 CPU 和内存使用数据。

public void StoreResourceUsageData(string job_id, string cpu_usage, string
memory_usage)
{
try
{
using (
PgSqlConnection pgSqlConnection = new PgSqlConnection
("User Id = postgres; Password = sa123#;" +
"host=localhost;database=demo; license key=Specify your license key here;"))
{
using (PgSqlCommand cmd = new PgSqlCommand())
{
cmd.CommandText = "INSERT INTO perfdata (job_id, cpu_usage, memory_usage) "
+ "VALUES (:job_id, :cpu_usage, :memory_usage)";

Thread.Sleep(250);
cmd.Connection = pgSqlConnection;
cmd.Parameters.AddWithValue("job_id", job_id);
cmd.Parameters.AddWithValue("cpu_usage", cpu_usage);
cmd.Parameters.AddWithValue("memory_usage", memory_usage);

if (pgSqlConnection.State != System.Data.ConnectionState.Open)
pgSqlConnection.Open();

var state = pgSqlConnection.State;
cmd.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
throw;
}
}
完整的源代码

下面给出CustomJobController的完整源码,供参考:

[Route("api/[controller]")]
[ApiController]
public class CustomJobController: ControllerBase
{
private readonly ILogger _logger;
private readonly IRecurringJobManager _recurringJobManager;
protected static PerformanceCounter cpuCounter;
protected static PerformanceCounter memoryCounter;
public CustomJobController(ILogger < CustomJobController > logger,
IRecurringJobManager recurringJobManager)
{
_logger = logger;
_recurringJobManager = recurringJobManager;
}
[HttpGet]
public IActionResult Get()
{
var jobId = Guid.NewGuid().ToString();
_recurringJobManager.AddOrUpdate(jobId, () => CustomRecurringJob(jobId),
Cron.Minutely);
Thread.Sleep(5000);
return Ok($"Job Id: {jobId} completed...");
}
public Task CustomRecurringJob(string job_id)
{
_logger.LogInformation($"Executing Job Id: {job_id} at {DateTime.Now}");
var resourceUsage = GetResourceUsageForProcess();
string cpu_usage = resourceUsage.CPU.ToString();
string memory_usage = resourceUsage.RAM.ToString();
StoreResourceUsageData(job_id, cpu_usage, memory_usage);
return Task.CompletedTask;
}
private dynamic GetResourceUsageForProcess()
{
string currentProcessName = Process.GetCurrentProcess().ProcessName;
PerformanceCounter cpuCounter = new PerformanceCounter
("Process", "% Processor Time",
currentProcessName, true);
PerformanceCounter memoryCounter = new PerformanceCounter
("Process", "Private Bytes",
currentProcessName, true);
cpuCounter.NextValue();
memoryCounter.NextValue();
Task.Delay(500);
dynamic result = new ExpandoObject();
result.CPU = Math.Round(cpuCounter.NextValue() /
Environment.ProcessorCount, 2);
result.RAM = Math.Round(memoryCounter.NextValue() / 1024 / 1024, 2);
return result;
}
public void StoreResourceUsageData(string job_id, string cpu_usage,
string memory_usage)
{
try
{
using(PgSqlConnection pgSqlConnection = new PgSqlConnection
("User Id = postgres; Password = sa123#;" +
"host=localhost;database=demo; license key=Your license key;"))
{
using(PgSqlCommand cmd = new PgSqlCommand())
{
cmd.CommandText = "INSERT INTO perfdata “ +
“(job_id, cpu_usage, memory_usage) " +
"VALUES (:job_id, :cpu_usage, :memory_usage)";
Thread.Sleep(250);
cmd.Connection = pgSqlConnection;
cmd.Parameters.AddWithValue("job_id", job_id);
cmd.Parameters.AddWithValue("cpu_usage", cpu_usage);
cmd.Parameters.AddWithValue("memory_usage", memory_usage);
if (pgSqlConnection.State != System.Data.ConnectionState.Open)
pgSqlConnection.Open();
var state = pgSqlConnection.State;
cmd.ExecuteNonQuery();
}
}
}
catch (Exception ex)
{
throw;
}
}
}

当您运行应用程序时,您可以看到我们之前创建的数据库表中存储的 CPU 和内存使用信息。

Hangfire 是一个优秀的库,用于将作业集成到 .NET 和 .NET Core 应用程序中。您可以使用它在特定时间、定期或基于 cron 表达式来安排您的作业。通过使用 Hangfire,您可以提高应用程序的响应能力。借助 Hangfire 和 ASP.NET Core,您可以在后台高效地完成耗时的任务,从而最大限度地提高应用程序的性能和用户体验。

猜你喜欢

转载自blog.csdn.net/m0_67129275/article/details/132898617