Özet: Laravel 11 with defer() kullanarak kodu HTTP yanıtı kullanıcıya gönderildikten sonra çalıştırarak API’nizi hızlandırır. Kuşaklar, iş sınıfları, işçi süreçleri gerekmez. Yapmanız gereken tek şey, ateşle unutun mantığıyla çalışacak olan kodunuzu defer() içine almak.
Geçtiğimiz yıl, sipariş API uç noktamızın neden 1.2 saniye sürdüğünü anlamak için iki gün harcadım. Sipariş yaklaşık 80ms içinde oluşturuluyordu. Peki geri kalan zaman nereye gidiyordu?
Görünüşe göre, bir onay e-postası göndermekte, bir analiz olayı takip etmekte, bir üçüncü taraf hizmetle envanter senkronizasyonu yapmakta ve bir önbellek anahtarını temizlemekteydik. Tüm bunlar, yanıt kullanıcıya gönderilmeden önce senkron olarak gerçekleşiyordu.
Kullanıcı, sipariş onaylarını görmek için bu işlemlerin tamamlanmasını beklemiyordu. Onlar sadece siparişin geçtiğini bilmek istiyordu.
Eski Yöntem: Her Şey İçin Kuşaklar
Eski Yöntem: Her Şey İçin Kuşaklar
Tipik öneri, bu görevleri bir kuyruğa atmaktır. Bir iş sınıfı oluşturun, işinizi gönderin, bir kuyruk işçisi çalıştırın, izleme ayarlayın, başarısız işleri yönetin. Karmaşık arka plan işlemleri olan büyük bir uygulama için bu mantıklıdır.
Ancak, bir e-posta göndermek veya bir olayı takip etmek gibi basit ateşle unut mantığındaki görevler için? Bu, basit bir şey için çok fazla altyapı.
Uygulamamızda 14 ayrı iş sınıfı vardı. Bunların sekizi sadece küçük bir şeyi yapan tek yöntemli sınıflardı. Her biri kendi dosyasına, testine ve başarısız işler tablosunda kendi kayıtlarına sahipti. Bu fazla geldi.
defer() ile Tanışın
defer() ile Tanışın
Laravel 11, defer() fonksiyonunu ekleyerek arka plan görevleri üzerindeki düşünme şeklimi değiştirdi. İşte nasıl çalışır:
Route::post('/order', function () {
$order = Order::create($data);
defer(fn() => Mail::send(new OrderConfirmation($order)));
defer(fn() => Analytics::track('order_placed', $order));
defer(fn() => InventorySync::push($order));
defer(fn() => Cache::forget("user:{$order->user_id}:cart"));
return response()->json($order);
});
Yanıt, sipariş oluşturulduktan hemen sonra kullanıcıya geri gönderilir. Sonrasında Laravel, tüm defer edilen geri çağrıları yanıt gönderildikten sonra çalıştırır. Kullanıcı bunlar için beklemez.
Hiçbir iş sınıfı yok. Hiçbir kuyruk işçisi yok. Hiçbir Redis veya veritabanı kuyruk sürücüsü yok. Yanıt sonrası çalışacak bir kapatma mekanizması var.
Ne Zaman defer() Kullanımında ve Kuyruklarda
Ne Zaman defer() Kullanımında ve Kuyruklarda
Bu, çoğu makalenin yanlış yaptığı bir kısımdır. Ya “her şey için defer kullanın” ya da “her zaman kuyrukları kullanın” derler. Gerçek daha nüanslıdır.
defer() kullanın, eğer iş basitse ve yeniden deneme mantığına ihtiyaç yoksa. Bir bildirim e-postası göndermek, bir analiz olayı takip etmek, önbelleği temizlemek, bir etkinliği günlüğe kaydetmek gibi. Eğer başarısız olursa, bunu otomatik olarak tekrar denemezsiniz.
Kuyrukları kullanın, eğer görev karmaşıksa veya güvenilirlik garantileri gerektiriyorsa. Bir ödeme işlemi yapmak, büyük bir PDF oluşturmak, binlerce kaydı harici bir API ile senkronize etmek gibi. Eğer başarısız olursa, bunu bilmek ve yeniden denemek istersiniz.
İlk başta bir webhook gönderiminde defer() kullanma hatasını yaptım. Webhook hedefi güvenilmezdi ve gönderimlerin %10’u sessizce başarısız oldu. Yeniden deneme mekanizması, başarısız iş kaydı yoktu, bunun olmasına dair hiçbir bilginiz yoktu. O işi 3 kez deneyecek bir kuyrukla geri döndürdüm ve sorun çözüldü.
Gerçek Sayılar
Gerçek Sayılar
Sipariş uç noktamızı defer() kullanacak şekilde yeniden yapılandırdığımızda, kritik olmayan görevler için:
- Yanıt süresi 1.2 saniyeden 280ms’ye düştü
- Kullanıcı percepsiyonu ile performans dramatik şekilde arttı
- 8 tane tek yöntemli iş sınıfını sildik
- Kuyruk işçisi yükü düştü çünkü daha az iş gönderiliyordu
E-posta hala gönderiliyor. Analiz olayı hala takip ediliyor. Önbellek hala temizleniyor. Kullanıcı sadece bunların hepsi için beklemiyor.
Her Projede Kullandığım Bir Model
Her Projede Kullandığım Bir Model
Yaygın istek düzeyindeki görevleri defer eden basit bir middleware sınıfı oluşturdum:
class DeferCommonTasks
{
public function handle($request, Closure $next)
{
$response = $next($request);
defer(fn() => ActivityLog::record($request));
defer(fn() => MetricsCollector::trackRequest($request, $response));
return $response;
}
}
Her istekte otomatik olarak etkinlik günlüğü kaydedilir ve metrikler takip edilir, yanıt süresine ek bir gecikme olmadan. Middleware bir kez çalışır ve her uç nokta bundan faydalanır.
Farklı Ne Yapardım
Farklı Ne Yapardım
Başlangıçtan itibaren net bir kural belirlerdim: Eğer görev yeniden deneme mantığı gerektirmiyorsa ve 5 saniyeden az sürüyorsa, defer() kullanın. Eğer yeniden denemeye ihtiyaç duyuyorsa veya daha uzun sürüyorsa, o zaman kuyruk kullanın. Bu kural, sonunda elde ettiğimiz 14 gereksiz iş sınıfının olmasını önlerdi.
Ayrıca, defer() kullanmamayı tercih ederdim. Eğer bir veritabanı işlemi geri alınırsa, defer edilmiş geri çağrılar hala çalışır çünkü yanıt sonrası çalışır. Bu durum, siparişin kaydedilemediği için sipariş onayı e-postalarının gönderilmesine neden olan bir hataya yol açtı. Bunu zor yoldan öğrendim.
Sonuç Olarak
Sonuç Olarak
defer() kuyrukların yerini almaz. “Yanıt” sonrası çalıştırılması gereken onca küçük iş sınıfının yerini alır.
Tek bir kod satırı. Hiçbir altyapı değişikliği gerektirmiyor. Ölçülebilir daha hızlı yanıtlar.
Laravel 11 veya daha sonra bir versiyon kullanıyorsanız ve hala defer() kullanmıyorsanız, muhtemelen kullanıcılarınızı bekletiyordur.
Laravel uygulamalarınızda bulduğunuz en basit performans kazanımı nedir?
Kaynak: Orijinal Makale


