Bunu yaşayan biri olarak söyleyeyim, Geçen kış, Şubat 2025’te İstanbul’daki bir fintech ekibiyle konuşurken aynı cümleyi üç ayrı kişiden duydum: “Mutex yüzünden saç baş yoluyoruz.” Haklıydılar. İşin aslı şu ki, eşzamanlı sistemlerde mesele çoğu zaman hız değil; kimin neyi, ne zaman değiştirdiği. Rust burada bayağı iyi bir kapı açıyor. Actor modeli de tam bu noktada devreye giriyor: durumu paylaşmak yerine mesajla konuşuyorsun. Daha az kavga, daha az kilit, daha az sürpriz.
Ben bu yaklaşımı ilk kez 2023’te kendi log işleme denememde test etmiştim. Küçük bir servis düşünün; gelen olaylar bazen fena halde artıyor, bazen sakinleşiyor. Başta klasik yöntemle gittim: ortak state, birkaç mutex, biraz da “idare eder” diye düşündüğüm optimizasyonlar… Sonuç? Gayet tanıdık. Gecikme sıçradı, hata ayıklama uzadı, ekip birbirine baktı. Sonra actor modeline geçince tablo değişti; her parça kendi alanında çalıştı, veri akışı da daha temiz oldu.
Bakın, burayı atlarsanız yazının kalanı anlamsız kalır.
Neden Actor Modeli Hala Bu Kadar Ise Yariyor?
İnanın, Bakın şimdi, actor modelini teorik bir akademik oyuncak gibi görmek kolay. Ama pratikte çok net bir derdi çözüyor: paylaşılan mutable state’i ortadan kaldırıyor. Yani iki iş parçacığı aynı veriye aynı anda abanıp sistemi dağıtmıyor. Rust’ın sahiplik kurallarıyla birleşince bu yapı bayağı sağlam duruyor.
Bir de şu var: actor yaklaşımı sadece güvenlik değil, mimari temizlik de veriyor. Her actor kendi küçük dünyasında yaşıyor; log işliyor olabilir, ödeme kuyruğu yönetiyor olabilir ya da IoT sensörlerinden veri topluyor olabilir. Hani mutfakta herkes aynı tencereye kaşık sokmasın diye ayrı kaplar kullanırsınız ya — mantık biraz öyle.
Şöyle söyleyeyim, Küçük startup tarafında bu yaklaşım hızlı deneme yapmayı kolaylaştırıyor. Enterprise seviyede ise asıl avantaj gözle görünür hale geliyor: servisler arası sınırlar netleşiyor, izleme kolaylaşıyor ve tek bir hatalı işlem bütün sistemi sürüklemiyor. E tabi her şey güllük gülistanlık değil; mesaj tasarımını kötü yaparsanız sisteminiz “esnek” değil “karışık” olur.
Paylasilmis state yerine mesaj gecmek
Aslında — dur bir saniye, önce şunu söyleyeyim — paylaşılmış state tamamen kötü değil. Ama büyüdükçe maliyeti artıyor. Mesajlaşma ise işi biraz yavaşlatabilir ama karşılığında kontrol veriyor. Hele bir de yüksek trafikli backend’lerde bu takas çoğu zaman değerli oluyor.
Açıkçası, Ben bunu 2024 sonbaharında Ankara’da bir SaaS projesinde tekrar gördüm. Takımda iki kişi aynı cache katmanına dokununca debug süresi uzuyordu; actor tabanlı küçük bir kuyruk katmanı ekledikten sonra sorunların yarısı kendiliğinden kayboldu desem abartmış olmam.
Mesajlari Net Tanimlamak Neden Önemli?
Actor modelinin bel kemiği mesaj tipleri. Mesaj ne kadar açık olursa sistem o kadar okunur oluyor. Rust burada zaten doğal olarak yardım ediyor çünkü enum ve struct ile kontratı sert biçimde tanımlıyorsunuz.
#[derive(Debug, Clone)]
pub enum LogEvent {
Warning { message: String },
Error { code: u32, message: String },
}
pub struct ProcessAck {
pub id: u64,
pub status: String,
}
Bu örnekte güzel olan şey şu: gönderen taraf ne yolladığını biliyor, alan taraf da ne beklediğini biliyor. “Acaba burada string mi geliyordu integer mı?” sorusu ortadan kalkıyor veya en azından derleme aşamasında yakalanıyor.
| Yaklaşım | Artısı | Eksiği |
|---|---|---|
| Müşterek mutable state + mutex | Daha direkt görünür | Kilit karmaşası ve deadlock riski var |
| Actor + message passing | Daha izole ve güvenli | Tasarlaması ilk başta biraz disiplin istiyor |
| Kanal tabanlı hibrit yapı | Trafik kontrolü iyi olur | Aşırı kuyrukta gecikme yaratabilir |
Neyse uzatmayalım; mesele sadece tip güvenliği değil. Mesajları düzgün kurarsanız ileride yeni event türleri eklemek de kolaylaşıyor. Kötü kurarsanız her yeni alan için kodun yarısını elden geçirmeniz gerekir… işte o kısmı pek eğlenceli değil. Bu konuyla ilgili C++ Neden Hâlâ Ayakta? Stroustrup’tan Gelen Dersler yazımıza da göz atmanızı tavsiye ederim.
Tokio Kanallariyla Akışı Kurmak
Tokio’nun mpsc kanalı bu iş için gayet uygun bir omurga sağlıyor çünkü üretici ile tüketiciyi ayırıyorsunuz. Gönderen taraf mesaj basıyor, alıcı taraf kendi ritminde işliyor. Bounded channel kullanınca da backpressure devreye giriyor; yani sistem “dur bakalım biraz yavaşla” diyebiliyor.
Çok konuştum, örnekle göstereyim. Bu konuyla ilgili MCP Ekosistemi Nereye Gidiyor: Hız, Açıklar, Güvenlik yazımıza da göz atmanızı tavsiye ederim.
let (sender, mut receiver) = mpsc::channel(256);
let actor = LogActor {
id: 1,
buffer: Vec::new(),
};
let worker_handle = tokio::spawn(async move {
while let Some(msg) = receiver.recv().await {
let _ = actor.handle(msg).await;
}
});
Bunu ilk kez canlıya yakın ortamda denediğimde fark ettiğim şey şu oldu: kanal dolunca uygulama çökmedi; sadece baskıyı geri itti. Bu küçük detay bile operasyonel tarafta büyük rahatlık veriyor. Kısacası queue taşarsa sistem size bağırmadan önce fren yapabiliyorsunuz. dissectml: EDA ile AutoML Arasındaki Eksik Parça yazımızda bu konuya da değinmiştik.
Nerede iyi çalışır?
Araya gireyim: Küçük projelerde log toplama, e-posta kuyruğu ya da arka plan job’ları için çok uygun.
Orta ölçekte ise ödeme öncesi doğrulama adımları veya telemetri toplama gibi parçalarda iyi sonuç veriyor.
Enterprise tarafta da servis içi orkestrasyon için kullanılabiliyor. Orada observability şart; yoksa aktörler konuşuyor ama siz duymuyorsunuz.
İşte o zaman sıkıntı başlıyor. OpenAI’dan Güvenlik İçin Yeni Hamle: Fellowship Programı Ne Anlatıyor? yazımızda da bu konuya değinmiştik. AI Ajanlar Neden Yalan Söyler: Asıl Ders Ne? yazımızda da bu konuya değinmiştik.
Aktorun Ic Dunyasi Nasıl Görünüyor?
Aktor dediğimiz şey aslında küçük bir kapsül gibi düşünebilirsiniz.
Kendi buffer’ı var mı? Var.
Kendi kimliği var mı? Var.
Dışarıdan gelip kafasına göre state değiştiren biri var mı? Yok.
Tam olarak güzelliği burada yatıyor zaten.
İyi tasarlanmış bir actor sistemi size hızdan önce düzen verir; düzen geldikten sonra hız zaten çoğu zaman arkadan geliyor!
Aslında, handle fonksiyonunda gelen event’e göre davranış değiştiriyorsunuz.
Warning gelirse yazdırıyorsunuz.
Error gelirse başka yol izliyorsunuz.
Bazı event’leri de direkt ignore edebiliyorsunuz.
Basit görünüyor ama ölçeklenebilirlik hissini veren tam da bu sadelik oluyor.
Biraz LEGO gibi (eh, fena değil). parçalar net olunca kombinasyon artıyor.
Kilit yoksa sorun biter mi?
Açık konuşayım:
Hayır. Her problem sihirli biçimde çözülmüyor — Mesela yanlış boyutlandırılmış kanal kapasitesi gecikme yaratabilir. Ya da fazla sayıda actor açarsanız scheduler üstünde gereksiz yük oluşabilir. Yani özgürlük geliyor ama bedava değil. Bence en can alıcı nokta burası:
- Mesaj formatını erken sabitleyin ama aşırı katı olmayın.
- Kanal kapasitesini trafik profiline göre ayarlayın.
- Error path’i baştan düşünün; yoksa sonradan yamamak zor olur.
- Loglama ve metrikleri unutmayın çünkü async dünyada sessizlik bazen kandırıcıdır. (bu kritik)
Küçük Startup ile Büyük Kurum Ayni Seyi Mi Yapar?
Küçük startup tarafında hedef genelde hızlı çıkmak oluyor. Burada actor modeli çok şatafatlı tasarlanmaz; sade tutulur. Örneğin tek bir ingest servisi içinde birkaç worker aktörü yeterli. Hatta bazen fazla soyutlama yapmak zarar bile verebilir çünkü ekip küçüktür ve herkesin zihinsel yükü sınırlıdır. Bir startup’ta ben olsam önce kritik dar boğazları actor’a taşırım…
Büyük kurumda ise tablo değişir.
Orada asıl mesele ekiplerin birbirine çarpmaması olur.
Actor sınırları servis sınırlarına benzer şekilde çizilir. Bağımlılıklar kontrollü gider.
Kurumsal projede geçen yıl gördüğüm örnekte mesela 14 mikroservis arasındaki kuyruk akışı tek merkezden yönetiliyordu; yarısı zaten gereksiz karmaşıktı.
Actor yaklaşımı o karmaşayı azaltmak için fena değildi ama tasarım disiplini istiyordu.
Gel gelelim her şeyi actor’a çevirmek de doğru değil; bazı işler düz fonksiyonlarla daha temiz kalıyor.”
Sahada Karsiligi Olan Artilar Ve Eksiler
” />
“””
“
Bu içerik işinize yaradı mı?
Benzer içerikleri kaçırmamak için beni sosyal medyada takip edin.



