ABP官方文档翻译 7.1 后台Jobs和Workers

后台Jobs和Workers

  • 介绍
  • 后台Jobs
    • 关于Job持久化
    • 创建后台Job
    • 在队列中添加一个新Job
    • 默认的后台Job管理器
      • 后台Job存储
    • 配置
      • 禁用Job执行
    • 异常处理
    • Hangfire集成
  • 后台Workers
    • 创建后台Workers
    • 注册后台Workers
    • 后台Worker生命周期
    • 高级计划安排
  • 让你的应用一直运行

介绍

  ABP提供了后台jobs和workers,他们用于在应用的后台线程中执行一些任务。

后台Jobs

  后台Jobs用来排队一些需要在后台队列或持久化的方式执行一些任务。以下几种情况可以使用后台jobs:

  • 执行一些长时运行的任务而不需要用户等待。例如,用户按了一个‘report‘按钮开始一个长时运行的报表job。可以将这个job添加到队列,当执行完成的时候通过邮件发送给用户报表结果。
  • 创建一个重试且持久化的任务来保证一段代码的成功运行。例如:可以在后台job中发送邮件来克服临时错误并保证它最终被发送。这样,当发送邮件时,用户不必等待。

关于Job持久化

  参见后台Job存储了解更多关于job持久化的信息。

创建后台Job

  我们可以通过从BackgroundJob<TArgs>类或直接实现IBackgroundJob<TArgs>接口来创建一个后台job类。

  这是最简单的后台job:

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

  后台job定义了Excute方法,它有一个输入参数。参数类型为泛型类参数。

  后台job必须注册到依赖注入系统。实现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;
    }

    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);
    }
}

  我们注入了user仓储(获得用户电子邮件)和电子邮件sender(发送邮件的服务)简单的发送邮件。SimpleSendEmailJobArgs为job的参数,定义如下:

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

    public long TargetUserId { get; set; }

    public string Subject { get; set; }

    public string Body { get; set; }
}

  job参数应该是可以序列化的,因为它被序列化并存储在数据库中。ABP默认后台job管理器使用JSON序列化(它不需要[Serilizable]特性),但是最好定义[Serializable]特性,因为我们可能在将来切换到其他job管理器,它可能使用.NET的内置二进制序列化。

  保持参数简单(如DTOs),不要包含实体或其他非序列化的对象。如在SimpleSendEmailJob示例中所示,我们可以存储实体的Id并从实体的仓储中获得实体。

在队列中添加一个新Job

  定义后台job之后,我们可以注入并使用IBackgroundJobManager来添加job到队列。参见TestJob定义示例:

public class MyService
{
    private readonly IBackgroundJobManager _backgroundJobManager;

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

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

  当入队时,我们使用42作为参数。IBackgroundJobManager将实例化并执行TestJob,参数为42。

  让我们添加一个上面定义的新的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
            });
    }
}

  Enque(或EnqueueAsync)方法还有其他参数如prioritydelay

默认的后台Job管理器

  IBackgroundJobManager默认由BackgroundJobManager实现。它可以被其他后台job提供者(参见hangfire集成)取代。BackgroundJobManager的一些参考信息:

  • 它是一个单线程的FIFO的简单job队列。它使用IBackgroundJobStore来持久化jobs(参见下部分)。
  • 在job成功运行(不抛出任何异常但是会记录)或超时前会一直重试。默认的重试时间为2天。
  • 当成功执行后,它从仓储(数据库)中删除job。如果它超时了,就设置为abandoned并保留在数据库中。
  • 每次重试前它会递增时间。第一次尝试等待1分钟,第二次等待2分钟,第三次等待4分钟...
  • 它以固定间隔轮训jobs仓储。按优先级(升序)、重试次数(升序)排列查询jobs。

后台Job存储

  默认的BackgroundJobManager需要一个数据仓储来保存和获取jobs。如果你没实现IBackgroundJobStore那么它使用InMemoryBackgroundJobStore,它不在持久化的数据库中存储jobs。你可以简单的实现它来在数据库中存储jobs或者可以使用已经实现了它的module-zero

  如果你使用了第三方的job管理器(如Hangfire),就不需要实现IBackgroundJobStore了。

配置

  你可以在模块的PreInitialize方法中使用Configuration.BackgroundJobs来配置后台job系统。

