Redis Nedir

Daha önceki yazıda Windows üzerinde Redis kurulumunu anlatmıştık ve o yazımızda bahsettiğimiz gibi bu yazımızda StackExchange.Redis kullanarak bir .Net Client projesi geliştireceğiz. Öncelikle pc nizde Redis'in çalıştığından emin olun. Bunun için aşağıdaki gibi bir deneme yapabilirsiniz. 

Redis çalışıp çalışmadığına dair kontrol için redis-cli.exe'yi çalıştıralım ve aşağıdaki resimde olduğu gibi bir key-value tanımlayalım ve sonrasında get set işlemi yapalım

Eğer Redis Server sorunsuz bir şekilde çalışıyorsa projemizi oluşturmaya başlayalım. Öncelikle VS'da bir adet RedisDotNetClientSample isminde bir ConsoleApplication oluşturalım.

StackExchange.Redis Kurulum

Daha sonra projemize Nuget Package Manager Console üzerinden StackExchange.Redis package'ı indirip kuralım.

PM > Install-Package StackExchange.Redis

Connection

StackExchange.Redis ile uğraşıyosanız herhalde en çok dikkat etmeniz gereken class ConnectionMultiplexer. ConnectionMultiplexer Redis Server'a bağlanmanızı sağlayacak olan sınıftır ve instance yönetimi Singleton olarak yapılması önerilir. Çünkü ConnectionMultiplexer fully thread-safe dir ve her bir işlem için tekrar tekrar instance oluşturmamız gerekir. Kurulum işlemi sorunsuz tamamlandıktan sonra aşağıdaki gibi RedisConnectionFactory adında bir class oluşturalım.

    public class RedisConnectionFactory
    {
        static RedisConnectionFactory()
        {
            lazyConnection = new Lazy<ConnectionMultiplexer>(() =>
            {
                return ConnectionMultiplexer.Connect("localhost:6379");//redis server conn string bilgisi, web config'den almak daha doğru ancak şimdilik buraya yazdık
            });
        }

        private static Lazy<ConnectionMultiplexer> lazyConnection;

        public static ConnectionMultiplexer Connection => lazyConnection.Value;

        public static void DisposeConnection()
        {
            if (lazyConnection.Value.IsConnected)
                lazyConnection.Value.Dispose();
        }
    }

Generic ICache Interface

Connection kısmını hallettik şimdi ise Cache tarafını yazalım. Bir tane aşağıdaki gibi ICache interface'i tanımlayalım.

    public interface ICache : IDisposable
    {
        T Get<T>(string key);

        void Set<T>(string key, T obj, DateTime expireDate);

        void Delete(string key);

        bool Exists(string key);
    }

RedisCache Class'ı

Şimdi ise ICache interface'ini implement etmiş RedisCache class'ımızı oluşturalım. Bu class içerisinde tanımlı fonksiyonları kullanarak string objelerimizi json formatında string olarak Redis'e atıyor olacağız.

    public class RedisCache : ICache
    {
        private readonly IDatabase _redisDb;

        //Connection bilgisi initialize anında alınıyor
        public RedisCache()
        {
            _redisDb = RedisConnectionFactory.Connection.GetDatabase();
        }

        //Redis'e json formatında set işlemi yapılan metot
        public void Set<T>(string key, T objectToCache, DateTime expireDate)
        {
            var expireTimeSpan = expireDate.Subtract(DateTime.Now);

            _redisDb.StringSet(key, SerializerHelper.Serialize(objectToCache), expireTimeSpan);
        }

        //Redis te var olan key'e karşılık gelen value'yu alıp deserialize ettikten sonra return eden metot
        public T Get<T>(string key)
        {
            var redisObject = _redisDb.StringGet(key);

            return redisObject.HasValue ? SerializerHelper.Deserialize<T>(redisObject) : Activator.CreateInstance<T>();
        }

        //Redis te var olan key-value değerlerini silen metot
        public void Delete(string key)
        {
            _redisDb.KeyDelete(key);
        }

        //Gönderilen key parametresine göre redis'te bu key var mı yok mu bilgisini return eden metot
        public bool Exists(string key)
        {
            return _redisDb.KeyExists(key);
        }

        //Redis bağlantısını Dispose eden metot
        public void Dispose()
        {
            RedisConnectionFactory.Connection.Dispose();
        }
    }

