Gönderen H.Levent KARAGÖL Etiketler:

Bu makalemizde, Node.JS ile geliştirdiğimiz uygulamalardaki aşırı kompleks algoritmaları farklı bir Process'e taşıyarak, mevcut Thread'i tıkamadan çalışmamıza imkan sağlayan Child Process modülünü inceleyeceğiz.

Node.JS'in tek Process ve tek Thread üzerinde çalıştığını ve bu Thread'i bloklayan bir fonksiyon çağırdığımızda, diğer tüm fonksiyonalitenin, bu fonksiyon tamamlanana kadar kilitleneceğini, Asenkron Çağrımlar isimli makalede uzun uzun konuşmuş, hatta bunun kötü bir şey olmayıp, tam tersi Node'un emsallerinden yük altında daha verimli ve performanslı çalışmasının temel sebebi olduğunun da üzerinde durmuştuk.

Hatta yine demiştik ki, istatistiklere göre mevcut Thread %99,999904982 boş duruyordu ve dış sistemlere yaptığımız çağrıları asenkron yaptığımız ve Context Switch'e girmediğimiz takdirde, tüm algoritmalarımız geriye kalan %0,000095018'lik dilimde tamamlanıyordu.

Yukarıdaki istatistikler, yazılan uygulamaların çok büyük çoğunluğunda geçerli olsa da, bazı durumlarda gerçekten de kompleks algoritmalar işletmemiz gerekebilir. Örneğin 3D Image Processing yapıyor olabiliriz, augmented reality işine girmiş olabiliriz ya da belki de Pi sayısının dibini bulmaya çalışıyoruzdur.

İhtiyaçlar değişebilir ancak bazen gerçekten de algoritmanın CPU üzerindeki geçirdiği vakit, I/O'dan fazla olabilir. Böyle durumlarda, Node'un üzerine kurulu olduğu yukarıdaki istatistikler işe yaramaz ve veli nimetimiz tek olan Thread tıkanarak başka isteklere cevap veremez hale gelir.

İşte, tam da bu tarz durumlar için hazırlanmış bir modül olan "Child Process" imdadımıza yetişir ve bu tıkaç vazifesi görecek kod bloğunu farklı bir Process'e taşıyarak orada işletmemize imkan sağlar. Biz de kendi uygulamamızdan, bu Process'e asenkron çağrımda bulunuruz ve işletilmesi gereken algoritma tamamlandığı zaman callback fonksiyonumuzu çağırmasını sağlayarak, yukarıdaki istatistiklere geri dönmüş oluruz.

"Child Process" modülü oldukça güçlü ve dikkatli kullanılması gereken bir modül. Sonuçta bu modül vasıtasıyla dilersek, Node'da yazdığımız uygulamayı 100 farklı Process halinde çalıştırabilir ve böylece 100 paralel kolda ilerleme şansına sahip oluruz ki, bu da bize klasik senkron program yazma imkanlarına kavuşturur. Bu durumda da Node.JS kullanıyor olmanın hiçbir anlamı kalmaz.

Dikkat : Bu tarz düşünce içerisinde olanlar, eğer henüz okumadılarsa, muhakkak Asenkron Çağrımlar başlıklı makaleyi okusunlar ve Node'un neden böyle tasarlandığını ve esas kuvvetli tarafının neden tek Process/Thread olduğunu çok net bir şekilde anlasınlar. Bu konuyu net bir şekilde anlayamayan insanlar, "Node.js is cancer" gibi makaleler yazıyorlar, yazık yav.

Sanırım modül hakkında yeteri kadar uyarıda bulunmuşumdur. O halde sorumluluk benden gitti. Artık modülün kullanım şeklini bir örnekle birlikte inceleyebiliriz.

"Child Process" modülü, "http" ve "fs" gibi Node içerisinde dahili olarak gelmektedir ve kullanım için npm ile herhangi bir modül indirmemize gerek yoktur.

Örneğimizde iki ayrı dosya hazırlayacağız. İlk dosyamız "program.js" adında olup, asıl programımızı temsil edecek. İkinci dosyamız ise "subProgram.cs" adında olup, kompleks algoritmamızı içeren alt programı temsil edecek.

İlk olarak aşağıdaki kodlarla "program.js" dosyamızı hazırlayalım.

Ardından aşağıdaki kodlarla "subProgram.js" dosyamızı hazırlayalım.

Programı çalıştırmadan önce yaptıklarımıza bir bakalım.


var childProcess = require("child_process");
var child = childProcess.fork("subProgram");

İlk olarak "program.js" dosyasındaki yukarıdaki kodla, Child Process olarak kullanacağımız uygulamamızı tanıttık.


