Abp vNext : 使用 BackgroundJobs

内存中的BackgroundJobs

第1步:在项目中应用如下包:

<PackageReference Include="Volo.Abp.BackgroundJobs" Version="6.0.1" />

然后添加模块依赖

using AbpManual;
using Volo.Abp.Application;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.Emailing;
using Volo.Abp.MailKit;
using Volo.Abp.Modularity;

namespace AbpManaul
{
    [DependsOn(
        typeof(AbpBackgroundJobsModule), 
        ...
    )]
    public class AbpManualApplicationModule : AbpModule
    {
    }
}

第2步:定义类 EmailSendingArgs,用于保存后台作业的数据

namespace AbpManual.BackgroundJobs
{
    /// <summary>BackgroundJob 的会自动进行重试
    /// 后台作业数据
    /// </summary>
    public class EmailSendingArgs
    {
        public string EmailAddress { get; set; }
        public string Subject { get; set; }
        public string Body { get; set; }
    }

}

第3步:定义后台作业类 EmailSendingJob

同步版本,继承BackgroundJob<T>

using Microsoft.Extensions.Logging;
using Volo.Abp;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Emailing;

namespace AbpManual.BackgroundJobs
{
    /// <summary>
    /// https://docs.abp.io/zh-Hans/abp/latest/Background-Jobs
    /// 后台作业是一个实现IBackgroundJob<TArgs>接口或继承自BackgroundJob<TArgs>类的类
    /// </summary>
    public class EmailSendingJob : BackgroundJob<EmailSendingArgs>, ITransientDependency
    {
        private readonly ILogger<EmailSendingJob> _logger;

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

        public override  void Execute(EmailSendingArgs args)
        {
            // 模拟随机产生异常,看看后台作业是否因失败再次执行
            if (RandomHelper.GetRandom(0, 10) < 5)
            {
                throw new ApplicationException("A sample exception from the EmailSendingJob!");
            }

            _logger.LogInformation($"############### EmailSendingJob: 邮件已成功发送至邮箱:{args.EmailAddress} ###############");
        }
    }
}

异步版本,继承AsyncBackgroundJob<T>, 如下代码所示:

using Microsoft.Extensions.Logging;
using Volo.Abp;
using Volo.Abp.BackgroundJobs;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Emailing;

namespace AbpManual.BackgroundJobs
{
    /// <summary>
    /// https://docs.abp.io/zh-Hans/abp/latest/Background-Jobs
    /// 后台作业是一个实现IBackgroundJob<TArgs>接口或继承自BackgroundJob<TArgs>类的类
    /// </summary>
    public class EmailSendingJob : AsyncBackgroundJob<EmailSendingArgs>, ITransientDependency
    {
        private readonly IEmailSender _emailSender;
        private readonly ILogger<EmailSendingJob> _logger;

        public EmailSendingJob(
            IEmailSender emailSender,
            ILogger<EmailSendingJob> logger)
        {
            _emailSender = emailSender;
            _logger = logger;
        }

        public override  async Task ExecuteAsync(EmailSendingArgs args)
        {
            // 模拟随机产生异常,看看后台作业是否因失败再次执行
            if (RandomHelper.GetRandom(0, 10) < 5)
            {
                throw new ApplicationException("A sample exception from the EmailSendingJob!");
            }

            _logger.LogInformation($"############### EmailSendingJob: 邮件已成功发送至邮箱:{args.EmailAddress} ###############");

            // 发送邮件
            await _emailSender.SendAsync(
               args.EmailAddress,
               args.Subject,
               args.Body
           );
        }
    }
}

第4步:使用 IBackgroundJobManager 发布后台作业

using AbpManual.BackgroundJobs;
using Volo.Abp.Application.Services;
using Volo.Abp.BackgroundJobs;

namespace AbpManaul.BackgroundJobs
{
    public class BackgroundJobService : ApplicationService, IBackgroundJobService
    {
        /// <summary>
        /// 默认后台作业管理器
        /// https://docs.abp.io/zh-Hans/abp/latest/Background-Jobs
        /// ABP framework 包含一个简单的 IBackgroundJobManager 实现;
        /// </summary>
        private readonly IBackgroundJobManager _backgroundJobManager;

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

        public async Task RegisterAsync(RegisterInput input)
        {
            //TODO: 创建一个新用户到数据库中...

            await _backgroundJobManager.EnqueueAsync(
                new EmailSendingArgs
                {
                    EmailAddress = input.EmailAddress,
                    Subject = "You've successfully registered!",
                    Body = "恭喜你注册成功!"
                }
            );
        }
    }
}

至此,就完成了后台作业的全部过程,并且,如果此次作业执行失败,执行失败的作业会被保存在内存中,过一段时间后(默认是60秒 x 等待时间因子(默认值为2)),BackgroundJob 的会自动进行重试。

