laravel 4 - events and queues - 2013.12.21
DESCRIPTION
İTÜ Ayazağa kampüsünde 21 aralık 2013 tarihinde sunmuş olduğum Laravel 4: Events & Queues sunumu Sunum boyunca verilen örneklerin kaynak kodlarına buradan erişebilirsiniz: https://github.com/Ardakilic/laravel-4-workshop-sunumTRANSCRIPT
LARAVEL 4:
EVENTS & QUEUES Hazırlayan:
Arda KILIÇDAĞI
E-mail: [email protected]
Twitter: @ardadev
GitHub: ardakilic
EVENTS (OLAYLAR)
Events (Olaylar) Nelerdir?
• «Olaylar»ı basitçe «bir veya birden fazla
aĢaması olan, ve de sürekli benzer Ģekilde
tekrarlanan iĢlemlerimiz» gibi düĢünebiliriz.
Buna örnek vermek gerekirse, bir üyenin
siteye kayıt olması, kiĢiselleĢtirilmiĢ toplu e-
posta gönderimi, harici bir sunucudan veri
çekilip derlenmesi gibi iĢlemlerin hepsini
birer olay olarak nitelendirebiliriz.
Events (Olaylar)
Bir kişi siteye kayıt olurken arka planda şu aşamaları
gerçekleştirdiğimizi farz edelim:
• Form doğrulama
• Kullanıcı bilgilerini veritabanına kaydetme
• Aktivasyon E-postası gönderimi (SMTP kullanıyorsak
harici sunucu bağlantısı)
• Kullanıcıyı bir eposta grubuna dahil etme (Mailchimp gibi
hizmetler kullanıyorsak bir baĢka harici sunucu bağlantısı)
Events (Olaylar)
Bunların Hepsi controller’da
olsa Controller metodumuz
Ģunun gibi bir görünümde
olacaktı:
Events (Olaylar)
Peki bunların hepsi sizce
controller’ın görevi mi?
Events (Olaylar)
Events (Olaylar)
Burada her bir grubu ayrı bir evente almak
iĢimizi oldukça kolaylaĢtıracak ve kod
hiyerarĢimize düzen getirecek. Bu ayrıca
ilerde bu kodları farklı yerlerde yeniden
kullanmamızı sağlayacak.
Events (Olaylar)
Olayları autoload etmiĢ her yerde (app/filters.php , app/routes.php , start/global.php vs.) tanımlayabiliriz, fakat benim Ģahsi tercihim app klasörü altına events.php diye bir dosya açıp, içine olayları tek tek tanımlamak. Zorunlu olmasa da bu yol kafama en yatan yol, çünkü olayların takibini de kolaylaĢtırıyor. Bunu aĢağıdaki yolları izleyerek yapabiliriz:
• Önce boĢ bir app/events.php dosyası oluĢturun.
• Daha sonra app/start/global.php yi açıp en altına aĢağıdaki değeri ekledim:
require app_path().'/events.php';
Alternatif olarak composer.json dosyanızı açıp autoload nesnesi içinde files keyine (yoksa önce oluĢturarak) de tanımlama yapabilirsiniz.
Olayları Tanımlamak
Bu bahsettiğimiz «olaylar»ı Laravel 4.x’te üstlenen sınıfın adı Event sınıfıdır. Bu sınıf altındaki bazı temel metodlar ile çok basit bir Ģekilde olayları tanımlayabilir ve çağırabiliriz.
Event tanımlamak için aĢağıdaki metodu kullanabiliriz:
Event::listen(
'olayAdi',
'closureMetodVeyaSınıftanMetod',
$oncelik = 0
);
Olayları Tanımlamak
•olayAdi : Burada olaya verdiğimiz bir
isim. Arasına . (nokta) koyarak gruplama
yapılabilir. Örneğin kullanıcılar ile ilgili tüm olayları user.olayAdi gibi gruplayabilirsiniz.
Bu size wildcard kullanımını getirecektir.
Örneğin bu sayede eğer
Event::listen(‘user.*‘, ...) gibi
bir olay tanımladıysanız user. Ġle baĢlayan
tüm etkinliklerde bu olay da çalıĢacaktır.
Olayları Tanımlamak
•closureMetodVeyaSınıftanMetod :
bu parametre olay tetiklenince çalıĢacak
metottur. Metoda belirteceğiniz
parametreler ile olaya veri de refere edip
daha dinamik bir yapı kazandırabilirsiniz.
•Priority : Üçüncü parametre olan
priority, metodun öncelik sırasını belirtir.
Farz edelim aynı anda birden fazla metod
Olayları Tanımlamak
• Priority : Üçüncü parametre olan priority, opsiyonel
olup yazılması zorunlu değildir. Tamsayı olarak tanıtılması
gereken bu parametre, metodun çalıĢmadaki öncelik
sırasını belirtir.
Farz edelim aynı anda birden fazla olay çağırdık, ya da
tetiğimiz wildcardlar ile birden fazla olayı tetikledi. Bu
durumda her Ģeyi aynı anda baĢlatmak yerine iĢlemlerde
öncelik sırası yaparak olası sorunların önüne priority
değerini girerek geçebilirsiniz.
Olayları Tetiklemek
Olayları tetiklemek için Event sınıfının fire() metodunu
kullanıyoruz. Metod Ģu Ģekilde:
Event::fire(
‘olayAdi‘,
$opsiyonelParametre
);
Örnekler
Örnek Olay Uygulaması - 1
Gelelim örnek birkaç olay hazırlamaya. Ġlk örnekte önce bir event
tanımlayacağız, ardından da bir controller metodu üzerinden bu
eventi tetikleyip direkt çıktısını alacağız.
• Ġlk olarak app/routes.php içinde Ģöyle bir resource hazırladım:
Route::get(
'olay/test1', 'SunumController@olayTest1‘
);
• Daha sonra app/controllers/SunumController.php
dosyasını oluĢturdum, ve de içine Ģu metodu ekledim:
Örnek Olay Uygulaması - 1
• Bu metotta event’in çalıĢtığını anlamak için event’in
çıktısını direkt metodda return ile son kullanıcıya
gösteriyoruz.
Örnek Olay Uygulaması - 1
• Ardından da Ģu kodları oluĢturduğum app/events.php içine
ekledim:
Örnek Olay Uygulaması – 1
• Ardından tarayıcımdan tanımladığım resource’a gittim:
Sonuç?
Örnek Olay Uygulaması - 2
• ġimdi örneği biraz zenginleĢtirelim. Bu sefer örneğe bir
parametre göndereceğiz.
• Bunun için öncelikle yine bir resource ve metod
oluĢturdum:
• Önce Resource:
Örnek Olay Uygulaması - 2
• ġimdi de Controller:
Örnek Olay Uygulaması - 2
• ġimdi de app/events.php içine olayın kendisini
ekleyelim:
Örnek Olay Uygulaması - 2
• Tarayıcıdan navige olduğumuzda her Ģey baĢarılı olduysa
aĢağıdaki çıktıyı alacağız:
Örnek Olay Uygulaması - 2
• Burada gireceğiniz parametre bir dizi, bir nesne veya
istediğiniz baĢka bir veri olabilir. Örnekte basit olması
amacı ile statik bir metin verdim. Misal nesne girerek
kullancı her login olduğunda nesneden son giriĢ tarihi gibi
bir veriyi güncelleyebilirsiniz. Nesne içinde bir collection
vererek collection üzerinde toplu iĢlem yapabilirsiniz.
Böylece controller’ın kendisine bindirdiğiniz yük de
azalmıĢ, ve de daha derli bir altyapıya sahip olursunuz.
Örnek Olay Uygulaması - 3
• Üçüncü örnekde ufak bir farklılık yapıp psr-0 ile autoload
ettiğim app/lib/Sunum/Olaylar.php adresindeki
basit bir sınıftan çalıĢtıracağım etkinliği. Ġkinci madde ile
çıktı neredeyse aynı, fakat tetiklenen metod bir sınıf
altından olacak.
• Bu özellik bize farklı olayları türlerine göre farklı sınıflarda
gruplamamızı sağlayacak.
• Bu yolun bir eksisi de kullanılan dosya sayısının bir adet
artmıĢ olması (ilerde çözümü var ;))
Örnek Olay Uygulaması - 3
• app/routes.php içindeki resource satırım:
Route::get('olay/test3', 'SunumController@olayTest3');
• app/controllers/SunumController.php içindeki route
metodum:
public function olayTest3()
{
return Event::fire('olay.test3','Arda Kilicdagi');
}
• app/events.php içindeki olay satırım:
Event::listen('olay.test3', 'Sunum\Olaylar@tetikle');
Örnek Olay Uygulaması - 3
• Hazırladığım app/lib/Sunum/Olaylar.php dosyası:
Örnek Olay Uygulaması - 3
• Tarayıcı çıktısı:
Örnek Olay Uygulaması - 4
Bu uygulamada olayların bir diğer özelliği olan abonelik
(subscribe) özelliğinden değinmek istiyorum. Örnek 3’te
fark ettiğiniz üzere olay dinleyicileri (event listener) ları
app/events.php üzerinden tanımlamıĢtık, fakat subscribe
yöntemi ile events.php yi kullanmadan direkt sınıf
üzerinden tanımlayabiliriz. Bu hem olayların daha derli
olmasını, hem de daha taĢınabilir kodlar olmasını sağlarlar.
Bunun için olayları barındıran sınıfımıza subscribe() adında
yeni bir metod eklemeliyiz. Bu subscribe metodunun içinde
listener’lar barınacak. Bu örneğimizde 3. olayın event
listener’ını abonelik üzerinden getireceğiz.
Örnek Olay Uygulaması - 4
• app/lib/Sunum/Olaylar.php:
Örnek Olay Uygulaması - 4
• Bu olaylar’ı bir yerde tanımlamamız lazım. Bunu autoload
eden herhangi bir dosyada yapabilirsiniz. Ben Ģahsen bu
olayların sadece controller’ımızda olduğunu farz ederek
bir __construct() metodu yazıp içinde yaptım.
public function __construct() {
Event::subscribe('Sunum\Olaylar');
}
• Ve de üçüncü örneği abonelik üzerinden çağıracağımız
için app/events.php de 3. örneğin event listener’ını
yoruma dönüĢtürdüm.
//Event::listen('olay.test3', 'Sunum\Olaylar@tetikle');
Örnek Olay Uygulaması - 4
• Sonuç?
Örnek Olay Uygulaması - 4
QUEUES (KUYRUKLAR)
Queues (Kuyruklar) Nedir?
• Queue’ları «uzun sürebilecek birtakım işlemleri sıraya alma, erteleme sistemi» gibi düşünebiliriz.
• Örnekleyecek olursak; farz edelim üye kaydınızda gönderdiğiniz bir eposta bir SMTP sunucuya bağlı, ve de SMTP’ye bağlanırken kullanıcı mail gidene kadar sitede «yükleniyor kum saati»ni görmek zorunda. İşte queue sistemi bizi burada kurtarıyor.
Queues (Kuyruklar) Nedir?
Queue sistemini basitçe sunucu tarafında
uzun sürebilecek iĢlemleri erteleyip bu
süreçte son kullanıcıyı bekletmek yerine bu
iĢlemleri üstlenip bekleyen ve gerçekleĢtiren
dahili ve/veya harici sistemler gibi
düĢünebiliriz.
Queues (Kuyruklar)
Son kullanıcıya hızlı bir site deneyimi
sağlamak varken arka plan hantallıkları için
tarayıcı başında neden bekletesiniz ki?
Queues (Kuyruklar)
Laravel 4.x’te üç adet harici Queue servisine çekirdek
desteği sunulmakta. Laravel tarafından desteklenen queue
servisleri ve repoları:
• Beanstalkd: pda/pheanstalk
• Amazon SQS: aws/aws-sdk-php
• IronMQ: iron-io/iron_mq
• Senkron (yerel sürücü)
Queues (Kuyruklar)
• Seçtiğiniz repo kaynakları kurmak için öncelikle composer.json dosyanızdaki requirements kısmına reposu
eklenip composer update komutunu çalıĢtırmalısınız. Ben
sunum boyunca Iron.IO servisini kullanacağım. ġu anki
composer.json da ilgili alanım Ģu Ģekilde:
Queues (Kuyruklar)
• Iron IO’yu kullanmamın bazı sebepleri var:
• Deneme, ve baĢarısız olma durumunda yeniden deneme,
ve bu aralıkta bekleme süresi tanımlaması
• Iron.io‘ya giden ve Iron.io’dan gelen mesajları loglaması
• Ġsteklerde yük dengeleme (load balancing) uygulaması
• Queue’yu durdurmak ile veya tekrar eden kuyrukta
yüksek bellek kullanımı gibi sorunlarla uğraĢmak zorunda
kalmamak
Queues (Kuyruklar)
• ġimdi yapmamız gereken Iron.io üzerinden bir hesap
oluĢturmak ve giriĢ yapmak. Hesap oluĢturduktan sonra
giriĢ yapınca bir «proje oluĢtur» sayfası karĢılayacak sizi:
Queues (Kuyruklar)
• Proje oluĢturulduktan sonra size iron.io credentials butonu
altında projeniz için proje ID’si veve private anahtarları
verecek:
Queues (Kuyruklar)
Bu anahtarları ve hangi queue motorunun
kullanılacağını app/config/queue.php de
belirtmemiz lazım. app/config /queue.php yi
açıp içindeki default anahtarını iron olarak
değitĢirin, ve aĢağıdaki connections.iron keyi
altında token, proje ID si ve herhangi bir
queue adını (iron.io da oluĢturduğunuz
queue adı olması zorunlı değil) tanımlayın.
Queues (Kuyruklar)
ġimdi ise Iron.io’ya bir queue’muz olduğunu öğretmemiz lazım. Bunu artisan komutlarından queue:subscribe ile
yapıyoruz. Komut temelde iki zorunlu parametre alıyor:
php artisan queue:subscribe queueAdi link
• queueAdi : Konfigürasyon dosyasında verdiğimiz queue
adı.
• link: ĠĢlemin baĢlaması için gereken tetiğin nerede
olacağı.
Queues (Kuyruklar)
Unutmayın! • Eğer yerel ortamda çalıĢıyorsanız
link alanı online bir production
ortamı olmalıdır. Queue sistemi
yerel sanal hostunuza eriĢemez.
• Subscribe artisan komutu sadece
Iron MQ için bir komut. Diğer
queue servislerinin yolları ve
komutları farklı olabilir.
Queues (Kuyruklar)
php artisan queue:subscribe Laravel-Sunum http://www.laratest.com/kuyruk/deneme
• Komutu çalıĢtırdığımızda Ģunun gibi bir çıktı alacağız:
Queues (Kuyruklar)
• Aboneliğin Iron.io servisince alındığını test etmek için,
iron.io da proje menüsünden MQ Sekmesine, oradan da
Queues tab’ına tıklayın. Eğer yeni bir değer görüyorsanız
queue sistemine baĢarı ile bir isim kaydettik demektir.
Queues (Kuyruklar)
• ġimdi de bu queue servisinin sitemize gelip tetikleyeceği
resource’u hazırlayalım. Bu resource içinde öyle bir kod
yazmalıyız ki bu kod uygulama içinde eriĢeceği isme atanmıĢ bir queue yu tetiklesin. Bunun için Queue
classının marshal() metodunu kullanmalıyız. Arka
planda Laravel push edilen queue’un bu komut gelince
tetikleneceğini kendisi ayarlıyor.
• app/routes.php ye aĢağıdaki kodları ekledim:
Route::post('kuyruk/deneme',
'SunumController @kuyrukDeneme');
Queues (Kuyruklar)
• Ardından da
app/controllers/SunumController.php dosyama
Ģu kodu ekledim:
public function kuyrukDeneme()
{
return Queue::marshal();
}
• Bu aĢamadan sonra queue oluĢturup test edebiliriz:
Queues (Kuyruklar)
• Queue tetiklemek için yeni bir resource tanımladım:
Route::get(
'kuyruk/tetik1', 'SunumController@kuyrukTetik1');
Queues (Kuyruklar)
• Daha sonra ilgili controller metodunu oluĢturdum:
Queues (Kuyruklar)
• Daha sonra tarayıcımdan www.laratest.com/kuyruk/tetik1
adresine gittim. Gelen bir boĢ sayfa idi, ama arka planda
e-posta göndermekle görevlenmiĢ bu sayfa gayet hızlı
yüklendi, ve de gelen kutuma bir e-posta düĢtü
Queues (Kuyruklar)
• Bu sırada, Iron MQ’ya bakacak olursak da, canlı
güncellenen raporlamasında 1 adet push queue geldiğini,
ve de baĢarı ile iĢlendiğini göreceğiz:
Queues (Kuyruklar)
Burada sırayla olanları anlatmak gerekirse;
• Önce tarayıcıdan navige ederek get request yaptık.
• Bu get request içinde (kuyruk/test1) bir Queue::push() metodu içinde closure bir metod ile bir e-posta gönderimi kodladık.
• Queue::push() tetiklendiği zaman Laravel arka planda iĢi Iron MQ’nun tetiğine göre hazır ediyor, ve de Iron MQ’yu bir nevi «çağırıyor».
• Daha sonra Iron MQ, /kuyruk/deneme adresine bir post request yapıyor. Bu request’i Queue::marshal()’ı tetikliyor.
• Tetiklenen Queue::marshal(), Queue::push()’ta tanımlanmıĢ iĢi gerçekleĢtiriyor. Bu süreç boyunca Iron MQ iĢlemin gerçekleĢtirilmesini tıpkı son kullanıcıymıĢ da mouse imleci terazi olmuĢ gibi bekliyor oluyor.
Queues (Kuyruklar)
• Son kullanıcı da bunların hiç birini beklemediği için sitede daha hızlı geziniyor, siteyi daha verimli kullanıyor.
Queues (Kuyruklar)
• Queue::push() metodu iki parametre alır:
Queue::push(‘closureVeyaSinif‘, ‘sinifaGidecekParametreler');
Birinci parametreye ya bir closure metod, ya da autoload etmiĢ bir sınıf veya sınıf@metodadı girilmelidir. Metod adı girilmezse var sayılan olarak sınıftaki fire() metodu tetiklenir.
Ġkinci parametre eğer ki queue metodu bir sınıftan çağrıldıysa, o metoda parametre göndermek içindir.
Queues (Kuyruklar)
ġimdi de bir sınıftan çağırıp tetikleyelim sıralamayı:
• Önce route:
Route::get(
'kuyruk/tetik2',
'SunumController @kuyrukTetik2'
);
Queues (Kuyruklar)
• ġimdi de controller metodu:
Queues (Kuyruklar)
• ġimdi de daha önceden psr-0’da tanımladığımız autoload
ettiğimiz Siralar sınıfı ( app/lib/Sunum/Siralar.php ):
Queues (Kuyruklar)
• Daha sonra tarayıcımdan www.laratest.com/kuyruk/tetik2
adresine gittim.
• Tarayıcıda boĢ beyaz bir sayfa açıldı, fakat bir yeni
epostam var uyarısı gelmiĢti:
• Gelen kutum;
Queues (Kuyruklar)
• Mail sınıfı e-posta gönderimi için kuyruklamada özel bir
metod barındırıyor.
• Mail::send(...) yerine Mail::queue(...) yazarsanız ayrı bir class yazmadan e-postalar otomatik
olarak queue kullanılarak gönderilir.
Queues (Kuyruklar)
• Eğer birden fazla sıralamanız varsa ve aynı parametre(ler)i farklı sınıflara gönderecekseniz bunu her birini ayrı satır yazmak yerine Ģöyle yazabilirsiniz:
Queue::bulk(array(‘Sinif1‘, ‘Sinif2'), $data);
• Queue’ları misal 30 dakika geç baĢlatmak istiyorsanız;
$neZaman = Carbon::now()->addMinutes(30);
Queue::later($neZaman, ‘SınıfVeyaClosure‘, $data);
Buradaki 2. ve 3. parametre push() metodu ile aynı.
Queues (Kuyruklar)
• Push queue gibi bir driver değil de yerel bir sunucu
kullanırsanız kuyruklar siz durdurana kadar durmaz,
devam ederler, bu da ram’in ĢiĢmesine sebep olur.
• Yerel Queue’ların durumunu php artisan queue:listen komutu ile izleyebilirsiniz. Bu izleyici
ayrıca sıraya yeni bir kuyruk eklendiği zaman anında
çalıĢtırır.
• Queue’ları durdurmak için iĢlemi gerçekleĢtiren kodların
en altına $isAdi->delete() , yeniden baĢlatmak için
$isAdi->release() komutlarını kullanabilirsiniz.
• Sıraya eklenmiĢ ilk iĢi çalıĢtırmak için php artisan queue:work komutunu kullanabilirsiniz.
Queues (Kuyruklar)
• Laravel 4.1 ile kuyruklarda olan hataları yakalama yeni bir özellik olarak geldi. Eğer ki kuyruklarda bir sorun yaĢadıysanız bunları artık yakalayabilir, otomatik olarak veri tabanına ekleyebilir, bir handler ile bir hata olduğunu yakalayıp ona göre iĢlem (misal, geliĢtirme takımına e-posta) yapılabilir.
• Önce bir hata tablosu migrasyonu kurmak lazım, bunu
php artisan queue:failed-table
Ġle kurabilirsiniz.
Queues (Kuyruklar)
• Queue::failing(function($gorev, $veri) { /**/ });
kodu ile sistemde kuyruklarda bir hata olduğu zaman veritabanına ekleme harici üçüncü parti iĢlemlerinizi yapabilirsiniz.
• php artisan queue:failed artisan komutu size tüm kuyruk hatalarının dökümünü verecektir.
• Tüm kuyruk hata loglarını temizlemek için
php artisan queue:flush
Komutunu kullanabilirsiniz.
Laravel Application Development Blueprints
Laravel 4 hakkında bilgi almak
ve uygulamalı örnekler
arıyorsanız İbrahim YILMAZ ile
beraber hazırladığım Packt
Publishing’den çıkan son
kitabım Laravel Application
Development Blueprints’i
incelemenizi öneririm. Bu kitapta
10 bölümde 10 farklı uygulama
yazıp L4’ün tüm özelliklerinden
faydalandık:
http://bit.ly/laravel-kitap
SORULAR ?
TEġEKKÜRLER!
• Hazırlayan: Arda KILIÇDAĞI
• E-mail: [email protected]
• Twitter: @ardadev
• GitHub: ardakilic
• http://bit.ly/laravel-kitap