禁用Job执行

  你可以禁用应用程序的后台job执行:

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

    //...
}

  几乎不需要这样做。但是,假如你的应用运行了多个实例并使用同样的数据库(在web farm中)。在这种情况下,每个应用将使用同样的数据库查询job并执行。这会导致同样job的多次执行及其他的问题。为了阻止这种情况,有两种选择:

  • 你可以只为应用程序的一个实例启用job执行。
  • 你可以为应用程序的所有实例禁用job执行,然后创建一个单独的执行后台jobs的应用程序(例如:windows service)。

异常处理

  因为默认的后台管理器会自动重试失败的jobs,它处理(并记录)所有的异常。如果当异常发生时你想要得到通知,可以创建一个事件处理者来处理AbpHandledExceptionData。后台管理器触发这个事件,它有一个BackgroundJobException异常对象参数,这个参数包装了真正的异常(获取InnerException获取真正的异常)。

Hangfire集成

  后台管理器设计为可以被其他后台job管理器代替。参见hangfire集成文档,使用Hangfire替换它。

后台Workers

  后台Workers与后台Jobs不同。他们为在后台运行的简单独立的线程。通常,他们定期执行一些任务。例如:

  • 后台worker可以定期的删除老的日志。
  • 后台worker可以定期的判定不活动的用户并发送邮件来通知用户。

创建后台Workers

  创建一个后台worker,我们需要实现IBackgroundWorker接口。作为另一个选择,我们可以基于我们的需求继承BackgroundWorkerBasePeriodicBackgroundWorkerBase

  假如我们想使用户失活,如果他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();
        }
    }
}

  这是一段直接在ABP moudle-zero中使用的真实代码。

  • 如果你从PeriodicBackgroundWokerBase(如在本例中)继承,需要实现DoWork方法来执行你的定期执行代码。
  • 如果你从BackgroundWorkerBase继承或直接实现IBackgroundWorker接口,你需要重写/实现Start,StopWaitToStop方法。Start和Stop方法需要为非阻塞的,WaitToStop方法需要等待worker完成它当前关键的job。

注册后台Workers

  创建后台worker后,我们需要把它添加到IBackgroundWorkerManger。最常见的地方为模块的PostInitialize方法:

public class MyProjectWebModule : AbpModule
{
    //...

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

  我们通常在PostInitialize方法中添加worker,但并不限制。你可以在任何地方注入IBackgroundWorkerManager并添加worker。当应用程序关闭时,IBackgroundWorkerManager将停止并释放所有注册的worker。

后台Worker生命周期

  后台workder一般实现为单例模式。但是并没有限制。如果你需要同个worker类的多个实例,你可以使它为瞬时的并添加多个实例到IBackgroundWorkerManager。在这种情况下,你的worker将为参数化的(也就是说你有一个单独的LogCleaner类但是两个LogCleaner worker实例他们监视并清理不同的日志文件夹)。

高级计划安排

  ABP后台worker系统比较简单。除了上面展示的定期执行worker,它没有计划安排系统。如果你需要更多计划安排的特征,我们建议你使用Quartz或其他类库。

让你的应用一直运行

  后台jobs和workers只有当你应用程序运行时才会工作。如果长时间web应用没有请求执行,ABP应用默认会关闭。所以,如果你在web应用中(这是默认行为)寄宿后台job的执行,你需要保证你的web应用配置为一直执行。否则,后台job只有应用在使用时才能工作。

