Bir uygulamanın tek bir TCP bağlantısı üzerinden çift yönlü veri alışverişi yapabilmesini sağlayan WebSocket protokolü ile uygulama altyapımız ve sunucularımız arasındaki ilişkilerin yönetimine değineceğiz.

Yazılım süreçlerimizde olduğu gibi; ölçeklendirilmiş, dağıtık yapıda olan bir sistem içinde bağımlılık yönetimi söz konusudur. Örneğin; www.netcore.com adresinden hizmet veren bir istemci uygulaması; yerel ağda 10.1.1.10 ip adresli ön bellek sunucusuna ve 10.1.1.11 ip adresli API - Servis sunucusuna ihtiyaç duyabilir. Bu durumda; yerel ağda bulunan bu iki sunucunun durumunu yönetmek ve istemci uygulamasının servisler ve ön bellek ile ilgili etkin kararlar verebilmesini sağlamamız gerekmektedir.

WebSocket protokolü HTTP üzerinden tarayıcılar ve web sunucuları tarafından uyarlanmak için tasarlanmış olsada herhangi bir istemci - sunucu uygulaması tarafındanda kolayca kullanılabilmektedir.

NetCoreStack.WebSockets kütüphanesi ile yukarıda değindiğimiz basit yapıyı birbirleri ile haberleştireceğiz.

Örnekleme yaparak ilerleyelim. www.netcore.com adresinden çalışan web uygulaması çalışma prensipleri gereği ön bellek ve servis sunucuları hazır olmadıkça HTTP isteklerini kabul etmeyecek ve kullanıcıya bir yükleme ekranı gösterecek. Bu süreç her versiyon çıkıldığında veya uygulama sunucusu yeniden başlatıldığında devam edebilecek. Sonunda arka planda işlemler tamamlanıp (ön bellek ve servis sunucusu ayaklandığında) değişkenler uygun değere eşitlendiğinde sistem hizmet vermeye başlayacak.

Bunun için aşağıdaki şekilde 3 proje oluşturuyorum:

İstemci uygulamasını WebClient.Hosting olarak isimlendirdim ve projeye yukarıda belirtilen paketleri ekliyorum. Bunun için proje ismine sağ tıklayıp -> Manage Nuget Packages demeniz yeterli.

Burada Discovery.Hosting isimli .NET Core Web API projesi orkestra şefi görevi görecek ve bu projeye sadece NetCoreStack.WebSockets kütüphanesini referans göstermemiz ve Startup.cs dosyasında aşağıdaki düzenlemeleri yapmamız yeterli olacaktır.

Böylece ASP.NET Core' un kendine özgü yapısıyla Startup.cs isimli dosyada gerekli servis ve bileşenleri işaretlenmiş metotlar aracılığıyla sağladık. Bu uygulama kendisine gelen WebSocket bağlantı isteklerini kabul edip bağlantı yaşam süresi boyunca diğer uç nokta veya noktalara istediğimiz zamanda veri göndermemizi sağlayacaktır.

WebClient.Hosting isimli projeyi ise www.netcore.com adresindeki uygulamamız olduğunu varsayıyorum. Buna göre bu uygulamanın Startup.cs dosyasıda aşağıdaki şekilde olacaktır:

Discover.Hosting projesinin adres ve port bilgisi (localhost:5916 - burada ortama [Environment] göre değişken belirlenebilir), bağlantı adını ve gelen tüm mesajları yorumlayabileceğimiz ve IClientWebSocketCommandInvocator arayüzünden türettiğimiz:

komut çalıştırıcısı yukarıdaki şekilde olacaktır. Bu sayede orkestra şefi ön bellek sunucusunun hazır olduğunu ilettiği anda uygulama değişkenlerinden biri olan CacheReady anahtarı bir değer artırılacak ve istemci uygulaması isteklere cevap vermeye başlayacaktır. (Interlocked)

Burada yukarıda belirttiğimiz gereksinimi tetikleyecek yapıyı basitçe Discovery.Hosting projesi DiscoveryController içerisinde tanımladığımız:

    [HttpGet(nameof(CacheReady))]
    public async Task CacheReady()
    {
        var webSocketContext = new WebSocketMessageContext {
        Command = WebSocketCommands.DataSend,
        Header = new RouteValueDictionary(new { CacheReady = Globals.CacheReady }),
        Value = new Context
        {
            Keys = new string[] { nameof(SimpleCacheItem) }
        }};

        await _connectionManager.BroadcastAsync(webSocketContext);
    }

metot yardımı ile sağlayabiliriz. Bu metot localhost:5916/api/Discovery/CacheReady adresine yapılacak HTTP GET isteği ile tetiklenip bağlı tüm istemci uygulamalarına ön bellek sunucusunun hazır olduğu bilgisini göndermiş olacaktır. Böylelikle istemci uygulamasında ProxyWebSocketCommandInvocator tipi InvokeAsync metodu içerisinde gerekli işlemleri yapabileceğiz.

Peki www.netcore.com adresinden hizmet veren uygulamamıza ön bellek sunucumuz hazır olana dek tüm isteklere yükleniyor çıktısı verebilecek yapıyı nasıl kazandırabiliriz. Benim aklıma hemen Middleware geliyor :) şu şekilde:

AcceptanceMiddleware tipi sayesinde uygulama; ön bellek sunucusu hazır olana dek tarayıcıya "Uygulama Bileşenleri Yükleniyor" şeklinde bir HTML metin ve HTTP 423 - Locked durum kodu döndürecek ayrıca kullanıcı tarayıcısını kapatana veya HTTP 200 - Ok cevabı alana dek bu süreç devam edecektir.

Bu yaklaşım ile özellikle lock veya döngüler aracılığıyla atomik operasyonlar yazmak zorunda kalmadan ilerleyebilir, sistem bağımlılıklarını dağıtabilir ve daha performanslı uygulamalar elde edebiliriz.

Dağıtık bir mimaride istemciler farklı amaçlar için farklı noktalarda bulundurduğumuz (API, entegrasyon, ön bellek, dosya, indeks vb.) sunuculara isteklerde bulunabilir ve kuralları işletebilirler. Tam bu noktada gerekli servis ve sunucuların kaynak durumu ve sağlığı :), indeks veya ön bellek durumunda meydana gelen değişikliklerin (Cache Invalidate vb.) istemcilere bildirilmesi ve güncellemelerin sağlanması ön plana çıkmaktadır. Bununla birlikte; mimarinin daha esnek ve sağlam çalışabilmesi için hangi uç noktaya istekte bulunacağını bilmesi, hizmet dışı kalmış veya çok yüklenilmiş bir servisten haberimizin olması büyük önem taşımaktadır.

Bundan sonrası sizlerin hayal gücü artık :) Faydalı olması dileğiyle. Örnek uygulama için Github

NetCoreStack WebSockets kütüphanesi için: