Makale Özeti

Microsoft’un “Velocity” kod adıyla duyurduğu distributed cache sağlayan alt yapısı ile veri kaynağına gereksiz ve maliyetli çağrılar yapmak yerine dağıtık ortamlarda da caching yapabiliyor olacağız.

Makale

Microsoft’un Distributed Cache Çözümü : Velocity

Microsoft’un “Velocity” kod adıyla duyurduğu distributed cache sağlayan alt yapısı ile veri kaynağına gereksiz ve maliyetli çağrılar yapmak yerine dağıtık ortamlarda da caching yapabiliyor olacağız.

Günümüzde kurumsal iş uygulamalarında caching yapmak neredeyse bir zorunluluk halini almıştır. Bir çok alt yapı tasarımında veri kaynağı katmanına (yani database sunucusu) erişmeden önce arada bir cache katmanı yer alır, erişilmeye çalışılan verinin cache’de bulunup bulunmadığına bakılır ve ardından gerçek veri kaynağına erişilir. Cachelenmesi gereken verinin niteliğine göre cache yapısının önemi artmaktadır. Örneğin sadece veri kaynağından alınıp okunan ve ön yüz katmanına geri geçilen bir veriden ziyade farklı veri kaynaklarından aldığı veriyi önce işleyen, iş kurallarından geçiren ve ardından ön yüz katmanına dönen bir yapıda cache’in önemi çok kritik olmaktadır.

Yoğun kurumsal iş uygulamalarında bir web sitesini sunan ya da bir web uygulama sunucusu rolünde çalışan bir sunucu her zaman yeterli olmamaktadır. Ve bu durumda Load Balancing çözümlerine gidilmelidir. En basit haliyle bir web uygulamasını sunan web sunucusun yanına aynı görevi üstlenen ikinci bir web sunucu konması ve ikisinin gelen yüke göre isteklere cevap verme senaryosudur.

Load Balancing ile çalışan iki uygulama sunucunun gösterimi.

Yukarıdaki şekilde de gözüktüğü gibi böyle bir senaryoda caching işlevi yapılırken iki uygulama sunucusunun ortak olarak paylaşacağı bir çözüme gerek vardır. Zira örneğin birinci sunucu üzerinde ASP.NET alt yapısı tarafından sağlanan caching nesne modeli ya da static olarak tanımlamış bir yerde veri saklanıyor olsaydı, ikinci sunucu aynı veriye erişemeyecekti. Aynı şey ikinci sunucunun cachelediği verilere birinci sunucunun da erişememesi şeklinde olacaktır.

Böyle bir senaryoda eğer cachelenecek verinin niteliği izin veriyorsa her uygulama sunucu üzerinde bir cache alanı kalmasına izin verilebilir ve bu şekilde standart yöntemler ile çalışılır. Ancak her zaman cachede saklanacak veri bu nitelikte değildir. Örneğin aynı istemciden gelen isteğe ilk adımda birinci sunucu cevap verdi ve cache alanına o istemcinin isteği sonucunda bazı veriler eklendi. Sonraki isteklerde aynı istemciden gelen isteğin aynı sunucu üzerinde çalışacağı garantisi yoktur. Bu durumda distributed caching yapmak gereklidir.

Load Balancing donanım ya da yazılımlarında kullanıcı oturumlarının sürekli aynı sunucu üzerine yönlendirilmesi şekliden bir ayar bulunmaktadır. Ancak bu oturum mantığında çalışan bir web sitesinde işe yarar. SingleCall çalışan bir uygulama sunucusunda işe yaramayacaktır. Senaryomuzun aldığı son hale göre ;

1.       Uygulama sunucularından birisinin cache sunucusu olarak belirlenebilir.

2.       Ayrı bir cache sunucusu eklenmesi uygun olabilir.

3.       Yada her uygulama sunucusu üzerinde cache alanı olması ancak birisinde yapılan güncellemelerin diğer uygulama sunuculara da yansıması sağlanabilir.

Yukarıda yazdığım çözümler kurduğumuz basit senaryo için yeterlidir. Velocity henüz RTM sürümüne ulaşmadı, RTM olduğunda tüm distributed caching senaryolarına en yüksek performansı sunacağı geliştirenler tarafından söylenmektedir.

