Categories: .NETNoSQL

Docker Üzerine Cassandra Kurulumu ve .Net ile Giriş

Merhaba arkadaşlar.

Bir önceki makalemde bir süredir Messaging yapıları üzerinde çalıştığımdan bahsetmiştim sizlere. Yine aynı şekilde bazı ihtiyaç ve kararlardan dolayı, NoSQL olarak Apache dünyasından Cassandra kullanmamız gerekti. Bu makalemde ise sizlere biraz geç de olsa Docker üzerinde Cassandra kurulumundan bahsedip, .Net üzerinde bir örnek gerçekleştireceğim.

Nedir bu Apache Cassandra?

Geliştirilmesine ilk olarak Facebook tarafından başlanıp sonrasında ise open-source bir hale gelerek, Apache tarafından devam edilmiş/edilmektedir. Özellikle yüksek ölçeklenebilirliğe ve yüksek performanslı bir distributed NoSQL mimarisine sahip olması dikkat çekmektedir.

Sorgulama dili ise CQL(Cassandra Query Language) olarak adlandırılıp, SQL’e çok benzemektedir. eBay, GitHub, Instagram, Netflix gibi hacimli siteler tarafından da kullanılmaktadır. Bir başka güzel yönü ise yüksek ölçeklenebilirliği sayesinde petabaytlarca veriyi tutabilirken, saniyeler içerisinde binlerce işlemi de gerçekleştirebilmektedir. Bunlara ek olarak doğrusal ölçeklenebilirliğe(fast linear-scale) de sahiptir. Yani -n sayıda node ile saniyede 200.000 işlem yapabilirken, sisteme bir node daha eklendiğinde ise işlem kapasitesi de artmaktadır.

Diğer bir güzel yanı da always on architecture’a ve tek kırılma noktasının olmaması(no single point of failure) ile sunucudaki herhangi bir hatadan dolayı tüm kümelerin çalışmasını engellememektedir. Çünkü klasik master/slave veya replication yapılarını kullanmaz. Verileri tüm node’lara dağıtmaktadır ve bu tüm node’lar, gelen request’lere response verebilecek şekilde tasarlanmıştırlar. Bu sayede herhangi bir node’da yaşanan sorun, tümünü etkilememektedir.

Cassandra’nın genel olarak şöyle özelliklerine bir bakmak gerekirse eğer:

  • Open source bir projedir
  • Elastic scability özelliği ile ihtiyaç duyulduğu noktada kapasite arttırılmasına kolayca uyum sağlamaktadır
  • Distributed bir yapıya destek verdiği için, veriyi istenilen kadar makine üzerinde genişletmeye imkan vermektedir
  • Transaction desteği olarak AID(Atomicity, Isolated, Durability)’i desteklemektedir
  • Bunlara ek olarak birde operational simplicity dedik mi, tadından yenmez kanımca

Consistency

Transaction içinde AID desteğini garanti ettiğini söylemiştik. Consistency konusunda ise opsiyonel bir seçenek sunar. Yani bu bizim ilgili veriyi nasıl okumak/işlemek istediğimizle alakalı bir durum. Örneğin response’u tüm node’lardan okuyarak al veya birkaçından okuyarak şu şu işlemi gerçekleştir gibi opsiyonel seçenekler mevcuttur.

Kurulum

Cassandra hakkında bu giriş bilgilerinden sonra dilerseniz kurulumuna geçelim. Kurulumu oldukça kolaydır ve farklı platform’lara destek vermektedir. Ayrıca Java ile yazıldığı için bilgisayarımızda java ortamının kurulu olması gerekmektedir. Download işlemlerini buradan gerçekleştirebilirsiniz.

Ben kurulum işlemini Docker üzerinden linux ortamında gerçekleştireceğim. Eğer sizde Docker üzerinden işlem yapmak isterseniz buradan ilgili işletim sisteminize göre olan versiyonu seçip, straightforward bir şekilde kurulumu gerçekleştirebilirsiniz.

Docker Quickstart Terminal’i açtıktan sonra

