后台任务和工作者

介绍

ASP.NET Boilerplate提供后台作业和工作程序,用于在应用程序的后台线程中执行某些任务。

后台工作

以排队且持久的方式,后台作业用于对在后台执行的某些任务进行排队。由于多种原因,您可能需要后台作业。这里有些例子:

关于工作持久性

有关 作业持久性的更多信息,请参见  后台作业存储部分。

创建后台作业

我们可以通过继承BackgroundJob <TArgs>类或直接实现IBackgroundJob <TArgs>接口来创建后台作业类。

这是最简单的后台工作:

public class TestJob : BackgroundJob<int>, ITransientDependency
{
    public override void Execute(int number)
    {
        Logger.Debug(number.ToString());
    }
}

后台作业定义了一个获取输入参数的Execute方法。参数类型定义为泛型类参数,如示例中所示。

必须通过依赖注入注册后台作业  实现ITransientDependency是最简单的方法。

让我们定义一个更实际的工作,在后台队列中发送电子邮件:

public class SimpleSendEmailJob : BackgroundJob<SimpleSendEmailJobArgs>, ITransientDependency
{
    private readonly IRepository<User, long> _userRepository;
    private readonly IEmailSender _emailSender;

    public SimpleSendEmailJob(IRepository<User, long> userRepository, IEmailSender emailSender)
    {
        _userRepository = userRepository;
        _emailSender = emailSender;
    }

    [UnitOfWork]
    public override void Execute(SimpleSendEmailJobArgs args)
    {
        var senderUser = _userRepository.Get(args.SenderUserId);
        var targetUser = _userRepository.Get(args.TargetUserId);

        _emailSender.Send(senderUser.EmailAddress, targetUser.EmailAddress, args.Subject, args.Body);
    }
}

我们  注入  用户  存储库  以获取用户电子邮件,
并注入电子邮件发件人(发送电子邮件的服务)并简单地发送电子邮件。SimpleSendEmailJobArgs是这里的作业参数,定义如下:

[Serializable]
public class SimpleSendEmailJobArgs
{
    public long SenderUserId { get; set; }

    public long TargetUserId { get; set; }

    public string Subject { get; set; }

    public string Body { get; set; }
}

作业参数应该是可序列化的,因为它被序列化并存储在数据库中。虽然ASP.NET Boilerplate的默认后台作业管理器使用JSON序列化(不需要[Serializable]属性),但最好定义[Serializable]属性,因为我们将来可能会切换到另一个作业管理器,我们可以使用它像.NET的内置二进制序列化。