Yukarıdaki senaryolardan ayrı bir cache sunucusu belirlenmesi ve tüm uygulama sunucularının bu cache sunucusunu kullanması senaryosunu bu makalemde çözüme kavuşturmaya çalışacağım.

Bu durumda uygulama sunucuları cache sunucusu için birer istemci rolünde çalışmaktadırlar. Bir uygulama sunucusunu cache sunucusu ile birlikte çalışır hale getirmeden önce Velocity’nin bileşenlerini tanıyalım. Velocity ile bir cache sunucusunu nasıl yapılandırıp ayağa kaldıracağımızı inceleyelim.

Velocity Bileşenleri

Velocity sunucusu aşağıdaki şemada gözüktüğü bir Windows servisi olarak çalışabilmektedir. Aynı zamanda birden fazla cache sunucusu da birlikte çalışabilmektedir. Birden fazla cache sunucusu olduğunda uygulanacak yapılandırma ve seçilecek topoloji önem arz etmektedir. Bu makale üzerine oluşacak ilgi üzerine başka bir makalede birden fazla cache host yani cache sunucusu ile bir cache cluster oluşturulmasını inceliyor olabiliriz.

Velocity -fiziksel olarak ayrılmış- bileşenleri

Cache Host: Velocity’nin servisidir, bir Windows servisi olarak çalışabilir. Cache Host bu makale yazıldığı sırada henüz ilk CTP sürümü üç gün önce yayınlanmış olan Velocity’nin bu sürümünde bir sunucu üzerinde sadece bir tane cache host çalışmaktadır.

Cache Cluster: Birden fazla cache hostun veriyi saklamak ve dağıtmak üzere birlikte çalışmasına cache cluster denilir. Tüm cache cluster, cluster içinde yer alan cache hostlardan bazıları yönetirler. Bu görevi üstlenen cache clusterlarına quorum adı verilir. Bu cache host’ların esas görevi tüm clusterin görevini devam ettirebilmesini sağlamak ve verinin cluster içinde yer alan cache hostlar arasında güncel kalmasını ve değişen topolojiye göre routing tableların güncellenmesidir.

ClusterConfig.xml : Bunların yapılandırılması ve kullanılması yine ClusterConfiguration dosyası ile yapılır.

Cache Administration Tool: Tüm cache clusteri yönetmek için bu araç kullanılabilir. Kurulum gerçekleştirildiğinde masaüsütnde bir kısayol oluşturulacaktır. Ancak standart kurulumda C:\Program Files\Microsoft Distributed Cache\V1.0\AdminTool.exe konumuna yüklenecektir. Aynı cluster için yer alan cache hostlar üzerindeki cache nesnelerini, onlara yapılan istekleri, cache regionlarını listeler. start cluster komutu ile tüm clusterdaki cache hostları çalıştırabilir, stop cluster ile durdurabilirsiniz.

 

Velocity Distributed Cache hizmetinin kurulması.

Velocity kurulumunu indirip çalıştırdıktan sonra kurulum sırasında Configuration konumu için aşağıdaki ekran görüntüsüne gözüken pencere açılacaktır. Cluster Configuration Share olarak girilen değeri yukarıdaki grafikte gözüktüğü şekilde birden fazla cache server olacak şekilde kullanmak isterseniz bir network konumu girebilirsiniz. Ben tek  cache host çalıştıracağım bu yapılandırma için disk üzerinde bir path seçtim. (C:\Users\CengizHan\Documents\VelocityCacheHost)

 

 

İlk Uygulama  - Distributed Cache Servisine veri yazmak ve okumak.

Öncelikle kurulduğunda otomatik olarak başlatılmayan servisi çalıştırmalıyız. Bunun için computer managementden servisi çalıştırmamız gerekir.

Yukarıdaki resimde gözüktüğü gibi cache host üzerindeki ilgili servisin adı Microsoft project code named “Velocity” dir.

Bunun yerine administration tool aracılığı ile de servisi başlatabiliriz.

 

Start cluster komutu ile cache cluster içinde yer alan tüm cache hostlar üzerinde ilgili servisi çalıştırmış oluruz. (Şimdiki örneğimizde tek cache host, tek bilgisayar bulunuyor.)

Cache sunucusuna veri yazma ve okumanın en temel fonksiyonlarını görmek adına bir windows forms application projesi açalım ve aşağıdaki resimde gözüken assembly dosyalarını referans olarak ekleyelim.