docker pull cassandra:latest

komutu ile güncel cassandra image’ini Docker Hub üzerinden sisteme çekelim.

Image’i sisteme çektikten sonra aşağıdaki komut ile container’ı çalıştıralım ve broadcast adresi olarak da “192.168.99.100” ip adresini kullanalım.

docker run --name cassandra -v /Users/MyProjects/scripts/:/script -d -p "9042
:9042" -e CASSANDRA_BROADCAST_ADDRESS=192.168.99.100 cassandra:latest

Bu komutun çalışmasından sonra, Cassandra’ya bağlanmak için hazır durumdayız.

Key Terms

Database işlemlerine başlamadan önce kafa karışıklığına sebebiyet vermemek için, Cassandra ile Relational Model’lerdeki kelime anlamlarına bir bakalım.

Relational Terms Cassandra Terms
Database Keyspace
Table Column Family
Row Record
Column Column

Cassandra GUI Kullanımı

.Net tarafındaki değişiklikleri görebilmemiz için şimdi bize bir GUI lazım. Forumlarda biraz araştırma sonucu en yaygın olarak tercih edilen database tool’u olan DBeaver‘ı kullanmaya karar verdim. Free bir database tool’u olan DBeaver, multi-platform bir universal client’dır. Enterprise sürümünü buradan indirebilirsiniz.

DBeaver’ın kurulum işlemlerini gerçekleştirdikten sonra, açılan ilk ekrandan yeni bir connection oluşturalım. Bunun için connection type’ı “Cassandra CQL” olarak seçelim. Ardından “next” tuşuna basarak “host” adresi olarak daha önce broadcast olarak belirlediğimiz “192.168.99.100” adresini verelim ve “port” u “9042” olarak sabit bırakalım. Bu işlemlerin ardından ise “Test Connection” butonuna basarak her şeyin sorunsuz olduğundan emin olalım.

Connection işlemi başarılı bir şekilde gerçekleşti. Test işlemi ardından tekrardan “next” butonuna basarak işlemimizi tamamlıyoruz. Connection başarılı bir şekilde tanımlandı ve “Keyspaces” yani database listesi aşağıdaki gibi listelenmiştir.

GUI hazır olduğuna göre hemen bir adet test keyspace’i ekleyelim. Bunun için yeni bir SQL Editor açalım ve aşağıdaki sorguyu execute edelim.

CREATE KEYSPACE TestDB 
WITH REPLICATION = 
{ 'class' : 'SimpleStrategy', 'replication_factor' : 1 };

Not: Burada bulunan “WITH REPLICATION” satırı ile verinin kaç kopyasının yazılacağını ve “class” property’si ile de, verinin kopyalarının hangi node’lara yazılacağını replication strategy ile belirlemektedir. Burada iki adet strategy almaktadır. Bunlar özetle:

  • SimpleStrategy: Bu seçenek seçildiğinde cluster içerisindeki node’lar “replication factor” sayısı kadar, sequentially bir şekilde saat yönünde yazılarak devam eder. (Tek bir data center olduğunda kullanılması gerekmektedir)
  • NetworkTopologyStrategy: Bu seçenek ise multiple data center’a sahip olunduğunda tercih edilmelidir. Bu strategy ile temel olarak her bir data center’da ne kadar replica’nın istenildiği belirlenebilmektedir.

Execute işleminden sonra sol tarafda bulunan Database Navigator’ı refresh yaptıktan sonra oluşturmuş olduğumuz “TestDB” keyspace’inin geldiğini görebiliriz.

 

Test keyspace’ini oluşturduktan sonra içerisine bir adet de column family yani yeni bir table ekleyelim. “Products” column family’sini ekleyebilmek için aşağıdaki komut satırını kullanalım.

CREATE COLUMNFAMILY Products (
Id int,
Name varchar,
Quantity int,
PRIMARY KEY (Id))

Yukarıdaki kodu execute ettikten sonra “products” column family’si aşağı görseldeki gibi “testdb” altında ki, “Tables” içerisine eklenecektir.

