SD #2: Scalability 2

Scalability postunda bircok onemli noktaya degindik. Burada sadece birkac ufak ek yaparak, bundan sonraki postta ornek system design'lara gececegiz. 

Cogu public servis bir load balancer arkasinda calisan birden fazla web server tarafindan serv edilmektedir. Bu sayede yuk artisina bagli olarak otomatik horizontal scaling yapmak mumkundur. Bir kullanici birinci requestte webserver #1 tarafindan cevaplanmis olabilir, ikinci requesting de webserver #2 tarafindan cevaplanmis olabilir. Kullanici ilk olarka hangi server ile muhattap oldugundna bagimsiz olarak tum web serverlar tarafindan ayni sekilde requestleri karsilanabilmelidir. Bunu yapabilmek icin:

Altin Kural: Tum webserver'lar ayni kodu calistirmali ve session datasi barindirmamalidir. Yani tamamen stateless olmalidir. Session datasi harici merkezi bir database veya persistent cache'te tutulmalidir. 

Deployment
Burada bir zorluk var. O da webserver kodunda bir guncelleme yaptigimiz zaman nasil deploy edelim ki ayni anda hem eski kod hem de yeni kod calismasin, tum webserver'lar ayni anda guncellensin. Bunu nasil yapabilecegimizi anlamak icin Kubernetes ozelinde deployment stratejilerine bir gozatalim. 

Recreate: Tum webserver'lari sil ve yeni kod sahip olanlari ayaga kaldir. Development icin en iyi strateji belki de bu. Avantaji yeni state direk olarak yansitilmis oluyor, dezavantaji ise downtime yasanmasi. Ama zaten development icin oldugundan sikinti yok. 

Ramped - slow rollout: Bu metotta yenileri ayaga kaldirirken, bir yandan da eskileri indiriyoruz. Yavas bir gecis yasaniyor. Resim cok guzel ozetlemis, maviler eski yesiller yeni webserverlar:


Dezavantajlari: trafik uzerinde bir kontrolumuz yok. trafigin bir kismi yenilere bir kismi da eskilere gidecek. Bu durumda da eski kod ve yeni kod yanyana calisiyor olacak ki bu da problem. Son olarak da zaman alacak bir surec.

Blue/Green deployment: Iste bizim bastaki probleme aradigimiz cozum bu. Bu metotta gerekli yeni webserverlarin hepsini ayaga kaldiriyoruz. Cok iyi bir sekilde test edip saglikli olduklarindan emin olduktan sonra bir anda trafigi eski olanlardan yeni olanlara kaydiriyoruz. 



Avantajlari: En buyuk avantaji ayni anda hem eski hem de yeni kodun calismiyor olmasi. 
Dezavantajlari: Gecis tamamlanana kadar 2 kat fazla resource'a ihtiyacimiz olacaktir. 

Bunu disinda Canary ve A/B Testing gibi iki deployment daha var, onlari suradan gorebilirsiniz. 

Persistence Layer
Tum bunlari yapsak da database noktasinda yine bir bottleneck olacaktir. Burada onumuzde 2 secenek var. 

1- SQL ile devam etmek. Master/Slave ya da Master/Master replication stratejisi uygulanabilir. Bununla beraber uzun vadede Sharding ve Sql Tuning de gerekecektir. 

2- NoSQL ile yola devam etmek. Hatta MySQL kullanarak, en bastan itibaren full denormalizasyon yaparak, NoSQLmis gibi kullanilabilir. Ya da direk olarak MongoDB ya da CouchDB gibi sistemler kullanilabilir. 

Ama eninde sonunda onlar da yavas kalacaktir ve bir Cacheleme mekanizmasi gerekecektir. 

Caching
Temelde 2 metot bulunmaktadir. 

1- DB Sorgularini cachelemek: Bir key-value cahce uzerinde, sql sorgusunun hashlenmis hali cache olacak sekilde DB'den donen sonuc cahce'te saklanabilir. Burada zorluk DB'e bir alan degistigi zaman bu alanin etkiledigi sorgularin da cache'ten silinmesidir. 