C:\Program Files\Microsoft Distributed Cache\V1.0 konumunda yer alan CacheBaseLibrary.dll, ClientLibrary.dll, FabricCommon.dll, CASBase.dll ve CASClient.dll dosyaları referans olarak eklenmelidir.

Ardından projemize bir app.config dosyası ekleyelim. App.config içeriğini aşağıdaki şekilde oluşturalım.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="dcacheClient" type="System.Configuration.IgnoreSectionHandler" allowLocation="true" allowDefinition="Everywhere"/>
    <section name="fabric" type="System.Fabric.Common.ConfigFile, FabricCommon" allowLocation="true" allowDefinition="Everywhere"/>
  </configSections>
  <dcacheClient deployment="simple" localCache="false">
    <hosts>
      <host name="CHAN" cachePort="22233" cacheHostName="DistributedCacheService"/>    </hosts>
  </dcacheClient>
  <fabric>
    <section name="logging" path="">
      <collection name="sinks" collectionType="list">
        <customType className="System.Fabric.Common.EventLogger,FabricCommon" sinkName="System.Fabric.Common.FileEventSink,FabricCommon" sinkParam="CacheClientLog" defaultLevel="1"/>
</collection>
    </section>
  </fabric>
</configuration>

Yukarıdaki yapılandırma dosyasında kalın olarak vurgulanmış olan bölüm bağlanılacak olan cache hostun bilgilerini içerir. Cache host yapılandırma dosyasında verilmiş olan cacheHostName, cachePort ve bağlanılacak cache host’un çalıştığı bilgisayarın adıdır.

Fabric elementi altında verilmiş olan loglama seçenekleri loglama işlemlerini belirler. Loglama için eventloga yazma gibi farklı logger sınıflarıda mevcuttur.Bu örnekte sadece FileEventSink kullanılarak dosyaya loglama yapılmaktadır.

Ayrıca burada verilmiş olan CacheClientLog değeri logların hangi isimdeki dosyaya yazılacağını belirler. Burada uygulama ile aynı klasörde CacheClientLog.log dosyası oluşturulacağı belirlenmiştir.

Örneğin defaultLevel olarak belirlenmiş olan loglama seviyesinde daha az detaylı loglama yaparken. Verbose değerine karşılık gelen 16 değeri ile çok daha detaylı loglama yapmaktadır.

Örneğin cache hosta erişilemediği (bilinçli olarak bilgisayar adını yanlış yazarak elde ettim bu hatayı) durumda aşağıdaki hata logları defaultLevel=1 iken elde edilmiştir.

DeadServerCallback Called, Server URI: net.tcp://CENGIZHANPCasdf:22233/DistributedCacheService,ThinClientCache,Error,2008-6-13 00:30:04.109

DeadServerCallback: Matches My Server, Cleaning Pending Requests,ThinClientCache,Error,2008-6-13 00:30:04.109

ExecuteAPI: Response is Null, msgId = 1,CacheAPI,Error,2008-6-13 00:30:04.115

 

defaultLevel değerini aşağıdaki gözüktüğü gibi 16 yaparak deneyelim.

        <customType className="System.Fabric.Common.EventLogger,FabricCommon" sinkName="System.Fabric.

Common.FileEventSink,FabricCommon" sinkParam="CacheClientLog" defaultLevel="16"/>

Bu durumda aşağıdaki log dosyası oluşmaktadır.

GetCache: CacheFactory not initialized...Initializing,CacheFactory,Verbose,2008-6-13 00:31:40.342

InitCacheFactory: Initializing Cache Factory,CacheFactory,Verbose,2008-6-13 00:31:40.346

InitCacheFactory: Instantiated Configuration Manager,CacheFactory,Verbose,2008-6-13 00:31:40.361

InitCacheFactory: Deployment Mode Read as ,CacheFactory,Verbose,2008-6-13 00:31:40.361

InitCacheFactory: Instantiated Cache List,CacheFactory,Verbose,2008-6-13 00:31:40.361

InitCacheFactory: CacheFactory Initialization Completed,CacheFactory,Verbose,2008-6-13 00:31:40.396

GetCache: Creating the Named Cache 'default',CacheFactory,Verbose,2008-6-13 00:31:40.397

GenericCache: Constructing Cache: default,CacheAPI,Verbose,2008-6-13 00:31:40.398

GenericCache: Instantiating Hashtable First Time,CacheAPI,Verbose,2008-6-13 00:31:40.398

Initializing First Time,ThinClientCache,Verbose,2008-6-13 00:31:40.400

ExecuteAPI: Begin: RequestId = 1, ReqType = PUT, CacheName = default, RegionName = , Key = uye,CacheAPI,Verbose,2008-6-13 00:31:40.490

SendMsgAndWait: Begin: msgId = 1,ThinClientCache,Verbose,2008-6-13 00:31:40.492

SendMsgAndWait: Serializing Object: msgId = 1,ThinClientCache,Verbose,2008-6-13 00:31:40.492

PrepareWait: Preparing For Wait, msgId = 1,CacheAPI,Verbose,2008-6-13 00:31:40.494

PrepareWait: Populating Hashtable, msgId = 1,CacheAPI,Verbose,2008-6-13 00:31:40.495

SendMsgAndWait: Sending Msg to Server: msgId = 1,ThinClientCache,Information,2008-6-13 00:31:40.499

DeadServerCallback Called, Server URI: net.tcp://CENGIZHANPCasdf:22233/DistributedCacheService,ThinClientCache,Error,2008-6-13 00:31:43.090

DeadServerCallback: Matches My Server, Cleaning Pending Requests,ThinClientCache,Error,2008-6-13 00:31:43.090

ExecuteAPI: Response is Null, msgId = 1,CacheAPI,Error,2008-6-13 00:31:43.095

 

defaultLevel değerine verebileceğiniz değerler kümesini System.Diagnostics.TraceEventType enumaration’a bakarak görebilirsiniz.

 

Veri yazmak ve okumak

Veri yazmak ve okumak için artık nesne modeline bakabiliriz. Bunun için aşağıdaki şekilde bir form oluşturup, cache yaz butonu ile veri yazmayı, ve cache oku ile veriyi okumayı deneyeceğiz.

Veri olarak basitçe string bir ifade yazmak yerine bir nesne oluşturup onu yazıp okumayı deneyelim.

[Serializable]

    public class Uye {

        public Uye(string adSoyad) {

            this.AdSoyad = adSoyad;

        }

        public string AdSoyad { get; set; }

    }

Velocity ile cacheleyeceğiniz nesnelerin serializable olarak işaretlenmiş olması zorunludur. Bu yüzden oluşturduğumuz sınıfın üzerinde serializable attribute’unu ekliyoruz.

Daha sonra butonlara tıklandığında çalışmak üzere aşağıdaki kodları yazıyoruz.

private void button1_Click(object sender,EventArgs e) {

            CacheFactory cf = new CacheFactory();

            Cache cacheDefault = cf.GetCache("default");

            cacheDefault.Put("uye",new Uye(textBox1.Text));

        }

        private void button2_Click(object sender,EventArgs e) {

            CacheFactory cf = new CacheFactory();

            Cache cacheDefault = cf.GetCache("default");

            Uye uye = (Uye)cacheDefault.Get("uye");

            if(uye != null) {

                textBox2.Text = uye.AdSoyad;

            } else {

                textBox2.Text = "<null>";

            }

        }

Yukarıda da gözüktüğü gibi CacheFactory’nin GetCache metodu ile Cache nesnesini elde edip, Get ve Put gibi metodlar ile istediğimiz anahtar ile veriyi kaydedip, kaydedilmiş veriyi okuyabiliyoruz.

Burada “default” olarak verilen cache ismi cache host üzerinde ClusterConfig.xml de belirlenmiş olan cache adıdır.

 

Administration tool aracılığı ile sunucu üzerindeki cachelenmiş veriler ile ilgili bilgilere erişebilirsiniz.

list cache ile cluster üzerindeki cache host bilgilerine eriştik. Ardından show hoststats ile cache host üzerindeki toplam cache verisi, toplam yapılan istek gibi istatistiki ve cache host durumunu gösteren bilgilere erişebiliyoruz.

Bu uygulamayı daha sonra aynı networkde yer alan farklı bilgisayarlardan da çalıştırıp ortak bir cache alanına sahip olduğunuz gözlemleyebilirsiniz.

Velocity’nin ASP.NET ve IIS üzerinde host edilen uygulama sunucusu rolündeki servisler ile entegrasyonu ile ilgili başka makalelere kadar konu ile ilgili çalışmalarınızla ilgili geri bildirim almaktan mutluluk duyacağım.