.Net Tarafına CRUD İşlemleri

Diğer işlemler için artık .Net tarafına geçebiliriz. “CassandraSample” isminde yeni bir console application oluşturalım ve NuGet Package Manager üzerinden “CassandraCSharpDriver” paketini indirelim.

CassandraCSharpDriver paketi Datastax isimli firma tarafından geliştirilmekte olup, hakkında daha fazla bilgiye buradan ulaşabilirsiniz.

“CassandraCSharpDriver” client’ında context olarak “ISession” interface’i üzerinden ilerlemektedir. Bu interface’i initialize edebilmek için ise “Cluster” class’ının builder’ı kullanılarak, docker üzerindeki cassandra endpoint’ini girmemiz ve build etmemiz gerekmektedir. Build işleminin ardından elde ettiğimiz “Cluster” objesi ile, “Connect” method’unu kullanarak, daha önce oluşturmuş olduğumuz “keyspace” e bağlanıyoruz ve “ISession” interface’ini initialize etmiş oluyoruz. Dilerseniz kod üzerinden bir bakalım.

Cluster cluster = Cluster.Builder().AddContactPoint("192.168.99.100").Build();

ISession session = cluster.Connect("testdb");

Artık işlemlerimize “session” objesi üzerinden devam edeceğiz. Haydi bir kaç CRUD işlemi gerçekleştirelim.

using Cassandra;
using System;
using System.Linq;

namespace CassandraSample
{
    class Program
    {
        static void Main(string[] args)
        {
            Cluster cluster = Cluster.Builder().AddContactPoint("192.168.99.100").Build();

            ISession session = cluster.Connect("testdb");

            InsertNewRow(session);
            GetRowById(session, 1);
            UpdateRowById(session, 1, "Iphone 7 Gold", 400);
            GetRowById(session, 1);
            DeleteRowById(session, 1);

            Console.ReadLine();
        }

        private static void InsertNewRow(ISession session)
        {
            session.Execute(@"insert into products (id, name, quantity)
                              values (1, 'Iphone 7 Plus', 500)");
        }

        private static void GetRowById(ISession session, int id)
        {
            var result = session.Execute($"select * from products where id={id}").First();

            Console.WriteLine("Name: {0} Quantity: {1}", result["name"], result["quantity"]);
        }

        private static void UpdateRowById(ISession session, int id, string name, int quantity)
        {
            session.Execute($"update products set name='{name}', quantity={quantity} where id={id}");
        }

        private static void DeleteRowById(ISession session, int id)
        {
            session.Execute($"delete from products where id={id}");
        }
    }
}

CQL sorguları ne kadar da SQL sorgularına benziyor değil mi? Evet CRUD işlemleri bu kadar basit. “session” objesinin “Execute” method’u kullanılarak, inline olarak CQL sorgularını yazıyoruz. Dilerseniz uygulamayı bir çalıştıralım ve sonucuna bakalım.

İlk önce “Iphone 7 Plus” isimli product’ı insert işlemini gerçekleştirdik ve ardından “GetRowById” method’u ile console’a yazdık. Ardından “UpdateRowById” method’u ile de product’ın name’ini “Iphone 7 Gold” ve quantity’sini 400 olarak güncelledik.

Peki hepsi bu kadar mı? Tabi ki hayır. Bir örnekte simple mapping üzerine gerçekleştirelim.

private static void SimpleMapping(ISession session, int id)
{
    IMapper mapper = new Mapper(session);

    // Fluently bir şekilde entity'leri map'liyoruz.
    MappingConfiguration.Global.Define(
        new Map<Product>()
            .TableName("products")
            .PartitionKey(p => p.Id));

    // Daha fazla örnek kullanım için: http://datastax.github.io/csharp-driver/features/components/mapper/#mapper-api-example
    var product = mapper.First<Product>("select * from products");

    Console.WriteLine("Name: {0} Quantity: {1}", product.Name, product.Quantity);
}

Burada ise “Mapper” objesini yine “session” üzerinden itinialize ediyoruz. Fluently bir şekilde “Product” entity’sini “MappingConfiguration” üzerinde map’liyoruz. Mapping işlemlerinden sonra ise oluşturmuş olduğumuz “mapper” context’i üzerinden “mapper.First<T>” method’unu kullanarak, içerisine girmiş olduğumuz CQL sorgusu sonucunda result’ın mapping işlemini gerçekleştiriyoruz.

Bu ve buna benzer daha fazla Fluent method’lar için ise, buraya bir göz atabilirsiniz.

Ne zaman tercih edilmeli?

Yukarıda vermiş olduğumuz bilgilerin ardından konuyu bir toparlamak gerekirse:

