Geleceğin Özelliği: Node.js’de Worker Thread’ler

10.5 sürümünden bu yana Node.js, deneysel bir özellik olarak sözde çalışan iş parçacıkları sağlamıştır. Bu blog makalesi, özelliği kısaca tanıtmaktadır.


JavaScript kodunun Node.js’de tek bir iş parçacığı içinde çalıştığı bilinmektedir. Bu iş parçacığında, olay kuyruğu adı verilen olay döngüsü çalışır – olay kuyruğundan gelen istekleri sürekli olarak kontrol eden ve giriş ve çıkış işlemlerinden gelen olayları işleyen bir döngü.

Örneğin, bir kullanıcı Node.js tabanlı bir web sunucusuna istekte bulunursa, olay döngüsü önce isteğin engelleme giriş veya çıkış işlemi gerektirip gerektirmediğini kontrol eder. Bu durumda, birkaç dahili Node.js çalışan düğümünden biri (prensipte ayrıca iş parçacıklarıdır, ancak Node.js’nin içindedir) etkinleştirilir ve görevi gerçekleştirir. Girdi veya çıktı işlemi tamamlanır tamamlanmaz, ilgili geri çağırma işlevi aracılığıyla bu konuda bilgilendirileceksiniz.

Buradaki belirleyici faktör, giriş ve çıkış işlemlerinin engellenmesi sırasında ana iş parçacığının bloke edilmemesidir: olay döngüsü kesintiye uğramadan çalışmaya devam eder ve bu nedenle gelen istekleri hemen işleyebilir. Çok uzak çok iyi.

Bununla birlikte, örneğin şifreleme ve veri sıkıştırma gibi CPU-yoğun hesaplamalar nedeniyle ana iş parçacığının engellenmesine yol açan durumlar da vardır.

Buna karşı koymak için çeşitli yaklaşımlar vardır:

  • Hesaplama indir: Burada karmaşık hesaplamalar, örneğin ilgili mesajların bir mesaj aracısına gönderilmesi ve bunların aracıya kayıtlı diğer hizmetler tarafından işlenmesi gibi diğer hizmetlere devredilir.
  • bölümleme: Karmaşık hesaplamalar, daha sonra olay döngüsünün birkaç turunda birbiri ardına işlenen birkaç bölüme ayrılmıştır. Genellikle karşılık gelen hesaplama, daha sonra işlev aracılığıyla kullanılan bir işleve dışsallaştırılır. hemen ayarla() olay döngüsünün “kontrol aşamasında”.
  • Arka plan işlemleri yoluyla kümeleme: Bu, işlev kullanılarak yapılır çatal() dan child_process-Paket, hesaplamasını bir alt sürece devreder, burada ana süreç ile alt süreç arasındaki iletişim için IPC (Süreçler Arası İletişim) kullanılır. Worker-farm gibi paketler, alt süreçleri oluşturmayı ve yönetmeyi kolaylaştırır, böylece süreç gruplaması ve alt süreçlerin yeniden kullanımı gibi şeyler hakkında endişelenmenize gerek kalmaz. Bununla birlikte, iş parçacıklarının temel dezavantajları devam etmektedir: iş parçacıklarına kıyasla, yoğun bellek ve yavaştırlar.
işçi konuları



Node.js 10.5’ten bu yana çalışan iş parçacıkları biçiminde başka bir çözüm mevcuttur. Worker Threads API, dosyada JavaScript kodunu çalıştırmanıza izin verir aynı süreç ana iş parçacığı ile paralel olarak çalıştırın.

Aşağıdaki sınıflar/nesneler API tarafından sağlanır:

  • işçiler: Bir çalışan iş parçacığını temsil eder.
  • Mesaj Kanalı: Çalışan iş parçacıklarının birbirleriyle ve ana iş parçacığıyla iletişim kurduğu bir iletişim kanalını temsil eder.
  • Mesaj Bağlantı Noktası: bir iletişim kanalının bir ucunu temsil eder, dolayısıyla her iletişim kanalında bu mesaj bağlantı noktalarından iki tane bulunur.
  • işçiVerileri: Bir çalışan iş parçacığına iletilen ve bu nedenle içinde bulunan veri nesnesi.
  • ebeveyn bağlantı noktası: bir çalışan iş parçacığı içinde, ana iş parçacığı ile iletişim kurmak için kullanılabilecek iletişim kanalı.
Açıklamak için, önce henüz çalışan iş parçacığı olmayan bir örneğe bakalım. Aşağıdaki liste, belirli bir n sayısı için 1’den n’ye kadar olan sayıların toplamını hesaplayan Gauss toplama formülünün basit bir uygulamasını göstermektedir.