2- Objeleri cachelemek: Aslinda webserver ya da herhangi bir uygulamanin kod icerisinde kullandigi sekli ile DB'te ifade edildigi sekli cok farklidir. Sirf bu farklilik yuzunde object oriented diller icin otomatize ORM araclari gelistirilmistir ki, DB'deki kayitlar Class'lar seklinde kod icinde ifade edilebilsin. 

Ayni bu sekilde, cache uzerinde onceden hesaplanarak cache'te Class'lar tutulabilir. Ornegin Product isimli bir class'in cache'teki halini uretmek icin birden fazla Sql query'si calistirmak gerekiyor olabilir. Ancak bu islem ototmatize edilebilir ve olusturulan objeler direk cache'te guncellenir. Bu sayede cok ciddi performans artisi saglanabilir. 

Asenkronizasyon
Aslinda scalability'nin altinda yatan temel motivasyon musteriyi bekletmemek, oyle degil mi? Sonucta 12 saniye response time'i olan bir e-ticaret sitesinin hayatta kalma sansi yoktur.

Benzer sekilde, bazi operasyonlar asenkron hale getirilerek, enazindan kullanicinin deneyiminin iyilestirilmesi saglanabilir. 

1- Zaman alan operasyonlarin onceden yapilmasi. Ornek olarak tum sitenin HTML olarak render edilmesi verilebilir. Her requestte dinamik olarak sql sorgulari yapilarak, sayfa render edilmesi yerine, tum sayfalari onceden render ederek son kullanici acisindan buyuk hiz kazanimi saglanabilir. Ayrica bu sekilde olusturulan HTML dosyalari bir CDN uzerinde static content olarak serv edilebilir ve cok daha fazla performans artisi saglanabilir. CDN'i kisaca hatirlayalim:

CDN: Content delivery network olarak adlandirilan sunucular toplulugudur. Cografi olarak bir icerik kullaniciya ne kadar yakin ise, aradaki latency de o kadar az olacaktir. Bu gercek goz onunde bulundurularak, degismeyen statik icerikler, bu networkler uzerinden kullanicilara sunulur. Bu sayede kullanici bunlari (html, css, javascript, resim, video ...) kendisine yakin bir sunucudan alir. Ayrica DDOS saldirilarina karsi da siteyi korur cunku bu saldirilar genelde dunya uzerinde yayilmis makinelerin tek bir siteye saldirmasi ile olur. Ancak CDN'den serv edilen icerik icin herkes kendine yakin olan sunucuya gidecegi icin bir cografi lokasyondaki trafigi tikayacak duruma gelinmesi cok zorlasir. 

2- Ama her islemi onceden hesaplamak mumkun degildir. Bu gibi durumlarda da kullaniciyi bekletmemek gerekir. Ornegin baslatildiktan sonra 10 dakika surecek bir islem icin kullanici bos bir sayfaya 10 dakika boyunca bakmamalidir. "isleminiz alinmistir" gibi bir mesaj gosterilip, bu is bittiginde kullaniciya haber verilmesi, yani tum surecin asenkron hale getirilmesi kullanici deneyimini iyi yonde etkiler. 

Bunu yapabilmek icin backend'in asenkron bir mimari ile tasarlanmasi gerekir. En temelde, bir is kuyrugu (job queue) ve bu kuyruktaki isleri sirasiyla isleyen worker'lar ile bu yapiyi elde edebiliriz. Uzun surecek bir is olusturuldugu zaman queue'ye eklenir. Bosta olan bir worker isi alir ve tamamlar, ve daha sonra baska bir birime haber verir. Son kullanici da en naive haliyle, bir serivisi poll eder ve isin bitip bitmedigini surekli kontrol eder. (Aslinda push notifikasyon daha iyi olur, sonucta bos yere API poll'lamak da verimsiz birsey).  

Diger bir system design postunda gorusmek uzere. Stay hungry, stay foolish, stay healthy :)

Yorumlar

Bu blogdaki popüler yayınlar

Python'da Multithreading ve Multiprocessing

Threat Modeling 1

Encoding / Decoding