Projemizin Redis bağlantı adımları ve set ve get işlemlerini yapan kısımları hazır. Şimdi ise yazdığımız kodları test etme adımı var. 

Örnek bir obje tanımlayıp bu objeyi redis'e atıyor olalım. Bunun için aşağıdaki gibi User adında bir class'ımız olsun.

    public class User
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
    }

Program.cs içerisinde aşağıdaki gibi Redis içerisine bir adet User objesi oluşturup get&set işlemi yapalım.

    class Program
    {
        static void Main(string[] args)
        {
            ICache redisCache = new RedisCache();

            var userToBeCached = new User
            {
                Id = 1,
                Email = "canertosuner@gmail.com",
                FirstName = "Caner",
                LastName = "Tosuner"
            };
            var key1 = "caner_key1";

            redisCache.Set(key1, userToBeCached, DateTime.Now.AddMinutes(30));//30 dakikalığına objemizi redis'e atıyoruz

            if (redisCache.Exists(key1))
            {
                var userRedisResponse = redisCache.Get<User>(key1);
            }
        }
    }

Projenizi çalıştırdıktan sonra aşağıdaki görselde olduğu gibi Redis'e atmış olduğumuz objeyi tekrar get işlemini yaptığımızda userRedisResponse değişkenine atanan değerleri görüyoruz.

 

Bu yazı C# ile Redis Cache ve StackExchange.Redis nasıl entegre edilip kullanılır basitçe özetlemekte. Tabiki Redis ile ilgili konuşulacak çok daha fazla konu var ancak elimden geldiğince basit bir şekilde redis client uygulaması geliştirdik.Bazı önemli linkleri aşağıda paylaşıyorum. Redis ile ilgili önemli gelişmeleri bu siteler üzerinden takip edebilirsiniz. 

redis.io 

MS OpenTech – Redis 

StackExchange.Redis 

Redis Desktop 

 

Redis Server Windows Üzerinde Kurulumu ve Kullanımı

Bu yazıda Distributed Caching sistemlerinden biri olan Redis'i inceliyor olacağız.

Redis Nedir ?

Redis için kısaca open source bir NOSQL Memcached veritabanı sistemidir diyebiliriz. Her ne kadar ilk olarak Linux için tasarlanmış olsada ihtiyaç doğrultusunda Windows işletim sistemlerinde de kullanılabilir hale getirildi. Çalışma şekli olarak Key-Value şeklinde gönderilen bilgileri store etmektedir. 

 

Veri Tipleri

Redis verileri String, Hashe, List, Set ve Sorted List olarak saklayabilir.

 Veri tipleri ile ilgili daha ayrıntılı bilgiyi bu linkte bulabilirsiniz. 

Kurulum ve Kullanımı

Öncelikle Redis'i indirip service olarak bilgisayarımıza kuruyoruz. Bunun için bu linkten sizin için uygun olan .rar uzantılı sürümü bulup bilgisayarımıza indiriyoruz. Sonrasında indirmiş olduğunuz dosyalardan redis-server.exe adlı exe'yi çalıştırıp kurulumu yapıyoruz. Default olarak 6379 port'unu hizmete sokar ancak istersek bunu değiştirebiliriz de. Exe çalıştıktan sonra aşağıdaki gibi bir ekran gördüyseniz kurulum OK dir.

Redis çalışıp çalışmadığına dair kontrol için redis-cli.exe'yi çalıştıralım ve aşağıdaki resimde olduğu gibi test amaçlı bir key-value tanımlayıp sonrasında get set işlemi yapalım

