.Net 6.0 Background Service
Bu gönderide background servislere giriş yapacağız, nedir, nasıl çalışır, hangi amaçlarla kullanıldığını ve.net 7.0 ile nasıl kullanılacağını inceleyip proje yapacağız.
Background Servisler Nedir?
Background Service IHostedService‘den miras alınmış asenkron şeklinde çalışabilen, ayrı bir worker olarak çalışmasına karşın .net core web uygulaması içinde de çalışabilir.
Kullanım Alanları
- Arkaplanda asenkron olarak çalışarak, kaynaklarımızı düzgün paylaştırma imkanı vermesi.
- Rabbitmq, kafka gibi mesaj broker implematasyonu ile asenkron iletişim sağlaması.
- Günlük, aylık gibi çalışması gereken süreçleri yönetebilme kolaylığı sağlaması.
Faydalı Kütüphaneler
Aşağıdaki kütüphaneler background joblarınızı yönetmenizde büyük kolaylık sağlıyor fakat basit uygulamalarınız için ise gereksiz yük olabileceğinden dotnet’in kendi servisini kullanmak daha faydalı olacaktır.
- Hangfire
- Quartz
Örnek proje üzerinden nasıl implement edilir ona bakacağız, ardından da CancellationToken ve HostedService tabanlı bir inceleme yapacağız.
Proje (Nasıl İmplement edilir)
.Net 5.0, 6.0,7.0 sürümlerinden herhangi birine uygulayabilirsiniz. Ben .net 7.0 ile projeyi oluşturacağım. Web api uygulaması olacak, içine birden çok servis ekleyebileceğiz ilk olarak service nasıl kaydettiğimize ve çalışma mantığına bakalım.
Kullan at tek seferlik servis :
Program.cs’ye hostedservice olarak kaydedelim.
1 |
builder.Services.AddHostedService<FireForgotService>(); |
Ayrıca aşağıdaki şekilde de kaydedilebilir.
1 |
builder.Services.AddSingleton<IHostedService, FireAndForgetService>(); |
Service:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class FireForgotService : BackgroundService { private readonly ILogger<FireForgotService> _logger; private int _counter = 0; public FireForgotService(ILogger<FireForgotService> logger) { _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { _logger.LogInformation("TimerService is working. Count: {Count}", ++_counter); await Task.Delay(1000, stoppingToken); await StopAsync(stoppingToken); await Task.CompletedTask; } } } |
StopAsync() BackgroundService class’ın bir üyesi bu metod ile servisi durdurabiliyoruz.
Cancelletion token uygulamamız çalışırken kapanması, taskin iptal edilmesi gibi process durdurulma durumlarında service’deki işlemi de durduracak bir işlem türüdür. Örneğin uygulama çalışırken konsoldan ctrl c ile shıtdown yapabilirsiniz.
Zaman bazlı çalıştırabilme (Cron )
Background jobların en çok işe yaradığı nokta planlı işlerimizi çalıştırabilmeye imkan verebilmesidir. Örneğin günde 1 kez, her ayın 15’inde, 10 dakika da bir gibi çalışabilmesi.
Cron Job Örnekleri
5 alandan oluşur eğer ki 6 hane olursa ilk ifade saniyeyi temsil eder.
* * * ? * * | Her saniye |
0 */10 * ? * * | 10 dakikada bir |
0 0 * ? * * | Her saat |
0 0 1 * * ? | Her gün gece 1 |
0 0 12 1 * ? | Her ayın 1’i gece 12 |
Zaman Bazlı Çalıştırılabilen Joblar (Schedule Job)
Web api projesi içine ayarladığımız cron ifadesine göre çalışacak örnek bir service yazalım.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
public class TimerService : BackgroundService { private readonly ILogger<TimerService> _logger; private readonly string cronExp = "0/5 * * * * ?"; // 5 seconds private DateTime nextRunTime = DateTime.UtcNow; public TimerService(ILogger<TimerService> logger) { _logger = logger; SetNextDate(); } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (!stoppingToken.IsCancellationRequested) { await Task.Delay(GetDelayTime(), stoppingToken); _logger.LogInformation("TimerService is working."); nextRunTime = GetNextDate(); } } private int GetDelayTime() { var delayTime = (nextRunTime - DateTime.UtcNow).TotalMilliseconds; if (delayTime < 0) { nextRunTime = GetNextDate(); delayTime = (nextRunTime - DateTime.UtcNow).TotalMilliseconds; } return (int)delayTime; } private DateTime GetNextDate() { var cron = CronExpression.Parse(cronExp, CronFormat.IncludeSeconds); var next = cron.GetNextOccurrence(DateTime.UtcNow); return next ?? DateTime.UtcNow; } private void SetNextDate() { nextRunTime = GetNextDate(); } } |
Yukarıdaki cron ifadesi her 5 saniyede bir çalışacak anlamına gelir ayrıca bu expression’ı çözümlemek için aşağıdaki paketi yükleyebilirsiniz.
1 2 3 |
Install-Package Cronos or dotnet add package Cronos |
Elle durdurma ve çalıştırma
Uygulama ilk ayağa kalktığı anda servislerimiz çalışmaya başlar fakat sonsuz bir döngüde devam edebilir. Bu sebeple ve bazen koşullara göre bu servisleri durdurma ihtiyaçlarımız olabilir. Endpoint üzerinden istek atarak servisi durdurmayı deneyelim.
Öncelikle servisimizin bir instance’ını oluşturup sonra o instance ile injection yapmamız gerekiyor.
1 2 |
builder.Services.AddSingleton<TimerService>(); builder.Services.AddHostedService(service => service.GetRequiredService<TimerService>()); |
Api:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
app.MapGet("api/stopTimer", async (ILoggerFactory loggerFactory, IServiceProvider serviceProvider) => { var timer2Service = serviceProvider.GetRequiredService<TimerService>(); await timer2Service.StopAsync(CancellationToken.None); return "success"; }); app.MapGet("api/startTimer", async (ILoggerFactory loggerFactory, IServiceProvider serviceProvider) => { var timer2Service = serviceProvider.GetRequiredService<TimerService>(); await timer2Service.StartAsync(CancellationToken.None); return "success"; }); |
Bu halde api’ye istek atarsak throw exception ile uygulamanın tamamı duracaktır bu yüzden için ilgili servisin execute metodunu try catch blokları içine alıp hatayı yakalayalım.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
protected override async Task ExecuteAsync(CancellationToken stoppingToken) { try { while (!stoppingToken.IsCancellationRequested) { await Task.Delay(GetDelayTime(), stoppingToken); _logger.LogInformation("Timer2Service is working. {0}", _counter++); nextRunTime = GetNextDate(); } } catch (TaskCanceledException e) { _logger.LogError(e, "Error"); } catch (Exception e) { throw e; } } |
TaskCanceledException hatası StopAsync çağırma durumumuz da yakalancaktır böylece uygulamamız tamamen kapanmayıp loglara kayıt atacaktır.
Github : okankrdg/BackgroundServices (github.com)
Başka bir yazıda görüşmek üzere esen kalın.