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 會把這條記錄刪除,如下圖所示:

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