https://buraksenyurt.com/Burak Selim Şenyurt - Ruby2019-12-03T07:32:39+00:00Matematik Mühendisi Bir Bilgisayar Programcısının NotlarıBurak Selim SenyurtBlogEngine.Net Syndication Generatorhttps://buraksenyurt.com/opml.axdBurak Selim SenyurtMatematik Mühendisi Bir Bilgisayar Programcısının Notlarıtr-TRBurak Selim Şenyurt0.0000000.000000https://buraksenyurt.com/post/ruby-tarafindan-redis-docker-bazli-veritabani-ile-konusmakRuby Tarafından Redis(Docker Bazlı) Veritabanı ile Konuşmak2019-12-01T09:45:00+00:00bsenyurt<p><img style="float: right;" src="https://buraksenyurt.com/image.axd?picture=/2019/11/terminatormotorcyle.jpg" alt="" />Geçtiğimiz yıl kendi kendimi eğitmek üzere 41 bölümden oluşan <a href="https://github.com/buraksenyurt/saturday-night-works" target="_blank">Saturday-Night-Works</a> isimli bir çalışma yapmıştım. Github üzerine aldığım notlar ve kod örneklerini blog üzerinde derleyip toparlamak onları pekiştirmek için önemli ve gerekliydi. Ne var ki yetişemediğimiz yazılım teknolojilerindeki gelişmeler için sürekli antrenman yapmam gerekiyor. Bu sebepten araya uzun bir tatil koyup tekrardan enerjimi depolamaya çalıştım. Nadas süresi boyunca neler yapabileceğimi düşündüm. Yeni maceramın felsefesi bir öncekisi ile aynı olmalıydı. Rastgele ve pek deneyimli olmadığım konularla ilgili araştırma yapıp bunları basit örneklerle çalışmalı ve dokümanlaştırmalıydım. Ha geldi gelecek derken nihayet ilham perim beni buldu ve artık tatilimi sonlandırmam gerektiğine karar verdim. Yeni serüvenime SkyNet evrenindeki Ahch-To<em>(MacOS Mojave - Intel Core i5 1.4Ghz, 4 Gb 1600 Mhz DDR3)</em> gezegeninde başlıyorum. Orada geçirdiğim bir saat dünya zamanında birkaç güne denk geliyor.</p>
<p>Öyleyse gelin size ilk gün başımdan geçenleri anlatayım. Bu ilk macerada epeydir eğilmediğim Ruby kodları ile tekrar bir aradayım. Amacım Ruby ile docker container üzerinde host edilmiş bir Redis veritabanında basit işlemler yapabilmek. Daha önce birkaç NoSQL sistemini deneyimle fırsatım olmuştu. Ancak MacOS üzerinde docker kullanımı konusunda tecrübeli değildim. Ruby kodlamayı ve Redis'in temel veri tipleriniyse çoktan unuttum. Dolayısıyla benim için bilgi pekiştirmek açısından iyi bir gün olacağını düşünüyorum.</p>
<p>Redis, In-Memory NoSQL veritabanı sistemlerinden birisi olarak karşımıza çıkıyor. Tüm veriyi bellekte saklayıp sorguladığından epeyce hızlı<em>(Metrik değerlere bakmak lazım)</em> Key-Value<em>(Tuple Store)</em> tipinden bir veritabanı olup hash, list, set, sorted-set ve <a href="https://redis.io/topics/data-types" target="_blank">string tiplerinden </a>oluşan zengin bir veri yapısına sahip. Dağıtılabilir caching stratejilerinde, otomatik tamamlama özelliklerine ait önerilerin hızla getirilmesinde, aktif kullanıcı oturumlarının takibinde, iş kuyruklarının modellenmesinde<em>(publisher/subscriber türevli)</em> etkili bir NoSQL çözümü olarak tercih ediliyor. </p>
<p>Bu temel bilgilere ek olarak CAP<em>(Consistency, Availability, Partition Tolerance)</em> üçgeninin CP kenarında yer aldığını söylemeliyiz. CAP teoremindeki harflerin anlamlarını kısaca hatırlayalım mı? Dağıtık bir sistemde Consistency ilkesine göre tüm istemciler verinin her zaman aynı görünümüne ulaşır. Pi'nin değeri bir istemci tarafından 3.14 olarak belirlenmişse diğer istemciler Pi'ye baktıklarında bu sayıyı görür. Availability ilkesi tüm istemcilerin dağıtık sistem üzerinde her an okuma ve yazma yapabiliyor olmasını öngörür. Partition ilkesine göre node'larda fiziki olarak kopma meydana gelse bile sistem kullanılabilir durumda kalmalıdır. Tabii en önemli nokta şudur ki CAP teoremine göre dağıtık bir sistem bu üç unsuru aynı anda karşılayamaz. Sürprizzzz :) Çok sık gördüğümüz CAP üçgenini aklımızda kaldığı kadarıyla çizmeye çalışırsak bilgileri biraz daha pekiştirmiş oluruz. Örneğin aşağıdaki gibi.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2019/11/screenshot_5.png" alt="" /></p>
<p>Bu kısa bilgilerden sonra örneklerimizi geliştirmeye başlamaya ne dersiniz!?</p>
<h2>İlk Adımlar</h2>
<p>Öncelikle Redis Docker imajını ayağa kaldırıp ping atmamız gerekiyor. Sistemde docker yüklüyse aşağıdaki komutlardan yararlanılabilir.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">docker pull redis
docker run --name london --network host -d redis redis-server --appendonly yes
docker run -it --network host --rm redis redis-cli -h localhost
ping
docker stop london
docker container rm 0793a</pre>
<p>İlk komut ile redis imajının son sürümünü indiriyoruz. Buradaki appendonly anahtarına verilen değer sebebiyle dataset üzerinde yapılan her değişiklik fiziki diskte kalıcı hale getirilecektir<em>(<a href="https://redis.io/topics/persistence" target="_blank">Persistance detayları için şuraya bakabiliriz</a>)</em> İkinci komutla container'ı başlatıp ardından gelen ile redis-cli terminal aracını devreye alarak redis ortamına bağlanıyoruz. ping karşılığında PONG mesajını görmemiz redis'in çalıştığının işaretidir. Dilerseniz buradayken de Redis ortamını deneyimleyebilirsiniz. Son komutlar opsiyonel olmakla birlikte Docker container'ını durdurmak ve kaldırmak için kullanılır.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2019/11/screenshot_1.png" alt="" /></p>
<p>Docker container'ı varsayılan olarak localhost:6379 adresinden hizmet vermektedir. Proje iskeletini oluşturmak için ben kendi sistemimde aşağıdaki terminal komutlarını kullandım. Diğer yandan örnek kodlarımızı ruby ile geliştireceğiz ancak Redis ile konuşabilmemizi kolaylaştıracak bir pakete de<em>(gem diyoruz)</em> ihtiyacımız olacak.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">mkdir src
cd src
touch main.rb publisher.rb subscriber.rb dessert.rb
sudo gem install redis</pre>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2019/11/screenshot_2.png" alt="" /></p>
<p>Son satırda yer alan install komutu ile redis isimli gem paketini sisteme dahil etmiş oluyoruz.</p>
<h2>Kod Tarafı</h2>
<p>Öğretide üç farklı uygulama söz konusu. İlk örnek ruby kodlarından Redis'e bağlanıp çok basit bir kaç işlemin nasıl icra edildiğini göstermekte. Ağırlıklı olarak Redis veri tiplerinin genel kullanımları söz konusu. Kodlarda yer alan yorum satırlarında mümkün mertebe ne yaptığımızı açıklamaya çalıştım.</p>
<p>main.rb içeriği</p>
<pre class="brush:ruby;auto-links:false;toolbar:false" contenteditable="false">require 'redis' # redis gem'ini kullanacağımızı belirttik
redis=Redis.new(host:"localhost")
#redis.ping()
redis.set("aloha",1001) # örnek key:value ekledik
word=redis.get("aloha") # eklediğimizi alıp
puts word # ekrana bastırdık
# List kullanımına örnekler
redis.del('user_actions') # önce user_actions ve tutorial_list listelerini temizleyelim
redis.del('tutorial_list')
# Right Push
redis.rpush('user_actions','Naycıl login olmayı deniyor')
redis.rpush('user_actions','şifre hatalı girildi')
redis.rpush('user_actions','Naycıl giriş yaptı')
redis.rpush('user_actions','Naycıl alışveriş sepetine bakıyor')
redis.rpush('user_actions','Naycıl sepetten 19235123A kodlu ürünü çıkarttı')
p redis.lrange('user_actions',0,-1) # tüm listeyi ilk girişten itibaren getirir
redis.ltrim('user_actions',-1,-1) # son elemana kadar olan liste elemanlarını çıkarttık
puts ''
p redis.lrange('user_actions',0,-1)
puts ''
#Left Push
redis.lpush('tutorial_list','redis')
redis.lpush('tutorial_list','mongodb')
redis.lpush('tutorial_list','ruby on rails')
redis.lpush('tutorial_list','golang')
p redis.lrange('tutorial_list',0,-1)
# Set Kullanımına Örnekler
redis.del('cenifer-friends')
redis.del('melinda-friends')
redis.sadd('cenifer-friends','semuel')
redis.sadd('cenifer-friends','nora')
redis.sadd('cenifer-friends','mayki')
redis.sadd('cenifer-friends','lorel')
redis.sadd('cenifer-friends','bill')
redis.sadd('melinda-friends','mayki')
redis.sadd('melinda-friends','ozi')
redis.sadd('melinda-friends','bill')
redis.sadd('melinda-friends','törnır')
redis.sadd('melinda-friends','sementa')
redis.sadd('melinda-friends','kıris')
puts ''
p redis.smembers('cenifer-friends')
puts ''
p redis.smembers('melinda-friends')
puts ''
p redis.sinter('cenifer-friends','melinda-friends') # Yukarıdaki iki kümenin kesiştiği elemanları verir
puts ''
p redis.srandmember('melinda-friends') # her srandmember çağrısı kümeden rastgele bir elemanı döndürür
p redis.srandmember('melinda-friends')
# Sorted Set örnekleri
redis.del('best-players-of-the-week')
# haftanın oyuncularını ağırlık puanlarına göre ekledik
redis.zadd('best-players-of-the-week',32,'maykıl cordın')
redis.zadd('best-players-of-the-week',24,'skati pipın')
redis.zadd('best-players-of-the-week',32,'leri börd')
redis.zadd('best-players-of-the-week',21,'con staktın')
puts ''
puts redis.zrevrange('best-players-of-the-week',0,-1) # en yüksek skor puanından en küçüne doğru getirir (rev hecesine dikkat)
puts ''
puts redis.zrevrange('best-players-of-the-week',0,0) # en iyi skora sahip olanı getirir (rev hecesine dikkat)
puts ''
puts redis.zrangebyscore('best-players-of-the-week',20,30) # skorları 20 ile 30 arasında olanlar</pre>
<p>İkinci örnek biraz daha kapsamlı olup publish/subscribe modelinin uyarlamasını ele alıyor. Publisher görevini üstlenen uygulama sembolik olarak belirli aralıklarla broadcast yayını yapar ve game-info-101 ve game-info-102 kodlu kannalar üzerinden mesajlar yollar. Bu kanalları dinleyen aboneler yayınlanan mesajları görebilir. </p>
<p>publisher.rb içeriği</p>
<pre class="brush:ruby;auto-links:false;toolbar:false" contenteditable="false">require 'redis'
puts 'Maç bilgileri gönderiliyor...'
redis=Redis.new(host:"localhost") #Redis sunucusuna bağlan
redis.publish 'game-info-101','Harden üç sayılık basket. Skor 92-92' # bir mesaj fırlat
redis.publish 'game-info-102','Furkan top çalma, hızlı hücum ve basket. Skor 2-0'
sleep 16
redis.publish 'game-info-102','Joel Embit blog. Skor 2-0'
sleep 14 # 14 saniye bekle
redis.publish 'game-info-101','Donçiç harika bir assist yapıyor ve Maksi Kleber smacı vuruyor. Skor 92-94'
sleep 22 # 22 saniye bekle
redis.publish 'game-info-101','Dallas son bir molaya gidiyor'
sleep 60 # 1 dakika bekle
redis.publish 'game-info-101','exit' # aboneler, bu kanal için aboneliklerini sonlandırabilir
redis.publish 'game-info-102','exit'
puts 'Program sonu'</pre>
<p>subscriber.rb içeriği</p>
<pre class="brush:ruby;auto-links:false;toolbar:false" contenteditable="false">require 'redis'
channelName=ARGV[0] # komut satırında kanal bilgisini al
redis=Redis.new(host:"localhost") #Redis sunucusuna bağlan
begin
# game-info-101 isimli olaya abone ol
redis.subscribe channelName do |on|
on.subscribe do |channel,msg| # abonelik gerçekleşince çalışır
puts "#{channel} kanalına abone olundu"
end
on.message do |channel, msg| # mesaj gelince çalışır
puts "#{channel} -> #{msg}" # mesajı ekrana bastır
redis.unsubscribe if msg=="exit" # eğer mesaj bilgisi exit olarak geldiyse aboneliği sonlandır
end
on.unsubscribe do |channel,msg| # abonelik sonlandığında çalışır
puts "#{channel} kanalına abonelik sonlandırıldı"
end
end
rescue redis::BaseConnectionError => err # bir bağlantı hatası sorunu olursa 3 saniye içinde tekra bağlanmaya çalışılır
puts "#{err}, 3 saniye içinde tekrar deneyeceğim"
sleep 3
retry
end</pre>
<p>Üçüncü ve son örnek ise bir SQL tablosunun Redis dünyasında kabaca nasıl tarif edilebileceği ile alakalıdır. Olayı karakterize etmek için aşağıdaki görseli ele alabiliriz.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2019/11/screenshot_6.png" alt="" /></p>
<p>Player isimli bir tablomuz olduğunu düşünelim. Bu tip bir veri yığınını Redis'in deneysel dokümanlarına göre sağ taraftaki gibi tariflemek mümkün. Hash ve Sorted Set'ler tablo ve Select sorgusunu karşılayabilir niteliktedir. Örneğin oyuncuların skorlarına göre sıralanacağı bir listeyi Sorted Set nesnesi gibi düşünebilir ve belli bir puan aralığındakilerin listesinin sıralı olarak çekilmesini sağlayabiliriz. Bu deneyselliği ele aldığımız Dessert.rb dosyasının içeriği aşağıdaki gibidir.</p>
<pre class="brush:ruby;auto-links:false;toolbar:false" contenteditable="false">require "redis"
redis=Redis.new(host:"localhost")
# Hash içerisine birkaç player örneği ekliyoruz
redis.hmset("player:1","fullname","Baz Layt Yiır","country","moon","score",339)
redis.hmset("player:2","fullname","Mega maynd","country","mars","score",317)
redis.hmset("player:5","fullname","Payn payn laki lu","country","wild west","score",405)
redis.hmset("player:3","fullname","Aeyrın Vaykovski","country","poland","score",322)
redis.hmset("player:4","fullname","Bileyd Rut","country","saturn","score",185)
# hgetall metoduna verdiğimiz parametre ile player:3 veri setini çekip ekrana bastırıyoruz
# Select'in where koşulu gibi düşünelim
player3=redis.hgetall("player:3")
puts "#{player3}\n\n"
# hmget ile player:2 key içeriğinden sadece country ve fullname değerlerini yazdırıyoruz
puts "#{redis.hmget('player:2','country','fullname')}\n\n"
# bir sorted list oluşturuyoruz
# listeyi yukarudaki hash üzerinden oluşturmak için player:* desenine uygun olan key içeriklerini çekmemiz lazım
# scan_each metodunun match parametresi bunu sağlıyor
redis.scan_each(:match=>"player:*"){|key|
currentScore=redis.hmget(key,"score") #hmget metoduna iterasyondaki key değerini verip score bilgisini yakalıyoruz
redis.zadd("score_list",currentScore,key) # score bilgisini o anki key ile ilişkilendirerek sorted list nesnesine ekliyoruz
}
# sorted list için skoru 330 puanın altına olanların listesini çektik (Select'in where koşulu gibi düşünelim)
scores_under_330=redis.zrangebyscore("score_list",0,330)
puts "Skoru 0 ile 330 arasında olanlar\n\n"
# bulduğumuz listede dolaşıp sadece fullname bilgilerini ekrana bastırdık
scores_under_330.each do |plyr|
info=redis.hgetall(plyr)
puts "#{info['fullname']} için güncel skor değeri #{info['score']}"
end</pre>
<h2>Çalışma Zamanı</h2>
<p>Kod tarafını tamamladıysak çalışma zamanına geçebiliriz. Main.rb ile birlikte diğer örnekleri de incelemeye başlayalım.</p>
<h3>Başlangıç</h3>
<p>Öncelikle Redis container'ını başlatmalıyız. Bunun için aşağıdaki terminal komutları ile ilerleyebiliriz. İlk komut docker container'ını çalıştırıyor. İkinci komut kontrol amaçlı olarak container listesine bakmak için. Son terminal komutumuz tahmin edileceği üzere main.rb isimli ruby dosyasını yürütüyor.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">docker run -d -p 6379:6379 redis
docker container ps -a
ruby main.rb</pre>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2019/11/screenshot_3.png" alt="" /></p>
<h3>Ana Yemek</h3>
<p>Yemek olarak publisher soslu subscriber'ımız var. Üstelik en ünlü İtalyan şeflerinden Rizotta Galliani tarafından özenle hazırlandı :P Sizi ciddiyete davet ediyorum Burak Bey. En az 3 terminal ekranı açıp birisinde publisher.rb diğer ikisinde de subscriber.rb dosyalarını dinleyecekleri kanalları parametre olarak verip çalıştırmak sonuçları irdelememiz için yeterlidir. Aynen aşağıdaki gibi.</p>
<pre class="brush:ruby;auto-links:false;toolbar:false" contenteditable="false">ruby subscriber.rb 'game-info-1'
ruby subscriber.rb 'game-info-2'
ruby publisher.rb</pre>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2019/11/screenshot_4.png" alt="" /></p>
<p>Dikkat edileceği üzere aboneler program başlatılırken bağlanacakları kanalın adını belirtiyor. Bu nedenle her ikisi de farklı maç akışlarını dinlemekteler. Pek tabii bir abonenin birden fazla kanalı dinlemesi de mümkün. Kurgumuz burada oldukça basit ve senkron kaldı. N sayıda maça ait haber akışını paylaşabileceğiniz bir publisher uygulamasını nasıl yazarsınız bir düşünün.</p>
<h3>Tatlı</h3>
<p>Tatlı olarak bir SQL tablosunun Redisce yorumlanması var. Aşağıdaki terminal komutu ile örneğimizi çalıştıralım ve sonuçları sınıfça irdeleyelim.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">ruby dessert.rb</pre>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2019/11/screenshot_7.png" alt="" /></p>
<p>Pek tabii SQL'in Domain'e özgü yazılmış lisanında SELECT, WHERE gibi ifadeleri kullanarak istenilen sorguları çalıştırmak çok daha kolay. Lakin yine de Redis dünyasında bu tip bir yaklaşımı nasıl ele alabiliriz sonuçlardan görebiliyoruz. Bunun farklı varyasyonlarını LINQ<em>(LanguageINtegratedQuery)</em> gibi yapılarda da görmemiz mümkün. Elimizde bir liste ve içerisinde veri tutan nesnelerimiz varken filtrelemeler için koddakine benzer yaklaşımları kullanıyoruz. </p>
<h3>Tamamlarken</h3>
<p>Eğer kullandığımız Redis Container'ı ile işimiz bittiyse önce durdurup sonrasında sistemden kaldırmak isteyebiliriz. Bunun için aşağıdaki terminal komutlarını çalıştırmak yeterli olacaktır<em>(Muhtemelen sizdeki Names değeri farklı olur. Ben çalışırken rastgele interesting_nobel ismi atanmıştı)</em></p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">docker container ps -a
docker stop interesting_nobel
docker container rm interesting_nobel
docker container ps -a</pre>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2019/11/screenshot_8.png" alt="" /></p>
<h2>Neler Öğrendim?</h2>
<p><a href="https://github.com/buraksenyurt/saturday-night-works" target="_blank">Saturday-Night-Works</a> çalışmalarında olduğu gibi yeni başladığım SkyNet serüveni de bana birçok şey öğreteceğe/hatırlatacağa benziyor. Bu ilk macerada aklımda kalanları şöyle özetleyebilirim.</p>
<ul>
<li>Redis docker imajını MacOS üzerinde kullanmayı</li>
<li>Redis'in CAP teoreminde hangi ikiliye yakın olduğunu</li>
<li>Redis gem'ini kullanarak yapılabilecek temel işlemleri</li>
<li>Temel redis veri tiplerini</li>
<li>Basit bir pub/sub kurgusunu işletmeyi</li>
<li>SQL stilinde bir tablonun Redisce oluşturulmasını ve verinin filtrelenmesini</li>
<li>Bir hash içindeki tüm key değerlerini nasıl dolaşabileceğimi</li>
</ul>
<h2>Eksikliği Hissedilen Konular</h2>
<p>SkyNet'in ilk gününden kalan ve merak ettiğim şeyler de var. Bunlar siz değerli okurlarımın araştırabileceği konular arasında yer alıyor. Bölüm sonu soruları gibi düşünebilirsiniz :)</p>
<ul>
<li>Publisher tarafında farklı kanalların birbirinden bağımsız ve asenkron olarak yayın yapmasını nasıl sağlayabiliriz?</li>
<li>Subscriber olarak birden fazla kanala abone olabilir miyim?</li>
<li>Olmayan bir kanala abone olmak istediğimde Ruby çalışma zamanı nasıl tepki verir?</li>
<li>Peki pub/sub senaryosunu Ruby on Rails tabanlı bir web projesinde nasıl kullanırım?</li>
</ul>
<p>Böylece geldik bir SkyNet gününün sonuna. Zaman farkından dolayı tekrar Dünya'ya dönmek zorundayım. Bir sonraki gidişimde bakalım bizleri ne tür maceralar karşılayacak. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>2019-12-01T09:45:00+00:00redisnosqlrubydockergembsenyurtNihayet Ahch-To(MacOS Mojave - Intel Core i5 1.4Ghz, 4 Gb 1600 Mhz DDR3) üzerinde ki ilk çalışmamı gerçekleştirebildim. 2020 yılı için planlarım arasında yer alan kendimi yetiştirme turları kapsamında çıktığım bu ilk macerada epeydir eğilmediğim Ruby kodları ile tekrar bir aradayım. Bu ilk maceradaki amacım Ruby kodlama ile docker container üzerinde host edilmiş bir Redis veritabanında basit işlemler yapabilmek.https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=dbea10e3-4d64-47ea-aed1-ab7835ee936e4https://buraksenyurt.com/trackback.axd?id=dbea10e3-4d64-47ea-aed1-ab7835ee936ehttps://buraksenyurt.com/post/ruby-tarafindan-redis-docker-bazli-veritabani-ile-konusmak#commenthttps://buraksenyurt.com/syndication.axd?post=dbea10e3-4d64-47ea-aed1-ab7835ee936ehttps://buraksenyurt.com/post/bir-ruby-uygulamasini-google-cloud-platform-uzerine-tasimakBir Ruby Uygulamasını Google Cloud Platform Üzerine Taşımak2018-01-27T17:00:00+00:00bsenyurt<p><img style="float: right;" src="https://buraksenyurt.com/image.axd?picture=/2018/01/rubyongcp_10.gif" alt="" />Merhaba Arkadaşlar,</p>
<p>"Futbol basit bir oyundur. 22 kişi 90 dakika boyunca bir topun peşinde koşar ve sonunda her zaman Almanlar kazanır." demiş bizim de ne yazık ki yakından tanıdığımız Gary Lineker. Konumuzla ne alakası var derseniz. Az sonra onun bu sözünü buluta alacağız.</p>
<p>Ben bu bulut platformlarını çok tuttum. Gerek Microsoft Azure, gerek Amazon Web Services, gerek Google Cloud Platform...Hepsi çok çekici duruyor. Kurcaladığım ve üzerinde çalıştığım örneklerle, özellikle West-World dünyasında yazılmış bir programın ilgili platform üzerinde konuşlandırılması ve yürütülmesini öğrenmeye gayret ediyorum. Ağırlıklı olarak REST<em>(Representational State Transfer)</em> tipinden servis uygulamalarını taşımaya çalışıyorum ki bu sayede bir mikroservis bulut alanında nasıl kullanılıyor öğreneyim.</p>
<p>Bu seferki hedefimse rastgele özlü söz veren Ruby programlama dili ile yazılmış bir REST servisinin Google Cloud Platform'a taşınarak App Engine Flexible ortamında ayağa kaldırılması. İşlemlerime başlamadan önce Google Cloud Platform üzerinde geçerli bir ödeme seçeneğinin olması gerekiyor<em>(AWS'de de benzer şekilde servislerden yararlanabilmek için geçerli bir kredi kartı bilgisi isteniyordu)</em> Bir ablam olduğu için çok şanslıyım. Sağolsun bu proje için sanal kredi kartını kullanmama izin verdi. Gelin adım adım ilerleyerek ruby servisimizi App Engine Flexible ortamından çalıştıralım.</p>
<h1>Google Cloud Tools Olmazsa Olmaz</h1>
<p>West-World bildiğiniz üzere Ubuntu 16-04 64bit sürümünü kullanıyor. Bu nedenle sıradaki Google Cloud Tools kurulum işlemi Debian/Ubuntu sistemleri için geçerli. Elbette Windows, MacOSX, RedHat/Centos gibi sistemler için de bu araçtan yararlanabiliriz<em>(<a href="https://cloud.google.com/sdk/docs/" target="_blank">Şu adresten</a> kendi sisteminiz için uygun olan sürümü indirebilirsiniz)</em> Google Cloud Tools taşıma işlemi sırasında kullanacağımız araç olduğu için yüklememiz gerekiyor. Aşağıdaki terminal komutlarını kullanarak kurulumu yapıyorum.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">export CLOUD_SDK_REPO="cloud-sdk-$(lsb_release -c -s)"
echo "deb http://packages.cloud.google.com/apt $CLOUD_SDK_REPO main" | sudo tee -a /etc/apt/sources.list.d/google-cloud-sdk.list
curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -
sudo apt-get update && sudo apt-get install google-cloud-sdk
sudo apt-get install google-cloud-sdk-app-engine-java
gcloud init</pre>
<p>Son adımda Google login sayfasına yönlendirildim ve hangi projeyi hangi Google Compute Engine bölgesinde kullanmak istediğime dair iki soruyla karşılaştım. Ben YourFunnyQuote isimli projemi ve uğurlu rakamım 13 ile australia-southeast1-b bölgesini tercih ettim.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2018/01/rubyongcp_1.gif" alt="" /></p>
<p>ve login sonrasında da aşağıdaki ekranla karşılaştım. </p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2018/01/rubyongcp_2.gif" alt="" /></p>
<p>Bir başka deyişle West-World üzerinden gcloud'u kullanabilmek için kimliğim doğrulanmıştı.</p>
<blockquote>
<p> Diğer bölgeler hakkında görsel bilgiye ihtiyaç duyarsanız <a href="https://cloud.google.com/about/locations/#regions-tab" target="_blank">şu adrese</a> uğramanızı tavsiye ederim.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2018/01/rubyongcp_9.gif" alt="" width="500" /></p>
</blockquote>
<p>Kurulumun sorunsuz tamamlanıp tamamlanmadığından emin olmak için de şu komutu çalıştırdım.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">gcloud --help</pre>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2018/01/rubyongcp_6.gif" alt="" /></p>
<p>Bu işlemler sırasında seçtiğimiz proje önemli. Nitekim taşıma işlemi sonrasında kodlarımız bu proje ile ilişkiliendirimiş Instance'a taşınacak.</p>
<h1>Ruby Uygulamasının Geliştirilmesi</h1>
<p>gcloud aracını yükledikten sonra basit bir Ruby projesi geliştirmeye karar verdim. Ne var ki bu ana kadar West-World üzerinde hiçbir ruby projesi geliştirmemiştim. West-World bu değerli Ruby mücehverinin ne olduğunu bilmiyordu. O yüzden ona kurulması lazımdı.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">sudo apt-get install ruby-full</pre>
<p>ile tam sürümü yükledim. Kurulum sonrası</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">ruby -v</pre>
<p>terminal komutu ile de yüklenen versiyondan emin oldum.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2018/01/rubyongcp_5.gif" alt="" /></p>
<p>Örnek olarak REST tipinden bir servis geliştirmeyi planlıyordum. İşimi kolaylaştıracak paket ise Sinatra'ydı<em>(Ruby on Rails çatısı da tercih edilebilir tabii ki)</em> Ancak Ruby ile yeni tanışmış olan West-World büyük üstad Sinatra'dan da bihaberdi. Ona "I did it my way" sözlerini fısıldayarak usta sanatçıyı anımsatmaya çalıştım dersem deli olduğumu düşüneceksiniz. Bende bu nedenle </p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">sudo gem install sinatra</pre>
<p>ile sisteme Sinatra'yı yüklemeyi uygun gördüm.</p>
<p>Ayrıca serüvenim sırasında Ruby bağımlılıklarının yüklenmesini sağlamak için ruby-bundle aracına da ihtiyacım vardı. Nitekim taşıma işlemi sırasında Google Cloud Platform, Gemfile.lock dosyasının içeriğine bakarak ilgili bağımlılıkların ortama kurulumunu gerçekleştirecekti. Onun standartları buydu.</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">sudo apt-get install ruby-bundler</pre>
<p>Nihayet ruby kodlarını yazmaya başlayabilirdim. Paslanmış olan ruby bilgime aldırmadan Visual Studio Code'u açtım ve uygun bir klasör içerisinde önce app.rb dosyasını ardından özellikle Google için önemli olan app.yaml ve Gemfile içeriklerini oluşturdum.</p>
<pre class="brush:ruby;auto-links:false;toolbar:false" contenteditable="false">require "sinatra"
require "json"
set :port, 8080
get "/quotes/random" do
quotes=Array.new
quotes<<Quote.new(122548,"Michael Jordan","I have missed more than 9000 shots in my career. I have lost almost 300 games. 26 times, I have been trusted to take the game winning shot and missed. I have failed over and over and over again in my life. And that is why I succeed.")
quotes<<Quote.new(325440,"Vince Lombardi","We didn't lose the game; we just ran out of time")
quotes<<Quote.new(150094,"Randy Pausch","We cannot change the cards we are dealt, just how we play the game")
quotes<<Quote.new(167008,"Johan Cruyff","Football is a game of mistakes. Whoever makes the fewest mistakes wins.")
quotes<<Quote.new(650922,"Gary Lineker","Football is a simple game. Twenty-two men chase a ball for 90 minutes and at the end, the Germans always win.")
quotes<<Quote.new(682356,"Paul Pierce","The game isn't over till the clock says zero.")
quotes<<Quote.new(156480,"Jose Mourinho","Football is a game about feelings and intelligence.")
quotes<<Quote.new(777592,"LeBron James","You know, when I have a bad game, it continues to humble me and know that, you know, you still have work to do and you still have a lot of people to impress.")
quotes<<Quote.new(283941,"Roman Abramovich","I'm getting excited before every single game. The trophy at the end is less important than the process itself.")
quotes<<Quote.new(185674,"Shaquille O'Neal","I'm tired of hearing about money, money, money, money, money. I just want to play the game, drink Pepsi, wear Reebok.")
content_type 'application/json'
index=rand(quotes.length-1)
quote=quotes[index]
quote.to_json
end
class Quote
attr_accessor:id
attr_accessor:owner
attr_accessor:text
def initialize(id,owner,text)
@id=id
@owner=owner
@text=text
end
def to_json(*a)
{
"json_class" => self.class.name,
"data" => {"id" => @id, "owner" => @owner, "text" => @text}
}.to_json(*a)
end
def self.json_create(object)
new(object["data"]["id"], object["data"]["owner"],object["data"]["text"])
end
end</pre>
<p>Kod oldukça basit. Quote sınıfına ait nesne örneklerinden oluşan bir dizi var. Quote sınıfı JSON serileştirme için gerekli fonkisyonellikleri de barındırıyor. sinatra çatısını kullanan uygulamanın HTTP Get ile erişilebilen bir metodu var. Buna göre localhost:8080 adresine gelip quotes/random yoluna gittiğimizde, üretilen rastgele sayıya göre diziden bir özlü sözün döndürülmesi söz konusu. Bildiğiniz üzere sinatra şarkılarını varsayılan olarak 4567 numaralı porttan söylemekte. Bu senaryoda 8080 portunu kullanmak için küçük bir atama söz konusu. Talep sonrası içerik tipinin application/json formatında olacağı da content_type ataması ile belirleniyor. Gelelim özellikle Google Cloud Platform tarafındaki sistem için önem arz eden diğer iki içeriğe.</p>
<p>app.yaml isimli bir dosya oluşturup içeriğini aşağıdaki gibi doldurmam gerekti <em>(Google dokümantasyonu sağolsun)</em></p>
<pre class="brush:ruby;auto-links:false;toolbar:false" contenteditable="false">runtime: ruby
env: flex
entrypoint: bundle exec ruby app.rb
manual_scaling:
instances: 1
resources:
cpu: 1
memory_gb: 0.5
disk_size_gb: 10</pre>
<p>Burada Google Cloud tarafındaki çalışma ortamı için gerekli bir takım ayarların bildirimi yapılıyor. Çalışma zamanının ruby motoruna göre tasarlanması gerektiği, uygulamanın başlatılacağı giriş noktası, ölçekleme seçenekleri ve donanımsal kaynak gereksinimler<em>i(işlemci adedi, bellek, disk boyutu)</em> gibi bilgilere yer veriliyor. Gemfile isimli uzantısız bir dosya daha eklemek lazım. Bu dosyada projenin ihtiyaç duyduğu gem'ler varsa isimleri bildiriliyor. source ataması ile de, ilgili gem içeriklerinin hangi kaynaktan çeklileceği söylenmekte. Paketler için depo bildirimi yapıldığını düşünebiliriz. Benim örneğimde sadece sinatra gerektiğinden içeriği aşağıdaki gibi yazmam yeterliydi.</p>
<pre class="brush:ruby;auto-links:false;toolbar:false" contenteditable="false">source "https://rubygems.org"
gem "sinatra"</pre>
<p>Bu işlemlerin arındandan ilk olarak kodun kendi sistemimde çalıştığından emin olmalıydım. Terminalden</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">ruby app.rb</pre>
<p>ifadesi ile programı çalıştırdım ve tarayıcıyı her güncelleyişimde rastgele bir söz ile karşılaştığımı gördüm. Dikkat edeceğiniz üzere localhost:8080/quotes/random adresine talepte bulunuluyor. Şu an için yeterli gibi görünse de amaç bunu Google Cloud üzerinden sunabilmekti.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2018/01/rubyongcp_3.gif" alt="" /></p>
<h1>gcloud ile Taşıma</h1>
<p>Sırada taşıma öncesi yapmam gereken bir hazırlık daha var<em>(mış)</em>. Ruby kodunu tamamladıktan sonra özellikle</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">bundler install</pre>
<p>terminal komutu ile proje bağımlılıkların yükleneceği Gemfile.lock'un oluşması gerekiyor. Ancak bu işlem sonrası taşıma adımlarına geçilebilir. Aksi halde taşıma işlemi sırasında hata alınmakta<em>(Ne olduğunu söylesem mi? Yok, söylemeyeceğim...bundler install yapmadan gcloud deploy komutunu çalıştırıp kendiniz görün)</em></p>
<p>İçerik proje için aşağıdaki gibi oluştu.</p>
<pre class="brush:plain;auto-links:false;toolbar:false" contenteditable="false">GEM
remote: https://rubygems.org/
specs:
mustermann (1.0.1)
rack (2.0.3)
rack-protection (2.0.0)
rack
sinatra (2.0.0)
mustermann (~> 1.0)
rack (~> 2.0)
rack-protection (= 2.0.0)
tilt (~> 2.0)
tilt (2.0.8)
PLATFORMS
ruby
DEPENDENCIES
sinatra
BUNDLED WITH
1.11.2</pre>
<p>Artık taşıma işlemini deneyebilirdim. </p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">gcloud deploy</pre>
<p>terminal komutunu çalıştırarak süreci başlattım. Bir kaç dakika sonrasında taşıma işleminin başarılı bir şekilde yapıldığını gördüm<em>(Belki de Avusturalya'yı seçmemeliydim. Uzak diye mi taşıma böyle uzun sürdü dersiniz? :P)</em> Bundan sonra tek yapmam gereken https://yourfunnyquote.appspot.com/quotes/random adresine gitmek oldu. Tabii dilerseniz komut satırından</p>
<pre class="brush:bash;auto-links:false;toolbar:false" contenteditable="false">gcloud app browse</pre>
<p>diyerekten de tarayıcının açılmasını ve ilgili projeye gidilmesini sağlayabilirsiniz. Benim elde ettiğim sonuç şöyleydi.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2018/01/rubyongcp_4.gif" alt="" /></p>
<p>Adres çubuğundaki adrese dikkat edin lütfen. Bu arada yazıyı tamamladıktan sonra servisi kapattım :)) Yani çalıştığının tek ispatı bu ekran görüntüsü. Nitekim Google Cloud Platform bana 364 günlük bedava kullanma süresi ve 300 dolarlık kredi bahşetmişti ama neme lazım. Her an her şey olabilirdi. Bu nedenle ilgili adrese ulaşamazsanız lütfen beni affedin. Kendi projeniz ile denediğinizde ekstra bir durum oluşmazsa taşımanızı sorunsuz gerçekleştiriyor olmalısınız.</p>
<h1>Kapatırken</h1>
<p>Pek tabii Google Cloud Platform bu kadar basit değil. Computation, Big Data, Storage, StackDriver, Networking vb pek çok ana başlık altında yer alan hizmetler içeriyor. Söz gelimi API Endpoint dikkatimi çekelerinden birisi. Özellikle ölçekleme ve yüksek performans gerektiren APIlerimiz için biçilmiş kaftan gibi duruyor. Bunun en büyük sebeplerinden birisi Proxy Container tarafında NGinx sunucularını kullanması. Aşağıdaki çizimde söz konusu yapının kabataslak hali var<em>(Çözünürlük için lütfen kusura bakmayın. Telefon kamerasından ancak bu kadar oluyor)</em> İstemci talepleri öndeki Load Balancer'dan sonra Nginx tabanlı Proxy servisine geliyor ve buradan API'nin<em>(söz gelimi Python veya bir Java uygulamasının)</em> konuşlandırıldığı Container Instance'ına geçiyor. gcloud alt taraftan da görüleceği üzere dağıtım adımından sorumlu. Servis yönetimi<em>(Service Management)</em> gcloud ile konuşarak taşıma operayonlarını ve konfigurasyon içeriğini yönetiyor. Çalışma zamanı kontrolleri ve raporlamalarda servis kontrolün<em>(Service Control)</em> işi. Çalışma zamanını web tabanlı Cloud Console üzerinden de izleme şansımız var. </p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2018/01/rubyongcp_8.gif" alt="" /></p>
<p>Bu mimarileri anlamaya çalışmamız önemli. Ama biraz dinlendikten sonra. Çünkü, West-World'de güneş çoktan battı. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>2018-01-27T17:00:00+00:00rubygoogle cloud platformsinatrarest servicescloud computinggoogle app enginebsenyurtBen bu Cloud platformlarını çok tuttum. Gerek Azure, gerek AWS, gerek Google Cloud Platform...Hepsi çok çekici duruyor. Benim ilgimi çeken şey onları araştırırken özellikle west-world dünyasında yazılmış bir programın ilgili platform üzerinde konuşlandırılması ve yürütülebilmesi. Ağırlıklı olarak REST tipinden servis uygulamalarını taşımaya çalışıyorum. Bu seferki hedefimse rastgele bir sözü sunan ruby ile yazılmış bir REST servisinin Google Cloud Platform'a taşınması ve oradan kullanılabilmesi. İşlemlerime başlamadan önce Google Cloud Platform üzerinden geçerli bir ödeme seçeneğinimizin olması gerekiyor. Yani bir kredi kartı bilgimizi tanımlamış olmalıyız. Bir ablam olduğu için çok şanslıyım :) Sağolsun bu proje için sanal kredi kartını kullanmama izin verdi. Gelin adım adım ilerleyelim.https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=11323c49-6a22-4353-9a93-462d3dd6bb6d1https://buraksenyurt.com/trackback.axd?id=11323c49-6a22-4353-9a93-462d3dd6bb6dhttps://buraksenyurt.com/post/bir-ruby-uygulamasini-google-cloud-platform-uzerine-tasimak#commenthttps://buraksenyurt.com/syndication.axd?post=11323c49-6a22-4353-9a93-462d3dd6bb6dhttps://buraksenyurt.com/post/ruby-kod-parcaciklari-34-fiber-ve-concurrent-programmingRuby Kod Parçacıkları 34 - Fiber ve Eş Zamanlı Programlama2017-05-05T12:17:00+00:00bsenyurt<p><img style="float: right;" src="https://buraksenyurt.com/image.axd?picture=/2017/05/rubyfiber_1.gif" alt="" />Merhaba Arkadaşlar,</p>
<p>Eş zamanlı programlamanın<em>(Concurrent Programming)</em> dile veya çatıya göre farklı uygulanma şekilleri olabiliyor. Esas itibariyle genel amaç eş zamanlı olarak birden fazla işin gerçekleştirilmesini sağlayabilmek. Bu noktada en zorlayıcı noktalardan birisi işlemcinin ve işletim sisteminin bu çalışma taleplerine olan anlık tepkilerinin yönetilmesi. Neredeyse pek çok programlama ortamında Thread'ler ile karşılaşıyoruz<em>(Bu arada yandaki fotoğrafın Ruby Fiber ile bir alakası yok. Fiber konulu imaj ararkan doğal olarak lifli yiyecekler ve sevimsiz diyet konusunu karşıma çıkmıştı)</em></p>
<p>Ruby tarafında da böyle bir yapı mevcut ama bunun dışında Fiber adı verilen farklı bir kontrol yapısı daha var. Bu nesneler daha hafif iş parçacıkları için kullanılıyor ve çoğunlukla otomatik hesaplamalar yapan yardımcı rutinler için tercih ediliyor. Tercih edilmelerindeki önemli etkenlerden birisi planlamanın geliştirici tarafından yapılması gerekliliği. Yani sanal makinenin veya önceden tanımlı bir Thread yönetim mekanizmasının kontrolünde değiller. Bu kontrol yapısı resume ve yield isimli fonksiyonları kullanıyor. resume ile bir Fiber kod bloğunun çalıştırılması sağlanıyor ve ilgili iş parçacığına abone olan diğer iş parçacığına dönebilmek için yield işlevinden yararlanılıyor. </p>
<p>Çoğunlukla parçalı hesaplamaların yapıldığı, yapılan hesaplama sonuçlarının çağıran ortama geri döndürüldüğü ve tekrardan hesaplamayı yapan kod bloğuna yollanarak yeni sonuçların elde edildiği senaryolarda tercih ediliyorlar. Aslında Fiber kavramı benim yeni karşılaştığım bir konu. .Net dünyasında çoğunlukla Task Parallel Library gibi işleri oldukça kolaylaştıran yapılarla çalışıyorum. Bakalım Fiber mevzusunu kıvırabilecek miyim? Dilerseniz vakit kaybetmeden Fiber kullanımına ait basit bir kod parçası ile yola devam edelim.</p>
<h1>Merhaba Fiber</h1>
<pre class="brush:ruby;auto-links:false;toolbar:false" contenteditable="false">=begin
Fiber konusuna bir bakalım
=end
myFiber=Fiber.new do
puts "Fiber metoduna girdik"
Fiber.yield
puts "Fiber metodunun sonuna geldik"
end
puts "Burası çağıran kod kısmı"
myFiber.resume
puts "Tekrar çağıran koddayım"
myFiber.resume</pre>
<p>Kodu çalıştırdığımızda aşağıdaki sonuçlar elde ederiz.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2017/05/rubyfiber_2.gif" alt="" /></p>
<p>Peki ne oldu burada? Aşağıdaki grafik konuyu daha güzel özetleyebilir.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2017/05/rubyfiber_3.gif" alt="" /></p>
<p>Aslında ana uygulama kodu ve Fiber nesnesi ile açılan kod bloğu arasında resume ve yield fonksiyonlarından yararlanarak geçişler yapıldığını görüyoruz. Yardımcı rutin olarak çalışmasını istediğimiz kod bloğuna geçiş yapmak veya o blokta kaldığımız yerden işlemlere devam etmek için resume, bu kod bloğunu çağıran uygulama parçacığına geri dönmek içinse yield fonksiyonundan yararlanıyoruz. Bu bir çeşit rutinler arası geçisin planlanmasıdır<em>(Scheduling)</em></p>
<h1>Çağıran ve Blok Arası Veri Alışverişi</h1>
<p>Pek tabi Fiber blokları ve çağıran uygulama arasında veri alışverişi yapılması da gerekebilir. Yani Fiber içerisine argüman gönderilmesi ve bir takım işlemler sonucu elde edilen değerlerin döndürülmesi istenebilir. Aşağıdaki kod parçasında bu veri alışverişinin nasıl yapılabileceği basitçe örneklenmeye çalışılmıştır. </p>
<pre class="brush:ruby;auto-links:false;toolbar:false" contenteditable="false">fiberX=Fiber.new do |input|
puts "#{input} bilgisi geldi.Şimdi bunla bir şeyler yapayım"
Fiber.yield(rand)
puts "{input} şeklinde yeni bir bilgi geldi. Bunu da hesaplayayım."
Fiber.yield(rand)
"Her şey tamamlandı"
end
puts "Çağıran kod"
output1=fiberX.resume(198)
puts "Fiber içinden #{output1} cevabı döndü"
output2=fiberX.resume(200)
puts "Fiber içinden bu kez #{output2} cevabı döndü"
puts(fiberX.resume)</pre>
<p>Bu kez Fiber bloğuna input isimli tek bir değişken taşınıyor. Bu değişkene göre üretilen rastgele sonuçlar da çağıran tarafa iletiliyor. Kullanım oldukça basit. yield ile Fiber bloğu içinden çağıran tarafa sonuçlar dönülebiliyorken, resume ile de Fiber içerisine parametre aktarılabiliyor. Tabii planlama sırasına göre çağıran kod parçası ile Fiber kod bloğu içerisinde karşılıklı geçişler sağlanmakta. Çalışma zamanı sonuçları aşağıdaki gibidir.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2017/05/rubyfiber_4.gif" alt="" /></p>
<h1>Fiber Blokları Arası Veri Transferi</h1>
<p>Şu ana kadar ki örneklerimizde Fiber ile çağıran ana uygulama arasında geçişler yaptık. Çok doğal olarak n sayıdaki Fiber bloğu arasında da veri transferi gerçekleştirmek isteyebiliriz. Nitekim bir Fiber kod bloğu tarafında yapılan hesaplamalar sırasında farklı bir Fiber kod bloğunun bu sonuçlar ile başka işlemler yapıp diğer bloğa cevap vermesi gerekebilir. Tamamen senaryoya bağlı bir durum. Biz şimdilik bu geçişlerin nasıl yapılabileceğine basit bir örnekle bakalım. İşte kod parçamız.</p>
<pre class="brush:ruby;auto-links:false;toolbar:false" contenteditable="false">require 'fiber'
fiber1=fiber2=nil
fiber1=Fiber.new do |input|
puts "Fiber 1 Başlangıç Input : #{input}"
newInput=fiber2.transfer(input*rand)
puts "Fiber 1e gelen yeni Input : #{newInput}"
fiber2.transfer("işlemleri bitir")
end
fiber2=Fiber.new do |input|
puts "Fiber 2ye gelen Input : #{input}"
newInput=fiber1.transfer(input*rand)
puts "Fiber1 diyor ki '#{newInput}'"
end
puts "işlemler başlıyor"
fiber1.transfer(100)
puts "işlemler bitti"</pre>
<p>İşlemler biraz karışık gelebilir ancak ilk iki örnekteki mantığı düşünmemiz örneği anlamamız için yeterli. Fiber blokları arasındaki geçişler için yield fonksiyonu yerine fiber modülünde yer alan transfer operasyonundan yararlanıyoruz. Hatta ana kod parçasından fiber1 isimli ilk kod bloğunu başlatırken de transfer fonksiyonunu kullanmaktayız. Kodun çalışma zamanı çıktısı biraz daha iyi fikir verecektir.</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2017/05/rubyfiber_5.gif" alt="" /></p>
<h1>Yaygın Örnek</h1>
<p>Ruby, Fiber blokları görüldüğü üzere eş zamanlı programlamada adına basit ve hafif bir kullanım sunmakta. Eş zamanlı çalışma planlamasının programcıya bırakıldığı bu hafif yapı yardımcı rutinlerin ele alındığı senaryolarda oldukça işe yarar görünüyor. En çok verilen başlangıç örneği ise Fibonacci sayılarının hesaplanması için Fiber bloğundan yararlanılması<em>(Hep Recursive metodları kullanırdık ama yardımcı rutin olarak bu sayıları üretecek bir iş parçacığını geliştirmeyi pek düşünmemiştik)</em></p>
<blockquote>
<p>Fibonacci sayı dizisi 0,1 ile başlayıp her sayının kendisinden önce gelen iki sayının toplamı olarak hesaplandığı bir seridir. 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144...</p>
</blockquote>
<p>Buna ait kod parçasını geliştirmeye çalışmanız, bir süre uğraşıp yapamadığınız takdirde Internetten bakmanız kişisel gelişiminiz açısından yararlı olacaktır. Aşağıdaki kod parçasına da lütfen uğraştıktan sonra bakınız.</p>
<pre class="brush:ruby;auto-links:false;toolbar:false" contenteditable="false">def fibogen
Fiber.new do
x,y=0,1
loop do
Fiber.yield(y)
x,y=y,x+y
end
end
end
generator=fibogen
20.times do
print generator.resume,","
end</pre>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2017/05/rubyfiber_6.gif" alt="" /></p>
<p>fibogen isimli fonksiyon içerisinde yeni bir Fiber bloğu oluşturulmakta. Bu blok içerisinde de sonsuz bir döngümüz var. Başlangıçta Fibonacci sayı dizisinin ilk iki değeri varsayılan olarak verilmekte<em>(ki sayı üreticisinin bu başlangıç değerlerini parametre olarak alması daha güzel olabilir. Nitekim bu sayede istediğimiz iki sayıdan sonrasını elde etme şansını da bulabiliriz)</em> Döngü içerisinde yield operasyonu ile çağıran koda geri dönülüyor ve o anki y değeri yollanıyor. Bu arada generator şeklinde bir değişken tanımlamassak hep 1 sayısını elde edebiliriz. Bunu bir deneyin. Ayrıca ilk 20 fibonacci sayısını bastıktan sonra örneğin ilk 5 fibonacci sayısını yakalamak istersek yeni bir fibogen değişkenine ihtiyacımız olacak<em>(Başlangıç değerlerini parametre olarak alınnnnn)</em> Nitekim bunu yapmassak sonraki 5 fibonacci elemanı ilk 20 elemanın arkasındakiler olarak hesaplanır.</p>
<p>Örneği çok daha iyi hale getirmek gerekiyor. Sınıflaştırılabilir, her yeni sayı dizisi hesaplaması için yeni bir generator üretilmemesi sağlanabilir. Bu kutsal görevleri siz değerli okurlarıma bırakıyorum. Böylece geldik bir Ruby kod parçacığımızın daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>
<p> </p>2017-05-05T12:17:00+00:00concurrencyconcurrent programmingmulti threadthreadfiberrubyruby kod parçacıklarıbsenyurtEş zamanlı programlanın farklı dillerde veya çatılarda farklı uygulanma şekilleri olabiliyor. Esas itibariyle genel amaç eş zamanlı olarak birden fazla işin gerçekleştirilmesini sağlayabilmek. Bu noktada en zorlayıcı noktalardan birisi işlemcinin ve işletim sisteminin bu çalışma taleplerine olan anlık tepkilerinin yönetilmesi. Neredeyse pek çok programlama ortamında Thread'ler ile karşılaşıyoruz. Ruby tarafında da böyle bir yapı mevcut ama bunun dışında Fiber adı verilen farklı bir tip daha var.https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=e3c0ed0f-9422-4902-8572-a4fff1710bad0https://buraksenyurt.com/trackback.axd?id=e3c0ed0f-9422-4902-8572-a4fff1710badhttps://buraksenyurt.com/post/ruby-kod-parcaciklari-34-fiber-ve-concurrent-programming#commenthttps://buraksenyurt.com/syndication.axd?post=e3c0ed0f-9422-4902-8572-a4fff1710badhttps://buraksenyurt.com/post/tek-fotoluk-ipucu-156-sevimli-ruby-block-lariTek Fotoluk İpucu 156 - Sevimli Ruby Block'ları2017-04-16T15:38:00+00:00bsenyurt<p>Merhaba Arkadaşlar,</p>
<p>Farklı programlama dillerini öğrenmeye çalışırken alışkın olduğum programlama dillerindeki ortamlardan çok daha farklı kabiliyetleri görme şansı buluyorum. Bazı dillerin kabiliyetleri çok dikkat çekici oluyor. Örneğin Ruby dilindeki block kavramı. İlk tanımaya çalıştığım <a href="https://buraksenyurt.com/post/ruby-kod-parcaciklari-10-yield-ve-block-kullanimi" target="_blank">şu kod parçasında</a> mevzuyu az çok anlamış olsam da asıl gücünü görmek için Ruby'nin kendi built-in yapılarındaki block kullanımlarını incelemem gerekti. Sonunda asıl faydasını anladım diyebilirim. Konuyu anlamak için dizilere sıklıkla uygulanan each, find_all, reject gibi metodları göz önüne aldım. Örneğin bir dizide belirli kurallara uyan elemanları elde etmek için kullanılan find_all metodunu kendim yazmak istesem ve bir block kullanmam gerekse bunu nasıl yapabileceğimi bulmaya çalıştım. Siz, Ruby'yi geliştiren kişi olsanız ve metodlara parametre olarak kod bloklarını geçirme yeteneğini ilave etseniz veri yapılarında bu özelliği nasıl kullandırtınız? Aşağıdaki örnek ekran çıktısında yer alan findSomething metodu gibi olabilir mi?</p>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2017/04/tfi156.gif" alt="" /></p>
<p>findSomething metodu içerisinde bir çok ders var. Örneğin bu metoda bir kod bloğu geçilip geçilmediğini anlamak için block_given? fonksiyonunu kullanıyoruz. findSomething public erişim belirleyicisine sahip. Kullanmadığımız takirde private kabul ediliyor ve çalışma zamanında NoMethodError hatası alıyoruz. Bir başka deyişle herhangibir nesneye uygulayamıyoruz. İçeride yer alan self anahtar kelimesi ile findSomething metodunun uygulandığı nesneyi yakalamaktayız. Şayet bu, dizi türevli bir nesne ise each ile elemanlarında dolaşabiliriz. yield ile tahmin edileceği üzere findSomething metoduna gelen kod bloğunu çağırıyoruz. Ayrıca yield ile findSomething metoduna gelen kod bloğuna işlenmesi için iterasyonun o anki nesnesini de<em>(item oluyor)</em> göndermekteyiz.</p>
<blockquote>
<p>yield anahtar kelimesinin kullanıldığı yerde findSomething metodundan dışarıya çıkıp kendisine parametre olarak gelen kod bloğuna(örnekte {|n|n%3==} kısmı) geçici olarak uğradığımızı ve item değerini oraya bıraktığımızı, bloktaki iş bitince de tekrar findSomething metoduna döndüğümüzü ifade edebiliriz. Bu block'ların temel çalışma felsefesidir.</p>
</blockquote>
<p><< operatörü ile result isimli diziye eleman ataması gerçekleştirmekteyiz. Kodun findSomething operasyonunu test ettiğimiz satırında 10dan 28e kadar olan değer aralığındaki sayıların 3 ile tam bölünebilenlerini çekip r isimli bir değişkende topluyoruz. findSomething metodu, parametre olarak gelen kod bloğuna göre elde ettiği sonuçları geriye döndüren bir fonksiyon görevi üstleniyor burada. find_all metodunun tıpkısını aynısı gibi değil mi?</p>
<p>Aynı felsefeyi kullanarak örneğin reject isimli built-in fonksiyonu da siz yazmaya çalışabilirsiniz. Aslında Ruby ile birlikte gelen ve kod bloğu alarak çalışan fonksiyonları kendiniz yazmayı deneyerek block kavramına olan aşinalığınızı arttırabilir konuyu pek güzelce pekiştirebilirsiniz. Ruby, sevimli yetenekleri ile beni kendisine hayran bırakmaya devam ediyor. Bir başka ipucunda görüşmek dileğiyle.</p>2017-04-16T15:38:00+00:00ruby kod parçacıklarırubyblockblock_given?find_allarraybsenyurtFarklı programlama dillerini çalışırken alışkın olduğum programlama dillerindeki ortamlardan çok daha farklı kabiliyetleri görme şansını buluyorum. Bazı dillerin kabiliyetleri bana çok ilgi çekici geliyor. Örneğin Ruby dilindeki block kavramı.https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=d269868c-7774-4622-be46-9e5c8329b1fa3https://buraksenyurt.com/trackback.axd?id=d269868c-7774-4622-be46-9e5c8329b1fahttps://buraksenyurt.com/post/tek-fotoluk-ipucu-156-sevimli-ruby-block-lari#commenthttps://buraksenyurt.com/syndication.axd?post=d269868c-7774-4622-be46-9e5c8329b1fahttps://buraksenyurt.com/post/ruby-kod-parcaciklari-34-struct-ve-openstructRuby Kod Parçacıkları 34 - Struct ve OpenStruct2017-04-10T15:00:00+00:00bsenyurt<p><img style="float: right;" src="https://buraksenyurt.com/image.axd?picture=/2017/04/ruby34_5.gif" alt="" />Merhaba Arkadaşlar,</p>
<p>Bir süredir şirket içinde vereceğim Ruby eğitimine hazırlanmaktayım. İşlerden çok vakit kalmasa da önceki Ruby notlarımı ve farklı kaynakları takip ederek 101 seviyesinde bir içerik oluşturmaya çalışıyorum. Gün içinde C# evde geç vakitlerde ise Ruby. Biraz yorucu olsa da oldukça keyifli aslında. Hem yeni bir şeyler öğreniyorum hem de iç eğitim gibi bir gerçek olduğundan ciddi anlamda not çıkartıyorum. Bugün konular üzerinden geçerken struct ve openstruct kavramlarını atladığımı fark ettim(Ov yooo) Tabii hemen öğrenmeye başladım. Neymiş ne için kullanılırmış biraz fikir sahibi oldum. İşte notlarım.</p>
<p>Struct aslında Ruby'nin built-in sınıflarından birisi. Temel olarak bir sınıf hazırlamadan nitelik ve değer barındıran tip tanımlanmasına olanak sağlıyor. Konuyu basit bir şekilde incelemeye başlamak için aşağıdaki kod parçasını göz önüne alarak ilerleyebiliriz.</p>
<pre class="brush:ruby;auto-links:false;toolbar:false" contenteditable="false">player=Struct.new :firstName,:lastName,:level
dam=player.new()
dam.firstName="jan kulod van"
dam.lastName="dam"
dam.level=900
puts "#{dam.lastName}, #{dam.firstName}-[#{dam.level}]"
obiWan=player.new("kenobi","obi wan",850)
puts "#{obiWan.lastName}, #{obiWan.firstName}-[#{obiWan.level}]"</pre>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2017/04/ruby34_1.gif" alt="" /></p>
<p>Örnek kodda player isimli bir yapı bildirimi yer alıyor. İlk tanımlama sırasında new operatörünü takiben bu veri yapısına dahil olan niteliklerin bildirimi yapılmakta. Kodumuzda aynı veri modeline sahip iki Struct değişkenine yer veriliyor. dam ve obiWan :) İlk kullanımda nitelik değerleri new operatöründen sonra atanmıştır. obiWan isimli değişken örneklenirken de ilgili nitelik değerleri new fonksiyonunda belirtilmiştir.</p>
<p>Yapıları tanımlarken do...end bloklarını da işin içerisinde dahil edebilir ve bu sayede çeşitli fonksiyonlar içermesini de sağlayabiliriz. Aşağıdaki kod parçasında bu durum örneklenmektedir.</p>
<pre class="brush:ruby;auto-links:false;toolbar:false" contenteditable="false">book=Struct.new :title,:price,:category,:author do
def getInfo
"#{title} from #{author}. #{price},#{category}"
end
end
tehlikeliOyunlar=book.new
tehlikeliOyunlar.title="Tehlikeli Oyunlar"
tehlikeliOyunlar.price=50
tehlikeliOyunlar.author="Oguz Atay"
tehlikeliOyunlar.category="Turk Edebiyati"
puts tehlikeliOyunlar.getInfo</pre>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2017/04/ruby34_2.gif" alt="" /></p>
<p>book isimli yapı yazılırken do end blokları arasında getInfo isimli bir metod tanımına da yer verilmiştir. getInfo metodu sadece yapının niteliklerini string formunda geriye döndürmektedir. Burada sınıflardan farklı olarak niteliklere erişirken @ işaretinin kullanılmadığı gözden kaçırılmamalıdır.</p>
<p>Aslında bu ve bir önceki örnekleri göz önüne alırsak benzer veri yapılarını sınıf olarak tanımlamak istediğimizde aşağıdakine benzer bir yol izlememiz gerektiği ortadadır.</p>
<pre class="brush:ruby;auto-links:false;toolbar:false" contenteditable="false">class Player
attr_accessor :firstName,:lastName,:price
def initialize(firstName,lastName,price)
@firstName,@lastName,@price=firstName,lastName,price
end
def getInfo
"#{@firstName},#{@lastName},#{@price}"
end
end</pre>
<p>Dikkat edileceği üzere attribute tanımlamaları ve new operatörü için initialize metodunun yazımı zorunludur. Yapılar bu noktada daha pratik bir veri modeli tanımlama yolu sunmaktadır. Nitekim bir yapı initialize metodu içermemesine rağmen new operatöründe içerdiği niteliklerine değer ataması yapılabilir.</p>
<p>Yapılar ile ilgili dikkat çekici bir diğer nokta da OpenStruct tipinin kullanımıdır. Bu tip kullanılırken niteliklerinin baştan belirtilmesine gerek yoktur. Yani yapı istenildiği kadar nitelik barındırabilir.Nasıl mı? Aynen aşağıdaki kod parçasında görüldüğü gibi.</p>
<pre class="brush:ruby;auto-links:false;toolbar:false" contenteditable="false">require "ostruct"
parameters=OpenStruct.new()
parameters.connection="provider=mysql..."
parameters.username="bsenyurt"
parameters.password="****"
parameters.timeout=6000
puts parameters.timeout
parameters.ftpAddress="ftp://localhost/images/"
puts parameters.ftpAddress</pre>
<p>OpenStruct için ostruct bildirimi gereklidir. Sonrasında yine new operatöründen yararlanılarak bir yapı tanımlanmıştır. parameters yapısına istediğimiz kadar nitelik atayabiliriz. Örnekte programlarımızda sıklıkla başvurduğumuz ve sayısı genellikle belli olmayan parametre modeli sembolize edilmeye çalışılmıştır. Tahmin edileceği üzere OpenStruct kendi içerisinde bir hash kullanır. Bunu new ile oluşturulduğu sırada örnekler. Hash, nitelik:değer eklendikçe genişlemeye olanak sağlar.</p>
<p>Peki bir yapının bu örneklerde olduğu gibi n sayıda niteliğinin tamamına kolayca erişmenin bir yolu yok mudur? Tabii ki vardır. Meşhur each metodumuz ne güne duruyor. İşte örnek bir kaç kullanım.</p>
<pre class="brush:ruby;auto-links:false;toolbar:false" contenteditable="false">require "ostruct"
parameters=OpenStruct.new()
parameters.connection="provider=mysql..."
parameters.username="bsenyurt"
parameters.password="****"
parameters.timeout=6000
parameters.ftpAddress="ftp://localhost/fileServer"
parameters.each_pair{|key,value| puts "#{key}->#{value}"}
player=Struct.new :firstName,:lastName,:level
obiWan=player.new("kenobi","obi wan",850)
obiWan.each{|o|puts o}
obiWan.each_pair{|key,value| puts "#{key}->#{value}"}
puts obiWan[:firstName]</pre>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2017/04/ruby34_4.gif" alt="" /></p>
<p>Bu örnekte each, each_pair ve [] operatörü kullanımları örneklenmiştir. each ile bir yapının tüm nitelik değerlerine erişmemiz mümkündür. each_pair tahmin edileceği üzere key:value benzeri yapının nitelik adı ve değerlerine erişmekte kullanılır. İstersek bir yapının elemanlarına indeksleyici ile de ulaşabiliriz. Aslında bir yapının diziye veya hash nesnesine atanması da oldukça kolaydır. Hatta select fonksiyonundan yararlanarak bir yapının taşıdığı değerler üzerinde koşullu seçimler yapılması da sağlanabilir <em>(Detaylar için <a href="https://ruby-doc.org/core-2.2.0/Struct.html" target="_blank">bu</a> ve <a href="http://ruby-doc.org/stdlib-2.0.0/libdoc/ostruct/rdoc/OpenStruct.html" target="_blank">şu</a> adreslerdeki Ruby dokümanlarına bakmanızı öneririm)</em></p>
<p>Peki yapıların bu pratik kullanımları nedeniyle sınıflar yerine tercih edilmeleri gerekir mi? Aslında yapıların kullanım sebepleri biraz daha farklıdır. Çoğunlukla geçici bir veri yapısına<em>(Temporary Data Structure)</em> ihtiyaç duyduğumuzda yapılardan yararlanabiliriz. Ya da test ortamında stub nesne ihtiyacı olduğunda kullanabiliriz. Bir diğer kullanım şeklide sınıf içerisinde dahili veri modeline ihtiyaç duyduğumuz durumlardır. Aşağıdaki kod parçasında bu durum örneklenmeye çalışılmıştır.</p>
<pre class="brush:ruby;auto-links:false;toolbar:false" contenteditable="false">class Employee
attr_accessor :firstName,:lastName, :address
Address = Struct.new(:street, :city, :country, :postal_code)
def initialize(firstName,lastName, addressInfo)
@firstName,@lastName=firstName,lastName
@address = Address.new(addressInfo[:street], addressInfo[:city], addressInfo[:country], addressInfo[:postal_code])
end
end
sitiv = Employee.new("Sitiv","Jobs", {
street: "hevan street",
city: "Second Parallel New York",
country: "Beauty Country",
postal_code: "HVN-1001"
})
puts sitiv.address.inspect</pre>
<p><img src="https://buraksenyurt.com/image.axd?picture=/2017/04/ruby34_3.gif" alt="" /></p>
<p>Bu örnekte Employee sınıfı içerisinde Address isimli bir yapı tanımlanmıştır. Employee sınıfına ait bir nesne örneklenirken initialize metoduna gelen son parametre bu yapıya ait bir değişken içeriğidir. Indeksleyici operatörü ile değerler alınıp Address yapısına ait değişken nitelikleri doldurulmuştur. </p>
<p>Görüldüğü gibi yapılar oldukça pratik kullanıma sahip bir veri tipi olarak karşımıza çıkmaktadır. Built-In olarak sınıf kökenli olan bu tipin kullanışlı fonksiyonları bulunmaktadır. Böylece geldik bir Ruby Kod Parçacığının daha sonuna. Tekrardan görüşünceye dek hepinize mutlu günler dilerim.</p>2017-04-10T15:00:00+00:00ruby kod parçacıklarırubystructopenstructbsenyurtBir süredir tek günlük bir Ruby eğitimine hazırlanmaktayım. İşlerden çok vakit kalmasa da önceki notlarımı ve farklı kaynakları takip ederek bir içerik oluşturmaya çalışıyorum. Konular üzerinden geçerken struct ve openstruct kavramlarına da rastladım. Struct aslında Ruby'nin built-in sınıflarından birisi. Temel olarak bir sınıf tanımı yapmadan nitelik barındıran tip kullanımına olanak sağlıyor diyebiliriz. Aşağıdaki kod parçasını göz önüne alalım.https://buraksenyurt.com/pingback.axdhttps://buraksenyurt.com/post.aspx?id=01e5edad-755a-4f45-a6a0-1d2e52f516c41https://buraksenyurt.com/trackback.axd?id=01e5edad-755a-4f45-a6a0-1d2e52f516c4https://buraksenyurt.com/post/ruby-kod-parcaciklari-34-struct-ve-openstruct#commenthttps://buraksenyurt.com/syndication.axd?post=01e5edad-755a-4f45-a6a0-1d2e52f516c4