const n = process.argv[2] || 500;
const sum = (n) => {
let sum = 0;
for (let i = 0; i < n; i++) {
sum += i;
}
return sum;
};

async function run() {
const result = sum(n);
console.log(result);
}

setInterval(() => {
console.log('Hello world');
}, 500);

run().catch((error) => console.error(error));


İşlev nispeten küçük n için oldukça hızlı dönse de, örneğin 50.000.000’lik bir n’nin hesaplanması birkaç saniye sürer. Hesaplama ana iş parçacığı içinde yapıldığından, bu süre boyunca bloke edilir. Sonuç: “Merhaba Dünya” mesajı setInterval() Her 500 milisaniyede bir verilmesi gerekir, yalnızca önceki hesaplamanın sonucu bilindiğinde verilir. Çıktı o zaman:

$ node start.js 50000000
1249999975000000
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world

Bu arada, özelliği kullanırsanız bu sorunla ilgili hiçbir şey değişmez toplam() asenkron olarak uygulandı. Aşağıdaki kod aynı sonuca yol açar. Yine “Merhaba Dünya” mesajlarının çıktısı ancak hesaplama sonucu belirlendikten sonra başlar.

const n = process.argv[2] || 500;
const sum = async (n) => {
return new Promise((resolve, reject) => {
let sum = 0;
for (let i = 0; i < n; i++) {
sum += i;
}
resolve(sum);
});
};

async function run() {
const result = await sum(n);
console.log(result);
}

setInterval(() => {
console.log('Hello world');
}, 500);

run().catch((error) => console.error(error));


Çalışan iş parçacıklarıyla, örnektekiler gibi karmaşık hesaplamalar, ana iş parçacığından bir çalışan iş parçacığına dışarıdan yaptırılabilir. Aşağıdaki liste, örnek için ana iş parçacığının gerektirdiği kodu gösterir. Yapıcıya yapılan bir çağrı, bir çalışan iş parçacığı başlatmak için yeterlidir işçiler işçi iş parçacığında yürütülecek kodu içeren dosyaya bir yol iletirsiniz (biraz daha fazlası). Bir yapılandırma nesnesi, diğer şeylerin yanı sıra, dosyanın yardımıyla ikinci bir parametre olarak iletilebilir. işçiVerileriverileri çalışan iş parçacığına iletmek için özellikler (örnekte n bu şekilde iletilir).

const { Worker } = require('worker_threads');
const n = process.argv[2] || 500;

function runService(workerData) {
return new Promise((resolve, reject) => {
const worker = new Worker('./worker.js', { workerData });
worker.on('message', resolve);
worker.on('error', reject);
worker.on('exit', (code) => {
if (code !== 0)
reject(new Error(`Worker stopped with exit code ${code}`));
});
});
}

async function run() {
const result = await runService({
n
});
console.log(result.result);
}

setInterval(() => {
console.log('Hello world');
}, 500);

run().catch((error) => console.error(error));


Aşağıdaki liste, çalışan iş parçacığının kodunu gösterir. üzerinde işçiVerileri iletilen parametre n, işlevin sonucu (değişmedi), kullanılabilir. toplam() yöntemi ile yapılan hesaplamadan sonradır. mesaj sonrası() ana konuya gönderildi.

const { workerData, parentPort } = require('worker_threads');
const sum = async (n) => {
return new Promise((resolve, reject) => {
let sum = 0;
for (let i = 0; i < n; i++) {
sum += i;
}
resolve(sum);
});
};

const { n } = workerData;
(async () => {
const result = await sum(n);
parentPort.postMessage({ result, status: 'Done' });
})();


Aşağıdaki program çıktısından da görebileceğiniz gibi “Merhaba Dünya” mesajları anında çıkarken, toplam()işlev hesaplanır (not: Node.js sürümüne bağlı olarak, dosyayı belirterek aşağıdaki kod belirtilmelidir) –deneysel-çalışan bayraklar başlatılır – ancak Node.js 12’de kod bu belirtim olmadan da çalışır).

$ node start.js 50000000
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
Hello world
1249999975000000
Hello world
Hello world
Hello world
Hello world
Hello world
Çözüm


Çalışan iş parçacıkları, JavaScript kodunun ana iş parçacığından bağımsız olarak çalışmasına izin vererek karmaşık hesaplamalar sırasında ana iş parçacığı kilitlenmesini önleyen deneysel bir Node.js özelliğidir.


()



Haberin Sonu
 
Üst