  • Değişken ve büyük verilerin saklanılması gerektiğinde
  • Bu büyük veriler üzerinde yüksek performans gerektiren read/write işlemlerinin yapılması gerektiğinde
  • High availability söz konusu olduğunda
  • ve günümüz teknolojisinde en temel concern’lerden birisi olan scalability söz konusu olduğunda

gibi bir çok nedenlerden dolayı tercih edilebilir.

Umarım keyifli bir yazı olmuştur. Örnek projeye ekten erişebilirsiniz.

Takipte kalın…

cassandrasample

Gökhan Gökalp

View Comments

  • Yazı için teşekkür ederim Gökhan Bey, şöyle bir senaryo için cassandra kullanmak ne derece uygundur.
    Elimde yüksek miktarda resim ve bunlara ait text formatta etiket verisi var, bu veri üzerinde sürekli olarak read/write işlemi uygulanıyor herhangi bir os file system istenilen performansı vermiyor, bu durumda cassandrayı tercih etmek neler kazandırır/kaybettirir. Şimdiden teşekkür ederim.

    • Merhaba, ne derece büyük veriniz var ve bu resimleri nasıl persist ettiğinizi bilmiyorum. Cassandra özelinde değil de, sadece read/write işlemi yapmanız gerekiyor ise text formattaki etiket verileriniz için, size herhangi bir key-value store NoSQL çözümü yararlı olacaktır.

  • Son zamanlsrda cok karsilasiyorum bende duzgun detayli turkce bir yazi yok gercekten. Bir yazi gelirse mukemmel olur

    • Merhaba, teşekkür ederim öncelikle yorumunuz için. Uygun bir vakitte değerlendirmeye çalışacağım Docker hakkında bir kaç yazı.

Recent Posts

Event-Driven Architecture’larda Conditional Claim-Check Pattern’ı ile Event Boyut Sınırlarının Üstesinden Gelmek

{:en}In today’s technological age, we typically build our application solutions on event-driven architecture in order…

2 ay ago

Containerized Uygulamaların Supply Chain’ini Güvence Altına Alarak Güvenlik Risklerini Azaltma (Güvenlik Taraması, SBOM’lar, Artifact’lerin İmzalanması ve Doğrulanması) – Bölüm 1

{:tr}Bildiğimiz gibi modern yazılım geliştirme ortamında containerization'ın benimsenmesi, uygulamaların oluşturulma ve dağıtılma şekillerini oldukça değiştirdi.…

9 ay ago

Identity & Access Management İşlemlerini Azure AD B2C ile .NET Ortamında Gerçekleştirmek

{:tr}Bildiğimiz gibi bir ürün geliştirirken olabildiğince farklı cloud çözümlerinden faydalanmak, harcanacak zaman ve karmaşıklığın yanı…

1 yıl ago

Azure Service Bus Kullanarak Microservice’lerde Event’ler Nasıl Sıralanır (FIFO Consumers)

{:tr}Bazen bazı senaryolar vardır karmaşıklığını veya eksi yanlarını bildiğimiz halde implemente etmekten kaçamadığımız veya implemente…

2 yıl ago

.NET Microservice’lerinde Outbox Pattern’ı ile Eventual Consistency için Atomicity Sağlama

{:tr}Bildiğimiz gibi microservice architecture'ına adapte olmanın bir çok artı noktası olduğu gibi, maalesef getirdiği bazı…

2 yıl ago