文檔目錄
本節內容:
簡介
ABP提供了後台作業和工作者,用來在後台線程裡執行應用裡的某些任務。
後台作業
後台作業用一種隊列且持久穩固的方式安排一些待執行後台任務,你可能有幾個理由,需要用到後台作業,例如:
關於作業持久化
查看後台作業存儲小節獲取有關作業持久化的更多信息。
創建一個後台作業
我們可以通過繼承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; } 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倉儲(可獲取用戶電子郵件)和郵件發送器(一個發送郵件的服務),並簡單的發送郵件,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; } }
一個作業的參數應當可序列化,因為它要被序列化後存儲到數據庫,雖然ABP默認後台作業管理器使用JSOn序列化器(它不需要使用[Serializable]特性),更好還是定義為[Serializable],因為將來我們可能會替換成另一個作業管理器,可能會使用.net內置的二進制序列化器。
保存你的參數簡單(如DTO),不要包含實體或其它非序列化對象,如所示的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, IBackgroundManager將實體化並以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) 有其它參數,如priority和delay。
默認的後台作業管理器
BackgroundJobManager默認實現了IBackgroundJobManager,它可被其它後台作業提供器替代(查看Hangfire文檔)。如下為一些關於默認BackgroudJobManager的信息:
後台作業存儲
默認的BackgroundJobManager需要一個數據存儲來保存和獲取作業,如果你沒有實現IBackgroundJobStore,它會使用InMemoryBackgroundJobStore,它不在持久化的數據庫中保存作業,你可以簡單的實現這個接口,讓作業存儲到一個數據庫或使用已經實現該接口的module-zero。
如果你使用第三方的作業管理器(如Hangfire),不需要實現IBackgroundJobStore。
配置
你可以在模塊的PreInitialize方法裡,使用Configuration.BackgroundJobs配置你的後台作業系統。
禁用作業執行
你可能會想為你的應用禁用後台作業執行:
public class MyProjectWebModule : AbpModule { public override void PreInitialize() { Configuration.BackgroundJobs.IsJobExecutionEnabled = false; } //... }
很少需要這樣,但考慮一下你正在運行一個應用的多個實例並訪問同一個數據庫,這種情況下,每個應用將向同個數據庫查詢作業並執行它們,這可能導致同個作業的多次執行和一些其它問題,為阻止這種情況,我們有兩個選擇:
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(); } } }
這是一段真實的代碼,它工作在ABP的module-zero裡。
注冊後台工作者
在完成創建後台工作者後,需要把它添加到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工作者實例用來監視和刪除不同的日志目錄)。
使你的應用一直運行
只有當你的應用運行時,後台作業和工作者才能工作,如果一個Web應用長時間沒有收到訪問請求,它默認地會被關閉,所以,如果你的宿主後台作業運行在你的web應用裡(這是默認行為),你應當確保你的web應用被配置成一直運行,否則,只有當你的應用在使用的時候,後台作業才能工作。
這裡有些技術可以做到這點,一個非常簡單的辦法是:從一個外部應用裡定期訪問你的Web應用,從而你可以一直檢查你的web應用是否一直運行著。Hangfire文檔解釋了一些其它方法。
kid1412附:英文原文:http://www.aspnetboilerplate.com/Pages/Documents/Background-Jobs-And-Workers