Fırat Esmer

Microsoft Yazılımları

Hangfire Hakkında

Bugünün konusu olarak belki duyduğunuz, belki de çoktan büyük ölçekli uygulamalarınızda kullandığınız Hangfire kütüphanesinden bahsedeceğim. Hadi başlayalım. (Github)

Hangfire Nedir?

Background job'ları (arka plan işleri) yaratmanıza, yürütmenize ve yönetmenize kolaylık sağlayan açık kaynaklı kütüphanedir. Job storage olarak bir çok veritabanı (SQL Server, SQL Server + MSMQ, Redis ve daha fazlası), IoC Container ve Unit Test desteklemektedir. Listesi için Hangfire Extension sayfasına göz atabilirsiniz. Hangfire Sidekiq, Resque ve Celery uygulamalarına .NET alternatifidir. 

Background Job Nedir?

Bazı kodların arka planda çalışması gerekmektedir. Çünkü bir iş parçacığının ana thread'de çalışması hem doğası gereği hem de bazı ihtiyaçlar dahilinde uygun olmayabiliyor.

Hangfire Kullanımının Artıları

  • Kullanımı kolay, bir kaç satırla tüm .NET uygulamalarınızda çalıştırabilirsiniz,
  • Yönetilebilirlik ve görünebilirlik sağlar,
  • İşler veritabanında tutulduğu için güvenilirdir. İş tamamlanmadıkça tamamlandı durumuna geçmez, kod bloğunun bitimine kadar çıkacak herhangi bir sorunda iş tekrar çalışacaktır,
  • Uygulamanızdan farklı, dağıtık şekilde kullanılabilir (Infrastructure),
  • ASP.NET uygulamalarında yaşanan sorunlara çözüm sağlar
    • Uzun süren request thread'ler,
    • Birden fazla yaratılmış background job instance'ı (aynı işin aynı zamanda yapılabiliyor olması sorunu),
    • IIS'in AppDomain ve App Pool recycle etmesi (Background job'ların yarım kalması ve tekrarlanmaması).

Hangfire'ın Desteklediği Background Job Tipleri

  • Fire and forget : Bir kere ve hemen çalışan background job tipi
var jobId = BackgroundJob.Enqueue(
    () => Console.WriteLine("Fire-and-forget!"));
  • Delayed : Bir kere fakat belirtilen sürenin sonunda çalışan background job tipi
var jobId = BackgroundJob.Schedule(
    () => Console.WriteLine("Delayed!"),
    TimeSpan.FromDays(7));
  • Recurring : Çok kez ve belirtilmiş CRON sürecinde (günlük, saatlik, haftalık veya CRON expressions vb.) çalışan background job tipi
RecurringJob.AddOrUpdate(
    () => Console.WriteLine("Recurring!"),
    Cron.Daily);
  • Continuations : Tanımlanan ana işin bitiminde çalışan background job tipi
BackgroundJob.ContinueWith(
    jobId,
    () => Console.WriteLine("Continuation!"));
  • Batch (PRO) : Birden fazla işin grup halinde çalışan background job tipi
var batchId = BatchJob.StartNew(x =>
{
    x.Enqueue(() => Console.WriteLine("Job 1"));
    x.Enqueue(() => Console.WriteLine("Job 2"));
});
  • Batch Continuations (PRO) : Grup halinde çalışan ana background job'ın bitimiyle çalışan background job tipi
BatchJob.ContinueWith(batchId, x =>
{
    x.Enqueue(() => Console.WriteLine("Last Job"));
});

NOT : PRO olarak ifade edilen background job tipleri Hangfire'ın ücretsiz sürümünde yer almamaktadır. Yıllık ücret karşılığında bu background job'lara erişilebilir. Ücretlendirme ile ilgili bilgilere ulaşmak için Hangfire Pricing ekranına göz atabilirsiniz. (Ek olarak : Compleks iş akışları, Redis depolama desteği, performance counter işlemleri)

Hangfire Architecture

Hangfire Kurulum ve Konfigürasyon

Hangfire'ı kullanmak istediğiniz projenize Nuget Package Manager yoluyla veya komutla Hangfire'ı yükleyin.

Hangfire NuGet Package

Komut : PM> Install-Package Hangfire (Nuget)

NOT : Farklı proje tipleri için farklı paketler yüklemeniz gerekebilir. Örneğin console uygulaması için Hangfire.Core yüklemeniz lazım fakat IIS'de host edilen web uygulaması için Owin de yüklenmelidir (dependency => Hangfire). Yükleme ve paket ile ilgili bilgiler için Hangfire Installation sayfasına göz atabilirsiniz.

Aşağıda paylaştığım konfigürasyon örneği (en basit haliyle), ASP.NET MVC projesidir.