  有一些技术可以实现。最简单的方式是使用外部应用定期请求你的web应用。这样,你可以检查文本应用是否已启动并运行。Hangfire文档解释了一些其他的方式。

返回主目录

时间: 2024-08-08 17:54:41

ABP官方文档翻译 7.1 后台Jobs和Workers的相关文章

ABP官方文档翻译 10.1 ABP Nuget包

ABP Nuget包 Packages Abp Abp.AspNetCore Abp.Web.Common Abp.Web Abp.Web.Mvc Abp.Web.Api Abp.Web.Api.OData Abp.Web.Resources Abp.Web.SignalR Abp.Owin Abp.EntityFramework.Common Abp.EntityFramework Abp.EntityFramework.GraphDiff Abp.EntityFrameworkCore Ab

ABP官方文档翻译 7.3 Quartz集成

Quartz集成 介绍 安装 创建Jobs 计划安排Jobs 更多 介绍 Quartz是一个全功能的.开源的job计划安排系统,可以用在小的apps也可以用于大型的企业系统.Abp.Quartz包简化了ABP集成Quartz. ABP有內建的持久化后台job队列和后台worker系统.如果对于后台workers你有更高级的计划安排需求,Quartz会是一个更好的选择.对于持久化后台job队列,Hangfire也是一个好的选择. 安装 在工程中安装Abp.Quartz nuget包并在模块上为Ab

ABP官方文档翻译 7.2 Hangfire集成

Hangfire集成 介绍 ASP.NET Core集成 ASP.NET MVC 5.x集成 面板授权 介绍 Hangfire是一个综合的后台job管理器.你可以 把它集成到ABP,用来取代默认的后台job管理器.Hangfire可以使用相同的后台jobAPI.因此,你的代码与Hangfire是独立的.但是,如果你喜欢的话,可以直接使用Hangfire的API. Hangfire集成依赖于使用的框架. ASP.NET Core集成 Abp.HangFire.AspNetCore包用来集成到ASP

ABP官方文档翻译 4.6 审计日志

审计日志 介绍 关于IAuditingStore 配置 通过特性启用/禁用 注意事项 介绍 维基百科:“审计追踪(也称为审计日志)是与安全相关的按时间先后的记录.记录集合.记录的目的地和源,提供一系列活动的纪实证据,这些活动可能在任何时刻影响一个特定操作.过程或事件.” ABP提供了基础设施自动记录应用所有的交互.它可以记录方法调用的调用者和参数. 基本上,保存的字段有:相关的tenant id,调用者user id,调用者service name(调用方法的类),调用者method name,

ABP官方文档翻译 6.4 导航

导航 创建菜单 注册导航提供者 显示菜单 每一个网络应用都会有一些菜单用来在pages/screens之间导航.ABP提供了通用的基础设施来创建并显示菜单. 创建菜单 应用可以由不同的模块组成,每一个模块都有自己的菜单项.为了定义菜单项,我们需要创建一个继承自NavigationProvider的类. 假定,我们有一个如下所示的主菜单: Tasks Reports Administration User management Role management 这里,Administration菜单

ABP官方文档翻译 6.1.2 MVC视图

ASP.NET MVC 视图 介绍 AbpWebViewPage基类 介绍 ABP通过Abp.Web.Mvc nuget包集成到MVC视图.你可以如往常一样创建正常的MVC视图. AbpWebViewPage基类 ABP提供了AbpWebViewPage,它定义了一些有用的属性和方法.如果你使用启动模板创建的工程,那么你所有的views都自动从这个基类继承. AbpWebViewPage定义了L方法用于本地化,IsGranted方法用于授权,IsFeatureEnabled和GetFeature

ABP官方文档翻译 3.3 仓储

 仓储 默认仓储 自定义仓储 自定义仓储接口 自定义仓储实现 基础仓储方法管理数据库连接 查询 获取单个实体 获取实体列表 关于IQueryable 自定义返回值 插入 更新 删除 其他 关于异步方法 管理数据库连接 仓储生命周期 仓储最佳实践 协调领域和数据映射层,使用类集合接口访问领域对象."(Martin Fowler) 实际上,仓储用来执行领域对象的数据库操作(实体和值类型).通常,每个对象(或聚合根)使用单独的仓储. 默认仓储 在ABP中,仓储类实现IRepository<TEn

ABP官方文档翻译 9.3 NHibernate集成

NHibernate集成 Nuget包 配置 实体映射 仓储 默认实现 自定义仓储 应用程序特定基础仓储类 ABP可以使用任何ORM框架,它内置集成NHibernate.此文档将讲解ABP如何使用NHibernate,假定你对NHibernate已经有了一定的了解. Nuget包 在ABP中实现NHibernate做为ORM框架的Nuget包为Abp.NHibernate.你需要在应用程序中添加它.最好在一个单独的程序集中实现NHibernate并在这个程序集里依赖Abp.NHibernate包

ABP官方文档翻译 9.1 EntityFramework集成

EntityFramework集成 Nuget包 DbContext 仓储 默认仓储 自定义仓储 应用特定的基础仓储类 自定义仓储示例 仓储最佳实践 事务管理 数据存储 ABP可以使用ORM框架,它内置集成EntityFramework.本文档将讲解ABP如何使用EntityFramework.假定你对EntityFramework已经有了初级水平. Nuget包 在ABP中使用Abp.EntityFramework nuget包扩展了EntityFramework.需要将它添加到工程中.最好在