Hangfire定時觸發做業,好像很簡單?

【導讀】本節咱們繼續稍微詳細講講在我沒有詳細瞭解源碼的前提下來探討經過Hangfire定時觸發做業有哪些須要注意的事項


間隔時間內執行做業git


舉個栗子,每隔10秒監控系統CPU,若CPU飆高(根據實際業務定義百分比)則在控制檯打印輸出,第一次執行做業若CPU飆高則打印輸出,但在接下來一分鐘內CPU連續飆高則再也不打印,若中間有中斷(CPU正常)則恢復正常打印,如此反覆循環。github


通常來說定時做業都會執行業務,但上述栗子卻根據做業內部邏輯判斷是否執行打印,因此兩者仍是有所區別web


接下來咱們一步步來進行大體模擬實現,首先咱們利用內存來存儲做業相關操做,而後每隔10秒執行打印方法算法

_colorify = new Format(Theme.Dark);

GlobalConfiguration.Configuration.UseMemoryStorage();

using var server = new BackgroundJobServer();

RecurringJob.AddOrUpdate(() => Print(), "*/10 * * * * *", TimeZoneInfo.Local);

Console.ReadLine();

接下來則是執行上述打印方法
微信

public static void Print()
{
     _colorify.WriteLine($"{DateTime.Now}:CPU飆高啦~~~", Colors.txtSuccess);
}

待執行做業方法必定要爲公共(public)方法,不然會拋出以下異常併發

最後咱們每隔10秒執行一次做業看看打印輸出時間是否如咱們所預期那樣
app

雖然Hangfire從1.7+開始支持秒級,但對於做業默認的最小間隔時間是15秒,貌似是沒法改變。因此上述咱們看到的做業間隔時間差是15秒而非10秒
編輯器


即便針對只觸發一次做業設置爲10秒也無濟於事,不知道是否可改變,未深刻研究
ide

var options = new BackgroundJobServerOptions
{
     SchedulePollingInterval = TimeSpan.FromMilliseconds(10)
};
BackgroundJob.Schedule(() => Print(), TimeSpan.FromSeconds(10));

以前咱們講過若是利用SQLite存儲做業那麼將會出現重複併發執行的狀況,比如咱們以下將其修改成SQLite存儲性能

GlobalConfiguration.Configuration.UseSQLiteStorage("Data Source=./hangfire.db;");

此時毫無疑問會出現連續打印狀況(在內存中也會偶爾出現,機率沒有SQLit高)

如果必須限制在間隔時間內只能執行一次做業且在內存或SQLite中存儲做業,那麼咱們能夠嘗試使用限流算法(漏桶算法),在指定時間內只容許幾個請求進入(算法參考地址:https://github.com/robertmircea/RateLimiters)

private static readonly FixedTokenBucket bucket = new FixedTokenBucket(1110000);

實例化對應漏桶算法且在10秒內只能透傳1個請求執行做業

public static void Print()
{
    if (bucket.ShouldThrottle(1))
    {
         return;
    }
    _colorify.WriteLine($"{DateTime.Now}:CPU飆高啦~~~", Colors.txtSuccess);
}

若在10秒超過1個請求進入則當即返回

接下來咱們實如今1分鐘內禁止連續打印CPU飆高的狀況,首先咱們將1分鐘內時間控制利用內存存儲來實現

var provider = new ServiceCollection()
     .AddMemoryCache()
     .BuildServiceProvider();

cache = provider.GetService<IMemoryCache>();


而後咱們繼續改造打印方法,在內存中記錄第1次打印的時間,而後對比接下來1分鐘的時間差,若小於則返回,不然打印再次存儲打印的時間

public static void Print()
{
    double totalMinutes = 0;

    if (cache.TryGetValue("sys_alarm_time"out DateTime time))
    {
      var subtract = DateTime.Now.Subtract(time);

      totalMinutes = subtract.TotalMinutes;

      _colorify.WriteLine($"subtract:{totalMinutes}", Colors.txtInfo);
    }

    if ((int)totalMinutes < 1 && totalMinutes != 0)
    {
      return;
    }

    cache.Set("sys_alarm_time", DateTime.Now);

    _colorify.WriteLine($"{DateTime.Now}CPU飆高啦~~~", Colors.txtSuccess);
}


這裏惟一須要注意的是在比較時間差1分鐘,不能用Convert.ToInt32來進行強制轉換

if (Convert.ToInt32(totalMinutes) < 1 && totalMinutes != 0)
{
   return;
}

利用上述強制轉換不能精確到接近於1分鐘,由於它是銀行家算法四捨五入,更貼切的說是四捨六入,好比爲時間差爲0.6時,通過強制轉換後結果就爲1,因此利用第一種強制轉換則是隻取整數部分


雖然說做業執行時間長短會略有差別,但利用第1種強制轉換會控制時間差不會和1分鐘相差太多

咱們看到上述時間間隔恰好是1分鐘加上默認的時間間隔15秒,能作到這樣基本上差很少了


本節咱們藉助一個栗子主要講述在控制檯中執行存儲在內存或SQLite中的做業,在實際項目中,使用Hangfire時或多或少都會存在一些問題


好比咱們是否考慮將做業存儲在內存中,那麼對於間隔時間很短的定時做業,是否會帶來的很大的存儲開銷呢?理論上Hangfire會對其進行處理,又好比若是做業有幾百個間隔時間很短的定時做業,那麼Hangfire是否會存在性能問題呢?還有其餘等在使用過程當中可能遇到的問題。

本文分享自微信公衆號 - dotNET跨平臺(opendotnet)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索