BackgroundJobs Moudle

上一节,BackgroundJobs 的执行失败的作业是存储内存中, 虽然也可以实现 BackgroundJob 的重试,但是毕竟是保存在内存中,如果重启了进程,内存中的任务就会丢失,
而 Abp vNext 社区版的 BackgroundJobs Moudle, 参见:后台作业模块,已经实现了将 BackgroundJob 存储到数据库中.
只需要在现有的项目引入即可。下面是在现有项目中引入BackgroundJobs Moudle 的的步骤:

第零步:照搬与内存中的BackgroundJobs 中的的几个步骤

第一步:AbpManual.Domain.Shared

在该项目引入如下模块:

<ItemGroup>
    <PackageReference Include="Volo.Abp.BackgroundJobs.Domain.Shared" Version="6.0.1" />
  </ItemGroup>

添加模块依赖:

[DependsOn(
    typeof(AbpBackgroundJobsDomainSharedModule)
    )]
public class AbpManualDomainSharedModule : AbpModule
{
}

第二步:AbpManual.Domain

在该项目引入如下模块:

<ItemGroup>
    <PackageReference Include="Volo.Abp.BackgroundJobs.Domain" Version="6.0.1" />
  </ItemGroup>

添加模块依赖:

using Volo.Abp.BackgroundJobs;
using Volo.Abp.Modularity;

namespace AbpManual;

[DependsOn(
    typeof(AbpManualDomainSharedModule),
    typeof(AbpBackgroundJobsDomainModule)
    )]
public class AbpManualDomainModule : AbpModule
{
}

第三步:AbpManual.EntityFrameworkCore

在该项目引入如下模块:

<ItemGroup>
    <PackageReference Include="Volo.Abp.BackgroundJobs.EntityFrameworkCore" Version="6.0.1" />
  </ItemGroup>

添加模块依赖:

namespace AbpManual.EntityFrameworkCore;

[DependsOn(
    ...
    typeof(AbpBackgroundJobsEntityFrameworkCoreModule)
    )]
public class AbpManualEntityFrameworkCoreModule : AbpModule
{
...
}

第四步:添加 BackgroundJobsModule 模块的数据库迁移

首先,在 AbpManualDbContext 类中添加 BackgroundJobsModule 模块的数据库配置


namespace AbpManual.EntityFrameworkCore;
public class AbpManualDbContext :
    AbpDbContext<AbpManualDbContext>
{
    public AbpManualDbContext(DbContextOptions<AbpManualDbContext> options) 
        : base(options)
    {
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        ...
        builder.ConfigureBackgroundJobs();
        ...
    }
}

然后添加数据库迁移

add-migration InitialBackgroundJobsMoudle

将迁移更新到数据库

update-database

或者 运行项目 AbpManual.DbMigrator 进行数据库更新,
更新成功后,会新增一张数据库表 AbpBackgroundJobs,如下图所示:

第五步:在项目 AbpManual.Web 或 AbpManual.HttpApi.Host 中 ,配置 BackgroundJob 的参数

配置 BackgroundJob 的参数,以便更快重试失败的后台作业,立马能查看的测试结果

    public class AbpManualWebModule : AbpModule
    {
        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            var configuration = context.Services.GetConfiguration();

            ConfigureBackgroundJob(context, configuration);
            ...
        }
       /// <summary>
        /// 配置后台作业
        /// </summary>
        /// <param name="context"></param>
        /// <param name="configuration"></param>
        private void ConfigureBackgroundJob(ServiceConfigurationContext context, IConfiguration configuration)
        {
            Configure<AbpBackgroundJobWorkerOptions>(options =>
            {
                options.DefaultTimeout = 864000; //10 days (as seconds)

                //配置重试参数,以便能快速测试效果
                options.JobPollPeriod = 1000;  // 优先级
                options.DefaultFirstWaitDuration = 10;  // 重试间隔,单位秒
                options.DefaultWaitFactor = 1;  // 等待时间因子
            });

            // 默认后台管理器不支持多进程执行相同的作业队列.
            // 所以, 如果你的应用程序中有多个正在运行的实现,并且使用的是默认的后台管理器, 你应该只在一个应用程序实例进程中启用作业队列.
            //Configure<AbpBackgroundJobOptions>(options =>
            // {
            //     options.IsJobExecutionEnabled = false; //禁用作业执行
            // });
        }

第六步:测试

点击执行按钮,如果执行作业失败,如下图所示:

这时,数据库表AbpBackgroundJobs 中会插入这个执行失败作业的记录,如下图所示:

在配置指定的时间后,失败的作业会再次被执行,如果还是失败,数据库表 AbpBackgroundJobsTryCount 会累加1,并更新相关其它字段,如下图所示:

如果最终这条后台作业执行成功,如下图所示:

数据库表AbpBackgroundJobs 会把这条记录删除,如下图所示:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章