<input id="ohw05"></input>
  • <table id="ohw05"><menu id="ohw05"></menu></table>
  • <var id="ohw05"></var>
  • <code id="ohw05"><cite id="ohw05"></cite></code>
    <label id="ohw05"></label>
    <var id="ohw05"></var>
  • 在Asp.Net Core中使用DI的方式使用Hangfire構建后臺執行腳本

    最近項目中需要用到后臺Job,原有在Windows中我們會使用命令行程序結合計劃任務或者直接生成Windows Service,現在.Net Core跨平臺了,雖然Linux下也有計劃任務,但跟原有方式一樣,沒撒圖形界面,執行結果之類的只能去服務器查看日志。
    看了下Hangfire,基本滿足于現有需求,有圖形UI,注冊后臺Job也非常簡便,考慮之下,就是用它了。

    安裝注冊

    Hangfire的使用也非常簡單,在項目中先安裝Hangfire包:

    PM> Install-Package Hangfire
    

    Asp.Net Core項目的話,打開Startup.cs,在ConfigureServices方法中添加注冊:

    services.AddHangfire(x => x.UseSqlServerStorage("connection string"));
    

    connection string是數據庫連接字符串,我用的時Sql Server,你也可以使用Redis,Mysql等其他數據庫。

    注冊完成后,我們在Configure方法中,添加如下代碼:

    app.UseHangfireServer();
    app.UseHangfireDashboard();
    

    好了,等項目啟動之后,Hangfire先Migration相關數據結構,項目啟動之后,可以通過項目地址+/Hangfire查看是否運行成功,看到如下界面基本沒有問題了。
    image

    基本使用

    Hangfire的使用非常簡單,基本上使用以下幾個靜態方法:

    //執行后臺腳本,僅執行一次
    BackgroundJob.Enqueue(() => Console.WriteLine("Fire-and-forget!")); 
    
    //延遲執行后臺腳本呢,僅執行一次
    BackgroundJob.Schedule(
        () => Console.WriteLine("Delayed!"),
        TimeSpan.FromDays(7));
        
    //周期性任務
    RecurringJob.AddOrUpdate(
        () => Console.WriteLine("Recurring!"),
        Cron.Daily);
        
    //等上一任務完成后執行
    BackgroundJob.ContinueWith(
        jobId,  //上一個任務的jobid
        () => Console.WriteLine("Continuation!"));
    

    依賴注入

    在.Net Core中處處是DI,一不小心,你會發現你在使用Hangfire的時候會遇到各種問題,比如下列代碼:

    public class HomeController : Controller
    {
        private ILogger<HomeController> _logger;
        public HomeController(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<HomeController>();
        }
        public IActionResult Index()
        {
            _logger.LogInformation("start index");
            BackgroundJob.Enqueue(() => _logger.LogInformation("this a job!"));
            return View();
        }
    
    }
    

    項目啟動后,你能正常訪問,但在Hangfire后臺你會看到如下錯誤:

    image
    錯誤信息呢大概意思是不能使用接口或者抽象方法類,其實就是因為Hangfire沒有找到實例,那如何讓Hangfire支持DI呢?

    我們先創建一個MyActivator類,使其繼承Hangfire.JobActivator類,代碼如下:

    public class MyActivator : Hangfire.JobActivator
    {
        private readonly IServiceProvider _serviceProvider;
        public MyActivator(IServiceProvider serviceProvider) => _serviceProvider = serviceProvider;
    
        public override object ActivateJob(Type jobType)
        {
            return _serviceProvider.GetService(jobType);
        }
    }
    

    重寫了ActivateJob方法,使其返回的類型從我們的IServiceProvider中獲取。

    我們試著寫兩個后臺腳本,CheckService和TimerService,CheckService的Check方法在執行計劃時,會再次調用Hangfire來定時啟動TimerService:

    CheckService:

    public interface ICheckService
    {
        void Check();
    }
    public class CheckService : ICheckService
    {
        private readonly ILogger<CheckService> _logger;
        private ITimerService _timeservice;
        public CheckService(ILoggerFactory loggerFactory,
            ITimerService timerService)
        {
            _logger = loggerFactory.CreateLogger<CheckService>();
            _timeservice = timerService;
        }
    
        public void Check()
        {
            _logger.LogInformation($"check service start checking, now is {DateTime.Now}");
            BackgroundJob.Schedule(() => _timeservice.Timer(), TimeSpan.FromMilliseconds(30));
            _logger.LogInformation($"check is end, now is {DateTime.Now}");
        }
    }
    

    TimerService:

    public interface ITimerService
    {
        void Timer();
    }
    public class TimerService : ITimerService
    {
        private readonly ILogger<TimerService> _logger;
        public TimerService(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<TimerService>();
        }
        public void Timer()
        {
            _logger.LogInformation($"timer service is starting, now is {DateTime.Now}");
            _logger.LogWarning("timering");
            _logger.LogInformation($"timer is end, now is {DateTime.Now}");
        }
    }
    

    目前還無法使用,我們必須在Startup中注冊這2個service:

    services.AddScoped<ITimerService, TimerService>();
    services.AddScoped<ICheckService, CheckService>();
    

    我們在HomeController修改以下:

    public IActionResult Index()
    {
        _logger.LogInformation("start index");
        BackgroundJob.Enqueue<ICheckService>(c => c.Check());
        return View();
    }
    

    好,一切就緒,只差覆蓋原始的Activator了,我們可以在Startup.cs中的Configure方法中使用如下代碼:

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, IServiceProvider serviceProvider)
    {
        GlobalConfiguration.Configuration.UseActivator<MyActivator>(new MyActivator(serviceProvider));
        ……
        ……
    }
    

    默認情況下Configure方法時沒有IServiceProvider參數的,請手動添加

    再次啟動,我們的Job就會成功執行,截圖如下:
    image

    參考資料

    posted @ 2018-06-24 20:57  James.Ying  閱讀(3894)  評論(2編輯  收藏  舉報
    国产美女a做受大片观看