using Owin;
using Microsoft.Owin;
using Hangfire;
using HangfireDemo;

[assembly: OwinStartup(typeof(Startup))]
namespace HangfireDemo
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            // Hangfire sunucu bağlantı
            GlobalConfiguration.Configuration.
                UseSqlServerStorage(@"Server=.\SQLExpress;Database=HangfireDemo;Trusted_Connection=True;");

            // Hangfire dashboard kullan
            app.UseHangfireDashboard();

            // Hangfire sunucu kullan
            app.UseHangfireServer();
        }
    }
}

NOT : Batch kullanmak istiyorsanız ekstradan "GlobalConfiguration.Configuration.UseBatches()" satırını eklemeniz gerekiyor.

Hangfire yaratılan job'ları veritabanında tutar, tamamlandıkça da kendisi siler. Veritabanını kendisi yaratmıyor fakat gerekli tabloları kendisi oluşturuyor. (Uygulamanızın veritabanı ile Hangfire veritabanı farklı olabilir.)

Uygulamayı çalıştırdığınızda tabloların oluştuğunu göreceksiniz.

Hangfire veritabanı tabloları

Hangfire dashboard'a ulaşmak için uygulamanızın URL'inin sonuna "/hangfire" yazmanız yeterli.

Hangfire Dashboard

Hangfire Entegrasyonu

Hangfire'ı uygulamanızda kullanırken uygulayabileceğiniz bir kaç farklı senaryo var.

  • Single Process
    Hangfire Single Process
  • Web Garden
    Hangfire Web Garden
  • Web Farm
    Hangfire Web Farm
  • Separate Service
    Hangfire Separate Service
  • Separate Server
    Hangfire Separate Server

Hangfire Parametre İşlemleri

  • Çoklu parametre desteği var,
  • Parametreler serialise (JSON) edilip veritabanında tutuluyor (Array, collection, custom object),
  • Referans parametre ve ref ve out keyword'leri için desteği bulunmamaktadır,
  • Tüm kayıt yerine Id gibi belirgin değerin tutulması tavsiye edilir. Sebebi ise job storage'da tutulacak kaydın boyutu daha küçük olması.

Hangfire Dashboard

Host İşlemi

  • OWIN Middleware olarak yazılmıştır
    • ASP.NET, Nancy ve ServiceStack
  • OWIN Self Host
    • Console uygulaması ve Windows Service
  • Gereklilikler
    • Microsoft.Owin.Host.SystemWeb
    • OWIN Startup class
    • app.UseHangfireDashboard() konfigürasyon kodu

Sunduğu Özellikler

  • Servers (sunucular) : Kullanılan Hangfire sunucuları ve bilgilerini (name, workers, queues, başladığı an ve en son çalıştığı an vs.) gösterir
  • Recurring Jobs : Yaratılan recurring job bilgilerini gösterir. Bunlar : Cron, Time Zone, Job, ne zaman çalışacağı, en son ne zaman çalıştığı ve yaratıldığı tarih. Ayrıca, her ne kadar belirli bir tarih tanımlanmış olsa da Trigger özelliği ile istenilen an tetiklenebilir.
  • Retries (tekrar) : Yaratılan job'ların hata alması durumunda job'ların gösterildiği ekrandır. Kaç kere denendiği de görülebilir. Default olarak atanmış deneme (retry) sayısı 10'dur.
  • Jobs (işler)
    • Enqueued : Sırada olan işler,
    • Scheduled : İleri tarihe ayarlanmış işler,
    • Processing : Çalışan işler,
    • Succeeded : Başarılı şekilde tamamlanmış işler,
    • Failed : Başarısız olmuş işler (tanınmış atama sayısından sonra bile hata alınıyorsa iş buraya düşer),
    • Deleted : Silinmiş işler,
    • Awaiting : Sırasını bekleyen (continuations) işler.

Hangfire Dashboard Gelişmiş Konfigürasyon Seçenekleri

Hangfire Dashboard URL değişikliği ve geri yönlendirme opsiyonu

            // Dashboard üzerinden "back to site" button
            var options = new DashboardOptions { AppPath = VirtualPathUtility.ToAbsolute("/Home/Index") };
            // Dashboard custom URL
            app.UseHangfireDashboard("/ApplicationHangfireDashboard", options);

Birden fazla Hangfire Dashboard (ve/veya farklı veritabanları) kullanımı

            var storage1 = new SqlServerStorage("HangfireDatabase1");
            var storage2 = new SqlServerStorage("HangfireDatabase2");

            app.UseHangfireDashboard("/Hangfire1", new DashboardOptions(), storage1);
            app.UseHangfireDashboard("/Hangfire2", new DashboardOptions(), storage2);

            // Hangfire sunucu kullan
            // app.UseHangfireServer();

Hangfire Dashboard Güvenlik