child.on("message", function (message) {
    console.log("İşlem Başarılı : " + message.succeed);
    console.log("İşlem Sonucu : " + message.result);
});

Ardından yukarıdaki kodla, "subProgram" uygulamamızdan gelecek mesajı yakalamak üzere bir Event Handler fonksiyonu yazdık. Bu fonksiyonda gelecek olan parametrenin bir nesne olmasını ve içerisinde "succeed" ve "result" adında iki değişken içermesini bekledik ki, bunları da konsol ekranına yazdırdık. Buradaki gelecek nesne yapısı tamamen ihtiyaca göre belirlenebilir.


child.send({ value1: 7, value2: 8 });

Son olarak yukarıdaki kodla da, "subProgram" uygulamamıza mesaj gönderdik ve parametre olarak içerisinde "value1" ve "value2" değişkenlerini içeren bir nesne gönderdik.


Gelelim "subProgram.js" dosyamıza.


process.on("message", function (params) {
    var total = params.value1 + params.value2;
    var startTime = new Date().getTime();
    while ((new Date().getTime() - startTime) < 5000)
    {
        total += (params.value1 * params.value2);
    }
    process.send({ succeed: true, result: total });
    process.exit();
});


Burada asıl uygulamamızdan "send" fonksiyonu ile gönderdiğimiz mesajı yakalayan bir Event Handler fonksiyonu yazdık. Fonksiyon içerisinde 5 saniye boyunca sürekli olarak gelen iki değeri birbiri ile çarpıp, önceki çarpımlar ile topluyoruz ve "process.send" fonksiyonu vasıtasıyla kümülatif toplamı ve işlemin başarılı sonuçlandığını bildiren bir değeri çağrım yapan asıl uygulamaya geri dönüyoruz. İşlem tamamlandığı durumda buradaki "process.exit()" fonksiyonu çağrımı önemli, aksi takdirde açılan Child Process ayakta kalmaya devam edecektir.

Uygulamamızı aşağıdaki komutla ayağa kaldıralım.

node program.js

Her şey yolunda gittiyse, beş saniye içerisinde aşağıdaki gibi bir ekran çıktısı ile karşılaşmış olman gerekiyor.



Bir de işin Process kısmını inceleyelim. Uygulamanın çalıştığı beş saniye içerisinde Windows'ta Task Manager veya Linux'da System Monitor vasıtasıyla çalışan Process'lere baktığımızda, aşağıdaki gibi bir görüntü ile karşılaşırız.




Görüldüğü üzere, Node'un iki ayrı Process'i bizim için ayağa kalkmış durumda. Böylece işletilecek Thread bloklayıcı fonksiyonlar farklı bir Process'e taşındığı için, üzerinde bulunduğumuz Thread'i bloklamıyor ve fonksiyon çağrımından sonra sorunsuzca diğer işlerle uğraşabiliyoruz.

Not : Açılan her yeni Process'in, boş dursa dahi bir miktar bellek tüketeceğini unutmayalım.

Gördüğün gibi Child Process modülü oldukça güçlü ve pek çok kritik yerde sorun çözücü olarak verilmiş bir nimet. Bu şekilde birden fazla Child Process açmamız da mümkün. Ancak daha önce de uyardığım gibi, bu modülün aşırı kullanımı ile Node'un çalışma prensibinin dışına çıkma tehlikesi ile karşı karşıyayız. Bu yüzden dengeyi oturtmak çok önemli.

Böylece kısa ama önemli bir makalenin de sonuna geldik. Makaleyi tamamlarken öğrendiklerimizi özetleyelim.
  • Node'un çalışma prensibi gereği, Thread bloklayıcı kompleks algoritmaları çalıştırdığımızda, Node'un tıkanma ve başka isteklere cevap verememe gibi sıkıntıları ile karşılaşırız.
  • "Child Process" modülü vasıtasıyla, bu algoritmaları farklı bir Process'e taşıyarak orada çalışmasını ve işlemini bitirdikten sonra bize haber vermesini sağlayabiliriz.
  • İhtiyaca yönelik olarak birden fazla Child Process açabiliriz.
  • Açılan Child Process'lerin, işlerini tamamladıktan sonra kendilerini "process.exit()" fonksiyonu ile kapatması gerektiğini unutmamalıyız.
  • "Child Process" modülünün aşırı kullanımı sebebiyle Node'un çalışma prensibinin dışına çıkma tehlikesi ile karşı karşıyayız. Bu yüzden, kullanmadan önce "Başka bir yolu yok mu?" diye düşünmek ve dengeyi oturtmak çok önemlidir.