İçeriğe geç

ASP.NET MVC’de Asenkron Controller (Async/Await)



MVC’de Asenkron İşlemler ile Sunucu Performansını Artırma

Modern web uygulamalarında sunucu performansı, kullanıcı deneyimi ve uygulamanın genel ölçeklenebilirliği için kritik bir öneme sahiptir. Özellikle veritabanı sorguları, harici API çağrıları veya dosya işlemleri gibi I/O yoğun işlemler, sunucu tarafında uzun süreli blokajlara yol açarak performansı olumsuz etkileyebilir. Geleneksel senkron programlama modellerinde, bu tür işlemler tamamlanana kadar sunucu iş parçacığı (thread) meşgul kalır ve diğer gelen istekleri işleyemez hale gelir. Bu durum, özellikle yüksek trafikli uygulamalarda ciddi darboğazlar yaratır. .NET Core MVC çerçevesi, Task<ActionResult> ve await anahtar kelimeleri ile asenkron programlamayı kolaylaştırarak bu soruna zarif bir çözüm sunar. Bu makalede, bu yapıların nasıl kullanıldığını, sunucu performansını nasıl artırdığını ve özellikle veritabanı işlemlerinde nasıl uygulandığını detaylı bir şekilde inceleyeceğiz.

Synchronous ve asynchronous işlemlerin anlaşılması

Bir sunucu uygulamasında, her gelen istek genellikle bir iş parçacığı (thread) tarafından işlenir. Senkron (synchronous) bir işlemde, bir metodun çağrılması ve tamamlanması arasında geçen süre boyunca, o metodu çağıran iş parçacığı tamamen meşgul kalır. Bu, özellikle veritabanı sorguları veya ağ istekleri gibi I/O (Input/Output) tabanlı işlemler söz konusu olduğunda bir problem haline gelir. Çünkü bu işlemlerin büyük bir kısmı, CPU’nun bir şey hesaplamasını beklemek yerine, diskin veya ağın bir yanıt vermesini beklemekle geçer. Bu bekleme süresi boyunca, iş parçacığı hiçbir iş yapmadan boşta durur ve diğer gelen istekler için kullanılamaz.

Öte yandan, asenkron (asynchronous) bir işlemde, bir I/O işlemi başlatıldığında, metodu çağıran iş parçacığı serbest bırakılır ve havuzdaki (thread pool) diğer görevleri veya gelen istekleri işlemek üzere kullanılabilir hale gelir. Veritabanı sorgusu tamamlandığında veya ağdan yanıt geldiğinde, işletim sistemi bu olayı bildirim mekanizmaları aracılığıyla uygulamaya iletir. Uygulama, daha sonra, orijinal isteği işlemek için havuzdan boşta olan başka bir iş parçacığı kullanarak işlemin kalan kısmını tamamlar. Bu model, özellikle sunucu uygulamalarında sınırlı olan iş parçacığı kaynaklarının çok daha verimli kullanılmasını sağlar ve aynı anda daha fazla isteğin karşılanabilmesine olanak tanır.

Task<ActionResult> ve await’in mvc’deki rolü

ASP.NET Core MVC’de Task<ActionResult> ve await anahtar kelimeleri, asenkron programlamanın temelini oluşturur. Bir controller action’ının dönüş tipi Task<ActionResult> olarak belirtildiğinde, bu action’ın asenkron bir işlem yürüteceği ve bir Task nesnesi döndüreceği ifade edilir. Task, henüz tamamlanmamış bir işlemi temsil eden bir .NET türüdür.

await anahtar kelimesi ise bir Task nesnesini beklemek için kullanılır. Bir metotta await ile bir Task beklendiğinde, eğer beklenen Task henüz tamamlanmadıysa, mevcut iş parçacığı bloke edilmez. Bunun yerine, kontrol çağıran metoda geri döner ve o iş parçacığı, havuzdaki diğer işleri yapmak üzere serbest bırakılır. Beklenen Task tamamlandığında, kontrol asenkron metodun kaldığı yerden devam eder ve genellikle farklı bir iş parçacığı üzerinde çalışmaya başlar. Bu, özellikle I/O yoğun işlemler için büyük bir avantajdır, çünkü sunucu iş parçacıkları bekleme süreleri boyunca boşta kalmak yerine diğer istekleri işleyebilir, bu da genel sunucu ölçeklenebilirliğini ve duyarlılığını artırır. Bu mekanizma, derleyici tarafından arka planda bir durum makinesi (state machine) oluşturularak gerçekleştirilir.

Veritabanı işleminde asenkron kullanım örneği

Veritabanından uzun süren bir veri çekme işlemi, asenkron programlamanın en yaygın ve faydalı kullanım senaryolarından biridir. Aşağıdaki örnekte, bir MVC Controller’ında nasıl asenkron bir veritabanı işlemi gerçekleştirebileceğimizi görelim. Burada, Entity Framework Core’un asenkron metotlarını kullandığımızı varsayıyoruz.

Veri erişim katmanı (data access layer)

Öncelikle, veritabanı işlemlerini yönetecek bir servis veya repository katmanı düşünelim. Bu katmanda asenkron veritabanı metotlarını kullanmak önemlidir (örneğin ToListAsync(), FirstOrDefaultAsync(), SaveChangesAsync() gibi).