Hangfire Dashboard'a default; local olarak erişilebilir. Örneğin sunucuda IIS'de çalışan bir web uygulamanızın Hangfire Dashboard'una remote (uzaktan erişim) makineden erişemezsiniz. Fakat uzaktan erişim için yetki vermeniz mümkün. Bunun için (IAuthorizationFilter 2.0.0 ile silinecek) IDashboardAuthorizationFilter interface'ini kullanmanız gerekiyor.

        public void Configuration(IAppBuilder app)
        {
            GlobalConfiguration.Configuration.
                UseSqlServerStorage(@"Server=.\SQLExpress;Database=HangfireDemo;Trusted_Connection=True;");

            app.UseHangfireDashboard("/hangfire", new DashboardOptions()
            {
                Authorization = new[] { new HangfireAuthorizationFilter() }
            });

            // Methodları çağırma sırası önemlidir
            // Önce authentication sonra 
            // HangFireServer
            app.UseHangfireServer();
        }

        public class HangfireAuthorizationFilter : IDashboardAuthorizationFilter
        {
            public bool Authorize(DashboardContext context)
            {
                if (HttpContext.Current.User.IsInRole("RoleName"))
                    return true;

                return false;
            }
        }

Daha fazla bilgi için => http://docs.hangfire.io/en/latest/configuration/using-dashboard.html#configuring-authorization

Hangfire Exception

Hangfire, uygulamanızda alacağınız hataları (default tekrar deneme sayısı : 10) hata detayı ile birlikte gösterir. Hata aldığınız job, retries sekmesine düşer. Retry sayısını Global ve Method seviyesinde değiştirmek mümkün. Örneğin retry sayısını 1 yaparsanız 1. denemeden sonra tekrar hata alınması durumunda job Failed sekmesine düşecektir.
Global seviyede retry sayısını değiştirmek için aşağıdaki kodu kullanabilirsiniz.

GlobalJobFilters.Filters.Add(new AutomaticRetryAttribute() {  Attempts = 1});

Method seviyesinde değiştirmek isterseniz, methodunuza aşağıdaki attribute'u ekleyebilirsiniz.

[AutomaticRetry(Attempts = 1)]

Eğer fail olan joblarınızın manuel değil de otomatik olarak silinmesini istiyorsanız
AutomaticRetry(OnAttemptsExceeded = AttemptsExceededAction.Delete) kodunu kullanabilirsiniz.
Daha fazla bilgi için => http://docs.hangfire.io/en/latest/background-processing/dealing-with-exceptions.html

HangFire Logging

Hangfire otomatik loglama desteğine (Serilog, NLog, Log4Net, EntLib Logging, Loupe ve Elmah) sahiptir. Otomatik loglama; startup sınıfında log provider tanımlamanızın yettiği anlamına geliyor. Ayrıca custom loglama desteği (ILogProvider, ILog) de mevcuttur.
Daha fazla bilgi için => http://docs.hangfire.io/en/latest/configuration/configuring-logging.html

HangFire SQL Server Ayarları

            var serverOptions = new SqlServerStorageOptions
            {
                // Hangfire'ın ne kadar süre aralıkta kontrol edeceği bilgisi
                // Default değeri 15 saniye
                QueuePollInterval = TimeSpan.FromSeconds(45),
                // Veritabanında tabloların yaratılıp yaratılmayacağı bilgisi
                // Manuel migration işlemleri için false yapılabilir
                // Default değeri true
                PrepareSchemaIfNecessary = false
            };

            GlobalConfiguration.Configuration.UseSqlServerStorage("Veritabanı", serverOptions);

Daha fazla bilgi için => http://docs.hangfire.io/en/latest/configuration/using-sql-server.html

NOT : Hangfire, Redis desteklemektedir. Redis'in tercih sebepleri arasında yüksek performans (in-memory) ilk sıradadır fakat Redis kullanmak için Hangfire PRO yani ücretli versiyonu kullanılması zorunludur. Daha fazla bilgi için => http://docs.hangfire.io/en/latest/configuration/using-redis.html

Hangfire Performance Comparison

Ve son olarak dikkat edilmesi gerekenler...

  • Job'ları kullandığınız metotlar yarıda kesilebilir ve tekrarlanabilir,
  • Methodlarda kullandığınız parametreler, kompleks olmayan (basit), küçük ve object yerine primitif tip,
  • Unit test ve IoC'ye uygun,
  • Job takibi Polling vs. Pushing (örnek : SignalR) ayrımı gözetilerek

yazılırsa kodunuz optimize olmuş olur.

Yorumlar (1) -

  • Bilal Emre Gülşen

    14.4.2017 13:49:22 | Yanıtla

    Güzel bir yazı olmuş. Eline sağlık.

Yorum ekle

Loading