保持您的参数简单(如  实体  或其他非可序列化的对象。如SimpleSendEmailJob示例中所示,我们只能存储实体的Id并从作业内的存储库中获取实体。

将新作业添加到队列

定义后台作业后,我们可以注入并使用IBackgroundJobManager将作业添加到队列中。请参阅上面定义的TestJob示例:

public class MyService
{
    private readonly IBackgroundJobManager _backgroundJobManager;

    public MyService(IBackgroundJobManager backgroundJobManager)
    {
        _backgroundJobManager = backgroundJobManager;
    }

    public void Test()
    {
        _backgroundJobManager.Enqueue<TestJob, int>(42);
    }
}

我们在入队时发送了42作为参数。IBackgroundJobManager将实例化并执行带有42作为参数的TestJob。

让我们为SimpleSendEmailJob添加一个新工作,如前所述:

[AbpAuthorize]
public class MyEmailAppService : ApplicationService, IMyEmailAppService
{
    private readonly IBackgroundJobManager _backgroundJobManager;

    public MyEmailAppService(IBackgroundJobManager backgroundJobManager)
    {
        _backgroundJobManager = backgroundJobManager;
    }

    public async Task SendEmail(SendEmailInput input)
    {
            await _backgroundJobManager.EnqueueAsync<SimpleSendEmailJob, SimpleSendEmailJobArgs>(
            new SimpleSendEmailJobArgs
            {
                Subject = input.Subject,
                Body = input.Body,
                SenderUserId = AbpSession.GetUserId(),
                TargetUserId = input.TargetUserId
            });
    }
}

Enqueue(或EnqueueAsync)方法具有其他参数,例如优先级和延迟。

默认后台作业管理器

默认情况下,IBackgroundJobManager由BackgroundJobManager实现。它可以被另一个后台作业提供程序替换(请参阅  hangfire集成)。有关默认BackgroundJobManager的一些信息:

背景工作店

默认的BackgroundJobManager需要一个数据存储来保存和获取作业。如果您没有实现IBackgroundJobStore,那么它使用InMemoryBackgroundJobStore,它不会在持久数据库中保存作业。您可以简单地实现它以在数据库中存储作业,或者您可以使用   已经实现它的Module Zero

如果您使用的是第三方作业管理器(如  Hangfire),则无需实施IBackgroundJobStore。

组态

您可以在 模块PreInitialize方法中使用Configuration.BackgroundJobs  来配置后台作业系统。

禁用作业执行

您可能希望禁用应用程序的后台作业执行:

public class MyProjectWebModule : AbpModule
{
    public override void PreInitialize()
    {
        Configuration.BackgroundJobs.IsJobExecutionEnabled = false;
    }

    //...
}

这很少需要。例如,如果您正在运行同一数据库(在Web场中)的多个应用程序实例。在这种情况下,每个应用程序将在同一个数据库中查询作业并执行它们。这导致多次执行相同的工作和其他问题。为了防止这种情况,您有两种选择:

异常处理

由于默认后台作业管理器应重新尝试失败的作业,因此它会处理(并记录)所有异常。如果您希望在发生异常时收到通知,您可以创建一个事件处理程序来处理AbpHandledExceptionData后台管理器使用BackgroundJobException异常对象触发此事件,该异常对象包装真实异常(获取实际异常的InnerException)。

Hangfire集成

后台作业管理器旨在由另一个后台作业管理器替换。请参阅  hangfire集成文档  以将其替换为  Hangfire

背景工人

后台工作者与后台工作不同。它们是在后台运行的应用程序中的简单独立线程。通常,它们会定期运行以执行某些任务。例子;

创建一个后台工作者

为了创建后台工作程序,我们实现了IBackgroundWorker接口。或者,我们可以根据需要从BackgroundWorkerBase或PeriodicBackgroundWorkerBase继承。

假设我们想让用户被动,如果他在过去30天内没有登录应用程序。看代码:

public class MakeInactiveUsersPassiveWorker : PeriodicBackgroundWorkerBase, ISingletonDependency
{
    private readonly IRepository<User, long> _userRepository;

    public MakeInactiveUsersPassiveWorker(AbpTimer timer, IRepository<User, long> userRepository)
        : base(timer)
    {
        _userRepository = userRepository;
        Timer.Period = 5000; //5 seconds (good for tests, but normally will be more)
    }

    [UnitOfWork]
    protected override void DoWork()
    {
        using (CurrentUnitOfWork.DisableFilter(AbpDataFilters.MayHaveTenant))
        {
            var oneMonthAgo = Clock.Now.Subtract(TimeSpan.FromDays(30));

            var inactiveUsers = _userRepository.GetAllList(u =>
                u.IsActive &&
                ((u.LastLoginTime < oneMonthAgo && u.LastLoginTime != null) || (u.CreationTime < oneMonthAgo && u.LastLoginTime == null))
                );

            foreach (var inactiveUser in inactiveUsers)
            {
                inactiveUser.IsActive = false;
                Logger.Info(inactiveUser + " made passive since he/she did not login in last 30 days.");
            }

            CurrentUnitOfWork.SaveChanges();
        }
    }
}

这个真正的代码直接在带有Module Zero的 ASP.NET Boilerplate中工作  

注册背景工人

创建后台工作程序后,将其添加到IBackgroundWorkerManager。最常见的地方是模块的PostInitialize方法:

public class MyProjectWebModule : AbpModule
{
    //...

    public override void PostInitialize()
    {
        var workManager = IocManager.Resolve<IBackgroundWorkerManager>();
        workManager.Add(IocManager.Resolve<MakeInactiveUsersPassiveWorker>());
    }
}

虽然我们通常在PostInitialize中添加工作者,但对此没有限制。您可以在任何地方注入IBackgroundWorkerManager并在运行时添加工作程序。当您的应用程序关闭时,IBackgroundWorkerManager将停止并释放所有已注册的工作程序。

背景工作者生活方式

后台工作人员通常被实现为单身人士,但对此没有限制。如果需要同一个工作类的多个实例,可以将其设置为瞬态并向IBackgroundWorkerManager添加多个实例。在这种情况下,您的工作人员可能是参数化的(假设您有一个LogCleaner类,但有两个LogCleaner工作器实例,他们会监视并清除不同的日志文件夹)。

高级调度

ASP.NET Boilerplate的后台工作系统很简单。除了如上所示的定期运行的工人之外,它没有计划系统。如果您需要更高级的计划功能,我们建议您查看  Quartz  或其他库。

使您的应用程序始终运行

后台作业和工作程序仅在您的应用程序运行时才有效。如果长时间未对Web应用程序执行任何请求,则ASP.NET应用程序将默认关闭。因此,如果您在Web应用程序中承载后台作业执行(这是默认行为),则应确保将Web应用程序配置为始终运行。否则,后台作业仅在您的应用程序正在使用时才起作用。

有一些技术可以实现这一目标。最简单的方法是从外部应用程序定期向Web应用程序发出请求。因此,您还可以检查Web应用程序是否已启动并正在运行。迟发型文档  解释了一些其他的方法来做到这一点。

nidie.com.cn - 用心与你沟通