Bu yazımızda Windows üzerinde Redis Server nasıl kurulur ve kullanılır bunu gördük. Bir sonraki Redis yazımızda StackExchange.Redis redis client kullanarak NET dilleri için (C# etc) örnek proje yapıyor olacağız.

Server Side Bir Projede Olmazsa Olmazlar || Olursa Güzel Olurlar

Server-side geliştirme yaparken projemizde olursa olmazsa olmaz OR güzel olur diyebileceğimiz bazı modüller&özellikler vardır ve bu özellikler bir çok kitap yazarı, ünlü blogger veya eğitmenler tarafından kabul görmüş özelliklerdir. Yazının başlığına bakacak olursak "Olmazsa Olmazlar" şeklinde ancak yazılım denen şey tabi ki "it depends on the business" duruma göre,ihtiyaca göre geliştirilen bir şey ve "Olmazsa Olmaz" dan kasıt bu zamana kadar hem kendi okuduğum çeşitli makalelerde vurgulanan hemde geliştirmiş olduğum çeşitli projelerde deneyimlediklerimden yola çıkarak 5 farklı önemli özellik var ki bu özellikler gerçekten kurumsal veya büyük çaplı bir projede farkında olarak veya olmadan size oldukça fazla fayda sağlamaktadır.

Bu 5 başlığı sırasıyla yazacak olursak;

  1. Security,
  2. Exception Handling,
  3. Logging,
  4. Response Consistency,
  5. Development Environment

 

1- Security

Bazı bilgiler vardır uygulamanız için veya uygulamayı kullanan kişiler için çok ama çok önemlidir. Örneğin; tckn bilgisi, banka hesap numarası, userId bilgisi, username-password bilgisi vs. bu gibi bilgilere hiç kimsenin ulaşmasını istemeyiz. Günümüzde fiddler, wireshark vb. tool'ları kullanarak client-server arasındaki gidip gelen http paketleri rahatlıkla dinlenmekte ve kötü niyetli bazı kişiler araya girerek giden data'da bulunan tckn,accounNumber,amount vs gibi alanları değiştirip bir çeşit dolandırıcılık yapabilmekteler. Genel de çözüm olarak "ya https yaparız abi hiç bişey olmaz.." şeklinde cümleler kurulup service'i https olarak dışarıya açarlar ve bu tehlikeyi önlediklerini sanarlar ancak artık https bile çeşitli yollar denenerek decrypte edilebilmekte.

Security'den kasıt aslında token based authentication vs. değilde request&response gelip giderken data için uygulanabilecek security. Tabiki de service'e gelecek olan client'ları belli authentication ve authorization kontrollerinden geçirdikten sonra içeriye almak gibi bir çok yöntem mevcut ancak data-trasnfer sırasında belli bazı önlemler alarak projenizin güvenliğini biraz daha atırabilirsiniz. Bunun için çeşitli yöntemler bulunmakta. Property bazında istenilen değeri şifleme veya maskeleme. Örneğin; UserLoginRequest adında bir modeliniz var ve içerisinde bulunan UserName, Password string alanlarını şifreleyebilirsiniz. Diğer bir yöntem ise endpoint'e gelen request ve endpoint'ten çıkan response modellerinizin tamamını şifreleyerek data transferini sağlayabilirsiniz ki bu daha base-oriented bir çözüm gibi duruyor. Örnek bir response örneği aşağıdaki gibi olabilir.

public class BaseResponse
{
	public object Data {get;set;} // şifrelenmiş bir şekilde endpoint'in return ettiği response Data içerisinde gönderilebilir
	public bool IsCrypted {get;set;} her endpoint şifrelenmiş şekilde response dönmeyebilir, client'ı bilgilendirmek adına bir bool alan tutabilirsiniz
}

Not: Checksum'da unutulmaması gereken diğer bir yöntemdir. Checksum kullanarak gelip giden data uzunluğu belli bir algoritma ile şifrelenip çift taraflı kontrol uygulanarak da güvenlik sağlamak işinizi baya bi kolaylaştırabilir.

 

2- Exception Handling

"Hatasız kod olmaz.."

Exception handling her bir proje için oldukça önemli bir konudur. Hatasız kul olmayacağı gibi hatasız kod da olmaz. Exception fırlatabileceğiniz düşündüğümüz yerleri zaten önceden tedbirini alıp handle ediyoruzdur ama asıl önemli olan beklenmedik hataları nasıl handle edeceğimizdir. En yaygın hatalardan biri olan null reference exception hemen hemen bütün projelerde başımıza gelmiş bir exception türüdür ve projeniz bu hatayı fırlattığında client'a http500 internal server error dönecektir. Peki bu tür hataları handle etmek için ne yapmak gerekir ?? Bir çok projede olduğu gibi her yere try catch koyarak kodunuzu spaghetti code haline getirip yönetilmesi zor bir proje haline getirmek heralde yapılabilecek en kolay çözüm. Öyle projeler var ki denk geldiğim adam try catch e bile güvenmeyip iç içe 2 try catch yazmış.

Bu ve benzeri durumlardan sakınmak gerekir. Open-source dünya ile birlikte exception handling için bir çok kütüphaneler-yöntemler mevcut. Projeniz için her yerde kullanabileceğiniz küçük küçük interceptor'lar yazarak exception handling'inizi tek bir yerden yönetebilir ve projenize reuseable fonksiyonaliteler kazandırabilirsiniz. Aspect-Oriented Programming anlayışı aslında en güzel örnek Nuget'te bulunan çeşitli kütüphaneler ile bir kaç dakika içerisinde bu modülleri projenize ekleyebilirsiniz.

Bu gibi yapılar ile çok kolay şekilde projenizi yönetebilir ve tek bir yerden kontrol edilebilir kodlar geliştirebilirsiniz. Böylelikle yarın bir gün exception handling ile ilgili değişiklikler yapmak istediğinizde bu işlem saatlerinizi almak yerine bir kaç dakikada yapabileceğiniz bir geliştirme olacaktır.

 

3- Logging

"Log, uğrunda ölen olmadıktan sonra log değildir.."

Log bir projede her şeydir. Küçük çaplı projeleri baz almazsak log yapısı büyük çapta olan projeler için oldukça hayati önem taşır. Öyle bir an gelir ki log'a attığınız küçücük bir byte size dünyaları kazandırır. Tabi log işlemi yaparken kayda değer şeyler logluyor olmak oldukça önemlidir. Özellikle dışa bağımlı çalıştığınız projelerde yani external bir service call işlemi olan yapılarda gelip giden request & response'ları context halinde logluyor olmak oldukça kolaylık sağlar. Gerek db ye gerekse file log yaparken bütün bu işemleri uçtan uca logluyor olmak ve aralarında ilişkileri bir unique identifier kullanarak loglamak gerekir. Örnek verecek olursak "getAccountList" adında geliştirdiğiniz bir endpoint var ve client'lar bu endpoint'e request atarak hesap listesini alıyor sizde client bu endpoint'e geldiğinde arka tarafta başka bir endpoint'e gidip hesap listesi ile ilgili farklı service call işlemleri yapıyorsunuz. Geliştirmiş olduğunuz log yapısından beklenen, belli bir context olarak uçtan uca hem client'ın request'ini hemde sizin server-side da yapmış olduğunuz diğer servis çağrımlarını logluyor olması gerekir. Bu size şunu kazandırır production'da olan bir müşteri şöyle bir şikayetle müşteri hizmetlerine şikayet bırakabilir "ben saat 11:38'den hesaplarım sayfasını görüntüleyemiyorum, sayfa boş geliyor..." bu gibi durumlarda hemen müşteri numarasından yola çıkarak log'ları kontrol ettiğinizde sorunun sizin geliştirdiğiniz service katmanından değilde dışa bağlandığınız service'den kaynaklandığını kolaylıkla görebilirsiniz. Bu logları internal ve external olarak ayırmakta fayda olabilir zira proje müdürü sizden ilerleyen zamanlarda yüzdelik olarak success ve fail oranlarını isteyebilir yani aslında tutmuş olduğunuz log'lardan yola çıkılarak proje yöneticileri analiz dahi yapmak isteyebilirler. Loglama ile ilgili örnek yazıları buradan göz atabilirsiniz.

Not: Log için çok çeşitli open source kaynaklar bulunmakta. Geliştirmelerinizi loosely coupled olacak şekilde yapıyor olursanız ilerde log yapınızla ilgili kolay değişiklik yapabilir, farklı log yapılarına kolay geçişler sağlayabilirsiniz.

 

4- Response Consistency

Response tutarsızlığı herhalde Türkiye de geliştirilen server-side projelerde ki en büyük sorunlardan biri olabilir. Bir çok defa dış servislere bağlı proje geliştirmiş biri olarak aynı namespace altında olup farklı farklı response'lar dönen durumlarla epeyce bir karşılaştım. Peki projenizde bulunan end-point'lerin döndükleri response'ların tutarlı olması neden önemlidir ? Bu sorunun cevabı hem server-side'da bulunan proje açısından hemde client açısından büyük öneme sahiptir.

Server-side açısından; öncelikle uygulamada exception-handling, logging, caching vs. gibi özellikleri geliştirirken endpoint'lerinizin return ettikleri response'ların bir BaseResponse.cs'den türüyor olması tercih edildiğinde bu gibi operasyonlar için yapılması gereken geliştirmelere harcanan eforu hemen hemen yarı yarıya azaltabilirsiniz. Yapmış olduğunuz BaseResponse.cs içerisinde Error case'leri için de bir property'iniz olması gerekir çünkü client'a farklı akışları olan error-exception mesajları dönebilirsiniz. 

Örnek BaseResponse.cs

public class BaseResponse
{
	public object Data {get;set;} // endpoint'in return ettiği response
	public ErrorModel Error {get;set;} // endpoint'in return ettiği error model
}

Client açısından; yukarıda ki BaseResponse örneğinden yola çıkacak olursak client tarafta geliştirilen network-layer da gelen response açılarak endpoint'in gönderdiği Data içerisinde bulunan gerçek response alınabilir ve sonrasında alt katmanlara gönderilip işlenebilir ve bunu yapabiliyor olmak client yazan arkadaşlar bilirler ki büyük bir nimettir. Servisten hata geldiğinde ise ErrorModel tipinde olan Error property'imiz içerisinde client'a gelen hata mesajı yine network katmanında alınıp base'den hata yönetimi yapılabilen bir layer'a gönderilip gerek pop-up ile hata mesajını gösterme gerekse kullanıcının o hata türünü aldığında uygulamada nasıl bir akışa yönlendirilmesi gibi işlemler yapılmasına kolaylık sağlanabilir.

 

5- Farklı Development Environment

"Dev de çalışıyor ama Test ortamında çalışmıyor..."

Development environment oldukça önemli bir diğer konudur. Farklı ortamlarda geliştirme yapıyor olmak biz developer'lar için eksik konfigurasyon vs gibi durumlarda başımızı ağrıtan bir durum olsada aslında çok önemli bir konudur. Bilindiği üzre Production seviyesinde geliştirme yapmak mümkün değil ama geliştirmekte olduğumuz ürünü prod'a aldığımızda sorunsuz çalışması istenir. Projenizi geliştirirken o an geliştirdiğiniz ortam, test işlemlerinin yapıldığı ortam ve production ortamlarının her birinde farklı konfigurasyonların olduğu server'lar sql'ler vs. bulunur. Geliştirme yaparken bu ortamların yönetimi ve konfigürasyonu oldukça basit ve ortamlar arası geçiş sorunsuz 1-2 hareketle geçilebiliyor olması beklenir. Peki bu development ortamları nelerdir ?

Development
Development ortamı ismindende anlaşıldığı üzre developer'ların geliştirme yaptığı ortam. Bu ortam developer istediğini yapmakta özgürdür diyebiliriz. Developer kritik kararlar alıp anlık değişiklikleri projede deneyebilir çünkü bu ortam onun kendi bireysel ortamıdır diyebiliriz.

Integration
Development ortamında bireysel olarak yapılan geliştirmelerin ortak bir branch'e commit'lenip bütün geliştirmelerin ortak bir alanda gözlemlendiği alan diyebiliriz. Kısaca bu ortamın amacı yapılan işlerin stage'ing den önce combine edilip developer'ların saptayabildiği bir hata varsa hemen fix edilip ürünü görücüye çıkartma şeklinde tanımlayabiliriz.

Staging
Staging Production'dan önceki ortamdır ve sahip olduğu özellikler itibari ile production'ın ile bire bir aynı olması beklenir. Örneğin benzer özelliklere sahip sunucular, sql server'lar, canlıda olan database'in bir gün öncesinin verilerine sahip bir database vs. gibi. Staging ortamına bir çok yerde Pre-Production da denilmekte.

Production
Son adım olarak da Production ortamı artık geliştirmiş olduğunuz projeyi canlıya aldığınız ve son kullanıcının erişimine açtığınız yerdir. Prod'a gelene kadar ki ortamlarda yapılan geliştirmeler sorunsuz-planlı bir şekilde yapıldığı taktirde testler sonucunda ürün prod'a alınır.

 

Yazının başında da belirtiğim gibi "it depends on the business" yani işe bağlı projeye bağlı. Olursa güzel olur diyebileceğimiz bu modülleri projelerinizde farklı şekillerde düşünebilir entegre edebilirsiniz.