// DataService.cs
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }
}

public interface IProductService
{
    Task<List<Product>> GetAllProductsAsync();
    Task<Product> GetProductByIdAsync(int id);
}

public class ProductService : IProductService
{
    private readonly ApplicationDbContext _context; // DbContext örneği

    public ProductService(ApplicationDbContext context)
    {
        _context = context;
    }

    public async Task<List<Product>> GetAllProductsAsync()
    {
        // Uzun süren bir veritabanı işlemi simülasyonu
        // Gerçek uygulamalarda bu kısım _context.Products.ToListAsync() gibi olur
        await Task.Delay(2000); // 2 saniye bekletme simülasyonu

        // Gerçek bir Entity Framework Core kullanımı şöyle olurdu:
        // return await _context.Products.ToListAsync();

        return new List<Product>
        {
            new Product { Id = 1, Name = "Laptop", Price = 1200 },
            new Product { Id = 2, Name = "Mouse", Price = 25 },
            new Product { Id = 3, Name = "Keyboard", Price = 75 }
        };
    }

    public async Task<Product> GetProductByIdAsync(int id)
    {
        // Gerçek bir Entity Framework Core kullanımı şöyle olurdu:
        // return await _context.Products.FirstOrDefaultAsync(p => p.Id == id);
        await Task.Delay(1000); // 1 saniye bekletme simülasyonu
        var products = await GetAllProductsAsync(); // Simülasyon olduğu için çağırıyoruz
        return products.FirstOrDefault(p => p.Id == id);
    }
}
    
MVC controller’ı

Şimdi bu asenkron servisi bir MVC controller’ında nasıl kullanacağımızı görelim:


// ProductsController.cs
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading.Tasks;

public class ProductsController : Controller
{
    private readonly IProductService _productService;

    public ProductsController(IProductService productService)
    {
        _productService = productService;
    }

    // Uzun süren bir veritabanı işlemini asenkron olarak çağıran action
    public async Task<IActionResult> Index()
    {
        // Veritabanından tüm ürünleri asenkron olarak çek
        // 'await' anahtar kelimesi, GetAllProductsAsync() tamamlanana kadar
        // mevcut iş parçacığını bloke etmez, diğer isteklere açar.
        List<Product> products = await _productService.GetAllProductsAsync();

        // Ürünler çekildikten sonra View'a gönder
        return View(products);
    }

    public async Task<IActionResult> Details(int id)
    {
        Product product = await _productService.GetProductByIdAsync(id);
        if (product == null)
        {
            return NotFound();
        }
        return View(product);
    }
}
    

Bu örnekte:

  • Index action metodunun dönüş tipi Task<IActionResult> olarak tanımlanmıştır. Bu, ASP.NET Core’a bu metodun asenkron olduğunu ve bir Task döndüreceğini bildirir.
  • Metot içinde async anahtar kelimesi kullanılmıştır. Bu, metodun içinde await kullanılmasına izin verir.
  • await _productService.GetAllProductsAsync(); satırında, veritabanı işlemi (simülasyon olarak Task.Delay) başlar. Bu süre boyunca, controller metodu (ve onu çağıran iş parçacığı) veritabanının yanıtını beklemek için bloke olmaz. İş parçacığı, ASP.NET Core’un iş parçacığı havuzuna geri döner ve diğer gelen istekleri işleyebilir.
  • Veritabanı işlemi tamamlandığında, sistem kontrolü await ifadesinden sonraki satıra geri döndürür (muhtemelen havuzdan başka bir iş parçacığı üzerinde) ve metodun geri kalanı yürütülür.

Bu yaklaşım sayesinde, uzun süren veritabanı işlemleri, sunucunun sınırlı iş parçacığı kaynaklarını israf etmeden, uygulamanın genel duyarlılığını ve eşzamanlı istek işleme kapasitesini artırır.

Özetle, Task<ActionResult> ve await anahtar kelimelerinin ASP.NET Core MVC uygulamalarında kullanımı, özellikle I/O yoğun işlemler için sunucu performansını ve ölçeklenebilirliği önemli ölçüde artıran güçlü bir yaklaşımdır. Geleneksel senkron modellerde bir veritabanı çağrısı veya harici bir API isteği gibi uzun süreli işlemler, sunucu iş parçacıklarını bloklayarak diğer eşzamanlı isteklerin işlenmesini engeller. Asenkron programlama, bu iş parçacıklarını, bekleyen işlemler tamamlanana kadar diğer görevlere serbest bırakır. Bu sayede, aynı anda daha fazla isteğin karşılanabilmesi sağlanır, kaynak kullanımı optimize edilir ve uygulama daha duyarlı hale gelir. Bu yaklaşım, yüksek trafikli senaryolarda sistemin genel verimliliğini ve kullanıcı deneyimini doğrudan etkileyen kritik bir mimari karardır. Modern web uygulamaları geliştirirken, asenkron desenlerin doğru ve bilinçli bir şekilde uygulanması, yüksek performanslı ve ölçeklenebilir çözümler üretmenin anahtarıdır.


Resim Sahibi: Kindel Media
https://www.pexels.com/@kindelmedia

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir