Углубленное понимание процесса и потока в node.js

Node.js внешний интерфейс
Углубленное понимание процесса и потока в node.js

предисловие

进程а также线程Это must-know программисту.Часто спрашивают об интервью, но в некоторых статьях речь идет только о теоретических знаниях.Может быть, некоторые друзья не очень понимают это, и они редко используются в реальной разработке. Помимо введения концепций, эта статья объясняет с точки зрения Node.js进程а также线程, и объясните некоторые практические применения в проекте, чтобы вы могли не только встретиться лицом к лицу с интервьюером, но и идеально применить его в реальном бою.

Путеводитель по статьям

Об авторе: koala, сосредоточив внимание на совместном использовании полного стека технологий Node.js, от JavaScript до Node.js, до серверной базы данных, я желаю вам стать отличным старшим инженером Node.js. [Руководство по развитию программиста] Автор, блог Github с открытым исходным кодомGitHub.com/koala-co Nth…

интервью будет спрашивать

Является ли Node.js однопоточным?

Как избежать блокировки, когда Node.js выполняет трудоемкие вычисления?

Как Node.js реализует открытие и закрытие нескольких процессов?

Может ли Node.js создавать потоки?

Как вы реализовали защиту процесса во время разработки?

Помимо использования сторонних модулей, упаковывали ли вы многопроцессорную архитектуру самостоятельно?

обработать

обработатьProcessЭто работающая программа на компьютере с определенным набором данных. Это базовая единица распределения ресурсов и планирования в системе. Это основа структуры операционной системы. Процесс представляет собой контейнер потоков (от Энциклопедия). Процесс — это наименьшая единица распределения ресурсов. Когда мы запускаем службу и запускаем экземпляр, мы запускаем процесс службы.Например, сама JVM в Java является процессом.node app.jsПри запуске сервисного процесса мультипроцесс является копией (форком) процесса.Каждый процесс из форка имеет свой независимый пространственный адрес и стек данных.Один процесс не может получить доступ к переменным и структурам данных, определенным в другом процессе.Только через связь IPC данные могут быть разделены между процессами.

  • Пример процесса запуска Node.js
const http = require('http');

const server = http.createServer();
server.listen(3000,()=>{
    process.title='程序员成长指北测试进程';
    console.log('进程id',process.pid)
})

После запуска приведенного выше кода ниже показан эффект, отображаемый инструментом мониторинга «Мониторинг активности», который поставляется с системой Mac, вы можете увидеть процесс Nodejs, который мы только что открыли 7663.

нить

Поток — это наименьшая единица, которую операционная система может выполнять для планирования операций.Прежде всего, мы должны знать, что поток принадлежит процессу и включен в процесс.Поток может принадлежать только одному процессу, но процесс может иметь несколько потоков..

один поток

Одиночный поток — это процесс, в котором открыт только один поток.

Javascript - это один поток, программа выполняется последовательно (не говоря уже о JS асинхронном здесь), вы можете представить себе очередь, после выполнения предыдущего может выполняться последний, когда вы программируете на однопоточном языке, не тратьте слишком много времени на синхронную работу, иначе поток заблокируется и последующий ответ не сможет быть обработан. Если вы используете Javascript для кодирования, пожалуйста, максимально используйте функцию асинхронной работы Javascript.

Пример времени блокировки потоков

const http = require('http');
const longComputation = () => {
  let sum = 0;
  for (let i = 0; i < 1e10; i++) {
    sum += i;
  };
  return sum;
};
const server = http.createServer();
server.on('request', (req, res) => {
  if (req.url === '/compute') {
    console.info('计算开始',new Date());
    const sum = longComputation();
    console.info('计算结束',new Date());
    return res.end(`Sum is ${sum}`);
  } else {
    res.end('Ok')
  }
});

server.listen(3000);
//打印结果
//计算开始 2019-07-28T07:08:49.849Z
//计算结束 2019-07-28T07:09:04.522Z

Чтобы увидеть распечатку, когда мы позвоним127.0.0.1:3000/computeКогда для вызова других адресов маршрутизации, таких как 127.0.0.1/, требуется около 15 секунд, можно также сказать, что пользователь запрашивает первый адрес.computeОжидание после интерфейса занимает 15 секунд, что крайне недружелюбно к пользователям. Ниже я создам многопроцессный методchild_process.forkа такжеclusterДля решения этой проблемы.

Некоторые описания отдельных потоков

  • Хотя Node.js — это однопоточная модель, она основана на управляемом событиями, асинхронном и неблокирующем режиме, который можно применять к сценариям с высоким параллелизмом, избегая накладных расходов на ресурсы, вызванных созданием потока и переключением контекста между потоками.
  • Когда ваш проект требует большого количества вычислений и операций, отнимающих много времени на ЦП, вам следует обратить внимание на возможность открытия нескольких процессов для его завершения.
  • В процессе разработки Node.js ошибки приводят к завершению работы всего приложения, а надежность приложения стоит проверить, особенно когда возникает неправильное исключение, а защита процесса является обязательной.
  • Один поток не может использовать преимущества многоядерных ЦП, но позже API, предоставляемый Node.js, и некоторые сторонние инструменты были соответствующим образом решены, что будет обсуждаться далее в статье.

Процессы и потоки в Node.js

Node.js — это рабочая среда Javascript на стороне сервера, построенная на движке V8 Chrome, основанная на управляемой событиями модели неблокирующего ввода-вывода, в полной мере использующей асинхронный ввод-вывод, предоставляемый операционной системой. система для многозадачного выполнения, подходящая для сценариев приложений с интенсивным вводом-выводом, из-за асинхронности программе не нужно блокировать ожидание возврата результата, но на основе механизма уведомления обратного вызова время ожидания в исходный синхронный режим можно использовать для обработки других задач,

Популярная наука: с точки зрения веб-серверов знаменитый Nginx также использует этот режим (управляемый событиями), который позволяет избежать накладных расходов на создание многопоточных потоков и переключение контекста потоков.Nginx написан на языке C и в основном используется в качестве высокопроизводительного -производительный веб-сервер. Не подходит для бизнеса.

В веб-разработке, если у вас есть сценарии приложений с высокой степенью параллелизма, Node.js будет для вас хорошим выбором.

В системе с одноядерным процессором мы используем для разработки однопроцессорный + однопоточный режим. В системах с многоядерными процессорами можноchild_process.forkОткрытие нескольких процессов (Node.js добавил Cluster после v0.8 для реализации многопроцессорной архитектуры), то есть многопроцессный + однопоточный режим. Примечание. Включение многопроцессорности предназначено не для решения проблемы высокого параллелизма, а в основном для решения проблемы недостаточной загрузки ЦП Node.js в однопроцессном режиме и полного использования производительности многоядерного ЦП.

Процессы в Node.js

модуль процесса

Процесс Process в Node.js — это глобальный объект, который можно использовать напрямую, без запроса, предоставляя нам соответствующую информацию в текущем процессе. В официальной документации есть подробные инструкции, а желающие могут сами попрактиковаться в документации Process.

  • process.env: переменные окружения, например черезprocess.env.NODE_ENVПолучить информацию о конфигурации проекта для различных сред
  • process.nextTick: речь идет оEvent Loopчасто упоминается
  • process.pid: получить идентификатор текущего процесса
  • process.ppid: родительский процесс, соответствующий текущему процессу
  • process.cwd(): Получить текущий рабочий каталог процесса,
  • process.platform: Получить платформу операционной системы, на которой запущен текущий процесс.
  • process.uptime(): время работы текущего процесса, например: значение времени безотказной работы демона pm2
  • События процесса:process.on(‘uncaughtException’, cb)захватить информацию об исключении,process.on(‘exit’, cb)Монитор выхода из процесса
  • Три стандартных потока:process.stdoutстандартный вывод,process.stdinстандартный ввод,process.stderrстандартный вывод ошибок
  • process.titleУкажите имя процесса, иногда нужно указать имя процесса

Выше перечислены только некоторые из часто используемых функциональных точек.Помимо процесса, Node.js также предоставляет модуль child_process для работы с дочерними процессами.Создание процесса Nodejs будет описано ниже.

Создание процесса Node.js

Существует множество способов создания процесса.В этой статье для объяснения используются модуль child_process и модуль кластера.

модуль дочернего_процесса

child_process — это встроенный модуль Node.js, адрес официального сайта:

Адрес официального сайта child_process:узел будет .capable/api/child_afraid...

Несколько часто используемых функций: четыре пути

  • child_process.spawn(): Подходит для возврата больших объемов данных, таких как обработка изображений, обработка двоичных данных.
  • child_process.exec(): Применимо к небольшому объему данных. Значение maxBuffer по умолчанию равно 200 * 1024. Превышение этого значения по умолчанию приведет к сбою программы. Если объем данных слишком велик, можно использовать спавн.
  • child_process.execFile():аналогичныйchild_process.exec(), разница в том, что его нельзя выполнить через оболочку, и не поддерживаются такие действия, как перенаправление ввода-вывода и поиск файлов.
  • child_process.fork(): Создание новых процессов. Процессы независимы друг от друга. Каждый процесс имеет свой собственный экземпляр V8 и память. Системные ресурсы ограничены. Не рекомендуется создавать слишком много дочерних процессов. Число** настроек.

Здесь подробно объясняется количество ядер ЦП.Вилка действительно может открывать несколько процессов, но не рекомендуется создавать слишком много процессов.Как получить количество ядер ЦПconst cpus = require('os').cpus();, Здесь cpus возвращает массив объектов, который содержит информацию о каждом установленном ЦП/ядре, массив суммы двух. Если предположить, что хост оснащен двумя ЦП, каждый из которых имеет 4 ядра, общее количество ядер равно 8.

fork для запуска дочернего процесса Demo

Форк запускает подпроцесс для устранения блокировки потока, вызванной трудоемкими вычислениями в начале статьи. Создайте подпроцесс, когда выполняются вычисления, и вычисление подпроцесса завершается черезsendметод отправляет результат основному процессу, который передаетmessageОбработка и выход после прослушивания информации.

fork_app.js

const http = require('http');
const fork = require('child_process').fork;

const server = http.createServer((req, res) => {
    if(req.url == '/compute'){
        const compute = fork('./fork_compute.js');
        compute.send('开启一个新的子进程');

        // 当一个子进程使用 process.send() 发送消息时会触发 'message' 事件
        compute.on('message', sum => {
            res.end(`Sum is ${sum}`);
            compute.kill();
        });

        // 子进程监听到一些错误消息退出
        compute.on('close', (code, signal) => {
            console.log(`收到close事件,子进程收到信号 ${signal} 而终止,退出码 ${code}`);
            compute.kill();
        })
    }else{
        res.end(`ok`);
    }
});
server.listen(3000, 127.0.0.1, () => {
    console.log(`server started at http://${127.0.0.1}:${3000}`);
});

fork_compute.js

Для примера, который нужно рассчитать в начале статьи, мы создаем подпроцессы и разделяем их для выполнения операций по отдельности.

const computation = () => {
    let sum = 0;
    console.info('计算开始');
    console.time('计算耗时');

    for (let i = 0; i < 1e10; i++) {
        sum += i
    };

    console.info('计算结束');
    console.timeEnd('计算耗时');
    return sum;
};

process.on('message', msg => {
    console.log(msg, 'process.pid', process.pid); // 子进程id
    const sum = computation();

    // 如果Node.js进程是通过进程间通信产生的,那么,process.send()方法可以用来给父进程发送消息
    process.send(sum);
})
кластерный модуль

Демонстрация открытого дочернего процесса кластера

const http = require('http');
const numCPUs = require('os').cpus().length;
const cluster = require('cluster');
if(cluster.isMaster){
    console.log('Master proces id is',process.pid);
    // fork workers
    for(let i= 0;i<numCPUs;i++){
        cluster.fork();
    }
    cluster.on('exit',function(worker,code,signal){
        console.log('worker process died,id',worker.process.pid)
    })
}else{
    // Worker可以共享同一个TCP连接
    // 这里是一个http服务器
    http.createServer(function(req,res){
        res.writeHead(200);
        res.end('hello word');
    }).listen(8000);

}
Анализ кластерного принципа

Модуль кластера вызывает метод fork для создания дочернего процесса, который является тем же методом, что и fork в child_process. Модуль кластера использует классическую модель ведущий-ведомый. Кластер создаст главный, а затем скопирует несколько подпроцессов в соответствии с указанным вами номером. Вы можете использоватьcluster.isMasterАтрибут определяет, является ли текущий процесс ведущим или рабочим (рабочим процессом). Все дочерние процессы управляются главным процессом.Главный процесс не отвечает за обработку конкретных задач, и его основная задача заключается в планировании и управлении.

Модуль кластера использует встроенную балансировку нагрузки, чтобы лучше справляться с давлением между потоками.Round-robinАлгоритмы (также известные как циклические алгоритмы). При использовании стратегии циклического планирования мастер accept() все входящие запросы на подключение, а затем отправляет соответствующую обработку запроса TCP выбранному рабочему процессу (который по-прежнему обменивается данными через IPC).

Объяснение вопросов порта при включении нескольких процессов: если несколько процессов Node прослушивают один и тот же порт, он появитсяError:listen EADDRIUNSошибка, и почему модуль кластера может позволить нескольким дочерним процессам слушать один и тот же порт?Причина в том, что внутри главного процесса запущен TCP-сервер, и только этот сервер реально слушает порт.При запросе с фронта end инициирует событие подключения сервера. Мастер отправит соответствующий дескриптор сокета дочернему процессу.

Сводка модуля child_process и модуля кластера

Будь то модуль child_process или модуль кластера, чтобы решить проблему, связанную с тем, что экземпляр Node.js работает в одном потоке и не может использовать преимущества многоядерного процессора. ядроРодительский процесс (то есть мастер-процесс) отвечает за прослушивание порта и после получения нового запроса распределяет его по следующему рабочему процессу..

Один недостаток кластерного модуля:

Неявный способ построения TCP-сервера внутри кластера действительно намного проще и прозрачнее для пользователя, но этот способ не может быть таким гибким, как использование child_process, потому что основной процесс может управлять только набором одного и того же рабочего процесса, и передавать child_process для создания рабочего процесса, основной процесс может управлять несколькими группами процессов. Причина в том, что когда child_process управляет дочерним процессом, может быть неявно создано несколько серверов TCP.Сравнение двух приведенных выше изображений должно понять, что я сказал.

Принцип взаимодействия процессов Node.js

Будь то модуль child_process или модуль кластера, описанный ранее, требуется связь между основным процессом и рабочим процессом. После создания дочернего процесса с помощью fork() или других API-интерфейсов, чтобы реализовать связь между родительским и дочерним процессами, родительский и дочерний процессы могут передавать информацию через сообщения и send().

Я думаю, что всем знакомо слово IPC, независимо от того, на каком языке разработки оно упоминает коммуникацию процессов, оно будет упомянуто. Полное название IPC — Inter-Process Communication, то есть межпроцессное взаимодействие. Его цель — предоставить различным процессам доступ к ресурсам и координировать работу друг с другом. Существует множество технологий для реализации межпроцессного взаимодействия, таких как именованные каналы, анонимные каналы, сокеты, семафоры, разделяемая память, очереди сообщений и т. д. Реализация каналов IPC в Node зависит от libuv. В Windows это реализовано с помощью именованного канала (name pipe), а система *nix реализована с помощью Unix Domain Socket. Межпроцессное взаимодействие на прикладном уровне имеет только простые события сообщений и методы send(), а интерфейс очень лаконичен и основан на сообщениях.

Схематическая диаграмма создания и внедрения IPC

Как создается коммуникационный конвейер IPC

Прежде чем родительский процесс фактически создаст дочерний процесс, он создастIPC通道и слушать его перед真正的создать из子进程, этот процесс также сообщит дочернему процессу дескриптор файла этого канала IPC через переменную среды (NODE_CHANNEL_FD). В процессе запуска дочернего процесса существующий канал IPC подключается в соответствии с дескриптором файла, тем самым завершая соединение между родительским и дочерним процессами.

Node.js обрабатывает передачу

Прежде чем говорить об дескрипторах, подумайте над вопросом: когда отправляется дескриптор отправки, действительно ли он отправляет серверный объект дочернему процессу?

Тип дескриптора, который может отправить метод send() объекта дочернего процесса.
  • TCP-сокет net.Socket
  • TCP-сервер Net.Server, любая служба прикладного уровня, построенная на основе службы TCP, может пользоваться преимуществами, которые она дает.
  • Сокеты TCP или каналы IPC на уровне net.Native C++
  • dgram.Socket UDP-сокет
  • Сокеты UDP на уровне dgram.Native C++
Отправить ручку Отправить принцип анализа

Легче понять принципиальную схему отправки и восстановления комбинированных дескрипторов.

send()Метод фактически собирает сообщение в два объекта перед отправкой сообщения в конвейер IPC, один параметр — хадлер, а другой — сообщение. Параметр сообщения выглядит следующим образом:

{
    cmd:'NODE_HANDLE',
    type:'net.Server',
    msg:message
}

То, что отправляется в канал IPC, на самом деле является дескриптором файла дескриптора, который мы хотим отправить. Когда этот объект сообщения записывается в канал IPC, он такжеJSON.stringfy()Сериализировать. Таким образом, информация, окончательно отправленная в канал IPC, представляет собой строку, и тот факт, что метод send() может отправлять сообщения и дескрипторы, не означает, что он может отправлять любые объекты.

Дочерний поток, подключенный к каналу IPC, может прочитать сообщение, отправленное родительским процессом, и после синтаксического анализа и восстановления строки в объект через JSON.parse() событие сообщения инициируется для передачи сообщения на прикладной уровень для использования. . В этом процессе объект сообщения также фильтруется. Если значение message.cmd имеет префикс NODE_, он будет реагировать на внутреннее событие internalMessage. Если значение message.cmd равно NODE_HANDLE, оно будет извлеченоmessage.typeЗначение и результирующий файловый дескриптор вместе восстанавливают соответствующий объект.

Взяв в качестве примера дескриптор отправленного TCP-сервера, код процесса восстановления после того, как дочерний процесс получит сообщение, выглядит следующим образом:

function(message,handle,emit){
    var self = this;
    
    var server = new net.Server();
    server.listen(handler,function(){
      emit(server);
    });
}

Этот код восстановления,子进程根据message.type创建对应的TCP服务器对象,然后监听到文件描述符上. Поскольку базовые детали не воспринимаются прикладным уровнем, в дочернем процессе у разработчика будет иллюзия, что серверный объект напрямую передается из родительского процесса.

Между Node-процессами передаются только сообщения, а реальные объекты не передаются.Эта иллюзия является результатом абстрактной инкапсуляции. В настоящее время Node поддерживает только те типы дескрипторов, о которых я упоминал ранее, и ни один тип дескриптора не может быть передан между процессами, если он не имеет полного процесса отправки и восстановления.

Модель многопроцессорной архитектуры Node.js

Мы сами реализуем мультипроцессорную архитектуру Guard Demo

написать основной процесс

master.js в основном обрабатывает следующую логику:

  • Создайте сервер и слушайте порт 3000.
  • Запуск нескольких дочерних процессов на основе системного процессора
  • Отправить сообщение дочернему процессу для связи через метод отправки объекта дочернего процесса
  • В основном процессе отслеживаются изменения дочернего процесса, и если это сигнал самоубийства, перезапускается рабочий процесс.
  • Когда основной процесс слушает сообщение о выходе, он сначала выходит из дочернего процесса, а затем из основного процесса.
// master.js
const fork = require('child_process').fork;
const cpus = require('os').cpus();

const server = require('net').createServer();
server.listen(3000);
process.title = 'node-master'

const workers = {};
const createWorker = () => {
    const worker = fork('worker.js')
    worker.on('message', function (message) {
        if (message.act === 'suicide') {
            createWorker();
        }
    })
    worker.on('exit', function(code, signal) {
        console.log('worker process exited, code: %s signal: %s', code, signal);
        delete workers[worker.pid];
    });
    worker.send('server', server);
    workers[worker.pid] = worker;
    console.log('worker process created, pid: %s ppid: %s', worker.pid, process.pid);
}

for (let i=0; i<cpus.length; i++) {
    createWorker();
}

process.once('SIGINT', close.bind(this, 'SIGINT')); // kill(2) Ctrl-C
process.once('SIGQUIT', close.bind(this, 'SIGQUIT')); // kill(3) Ctrl-\
process.once('SIGTERM', close.bind(this, 'SIGTERM')); // kill(15) default
process.once('exit', close.bind(this));

function close (code) {
    console.log('进程退出!', code);

    if (code !== 0) {
        for (let pid in workers) {
            console.log('master process exited, kill worker pid: ', pid);
            workers[pid].kill('SIGINT');
        }
    }

    process.exit(0);
}

Процесс работы

Логика обработки дочернего процесса worker.js следующая:

  • Создайте объект сервера, обратите внимание, что порт 3000 не прослушивается в начале
  • Получать сообщения, отправленные методом отправки основного процесса, через событие сообщения.
  • Прислушивайтесь к событиям uncaughtException, перехватывайте необработанные исключения, отправляйте сообщения о самоубийстве, чтобы перестроить процесс основным процессом, а дочерний процесс завершается после закрытия ссылки.
// worker.js
const http = require('http');
const server = http.createServer((req, res) => {
	res.writeHead(200, {
		'Content-Type': 'text/plan'
	});
	res.end('I am worker, pid: ' + process.pid + ', ppid: ' + process.ppid);
	throw new Error('worker process exception!'); // 测试异常进程退出、重启
});

let worker;
process.title = 'node-worker'
process.on('message', function (message, sendHandle) {
	if (message === 'server') {
		worker = sendHandle;
		worker.on('connection', function(socket) {
			server.emit('connection', socket);
		});
	}
});

process.on('uncaughtException', function (err) {
	console.log(err);
	process.send({act: 'suicide'});
	worker.close(function () {
		process.exit(1);
	})
})

Демон процесса Node.js

Что такое демон процесса?

Каждый раз, когда вы запускаете программу Node.js, вам нужно вводить команду в командном окнеnode app.jsдля запуска, но если командное окно будет закрыто, служба программы Node.js будет немедленно прервана. Кроме того, когда наша служба Node.js неожиданно выходит из строя, процесс не может быть перезапущен автоматически. Это не то, что мы хотим видеть, поэтому нам нужно каким-то образом защитить запущенный процесс.После выполнения node app.js для запуска сервисного процесса я могу делать другие вещи на этом терминале, не влияя друг на друга. , когда возникает проблема, он может автоматически перезапуститься.

Как реализовать процесс-демон

Здесь я говорю только о некоторых сторонних фреймворках процессных демонов, pm2 и навсегда, все они могут реализовать процессный демон, а нижний уровень также реализуется через модуль child_process и модуль кластера, упомянутые выше, и их принципы здесь упоминаться не будут.

pm2 указывает производственную среду для запуска службы узла с именем test

pm2 start app.js --env production --name test

pm2 часто используемый API

  • pm2 stop Name/processIDОстановить службу по имени службы или идентификатору процесса службы

  • pm2 delete Name/processIDУдалить службу по имени службы или идентификатору процесса службы

  • pm2 logs [Name]Просмотр журнала, если вы добавляете имя службы, укажите для просмотра журнала какой-либо службы, если нет, просмотрите все журналы.

  • pm2 start app.js -i 4Cluster, параметр -i используется, чтобы указать PM2 запустить ваше приложение в режиме cluster_mode (соответственно называемом fork_mode), а следующее число указывает количество запускаемых рабочих потоков. Если указанное число равно 0, PM2 создаст соответствующие рабочие потоки в зависимости от количества ядер вашего процессора. Обратите внимание, что режим cluster_mode обычно используется в производственной среде, а режим fork обычно используется в тестовой или локальной среде, что удобно для тестирования ошибок.

  • pm2 reload Name pm2 restart NameКогда код приложения обновлен, вы можете использовать перезагрузку для загрузки нового кода или перезагрузку для завершения.Перезагрузка может загрузить новый код за 0 секунд простоя, перезагрузка — это перезагрузка, а перезагрузка часто используется в производственной среде для завершения обновления кода!

  • pm2 show NameПосмотреть сведения об услуге

  • pm2 listПосмотреть все проекты в pm2

  • pm2 monitИспользуйте monit, чтобы открыть монитор в реальном времени для просмотра использования ресурсов.

Адрес официального сайта pm2:

Скрин 2.ключевые показатели.IO/docs/usage/…

навсегда никаких особых указаний, официальный адрес сайта

GitHub.com/навсегда JS/…

Примечание: они рекомендуют pm2, и вы можете понять, почему я рекомендую использовать pm2, взглянув на сравнение между ними.woohoo.brief.com/afraid/real Estate 12 из 82 нет…

Linux закрыть процесс

  • Найти PID, связанные с процессами

    ps aux | grep server
    проиллюстрировать:

    root     20158  0.0  5.0 1251592 95396 ?       Sl   5月17   1:19 node /srv/mini-program-api/launch_pm2.js
上面是执行命令后在linux中显示的结果,第二个参数就是进程对应的PID
  • убить процесс
  1. Завершите процесс изящно

    kill -l PID

    Параметр -l указывает команде kill завершить процесс, как если бы пользователь, запустивший процесс, вышел из системы. При использовании этой опции команда kill также пытается убить все оставшиеся дочерние процессы. Но эта команда не всегда работает — все же может потребоваться сначала вручную завершить дочерний процесс, а затем — родительский.

  2. команда kill используется для уничтожения процесса

例如: `kill -9 [PID]`

-9 означает немедленную остановку процесса

这个强大和危险的命令迫使进程在运行时突然终止,进程在结束后不能自我清理。
危害是导致系统资源无法正常释放,一般不推荐使用,除非其他办法都无效。
当使用此命令时,一定要通过ps -ef确认没有剩下任何僵尸进程。
只能通过终止父进程来消除僵尸进程。如果僵尸进程被init收养,问题就比较严重了。
杀死init进程意味着关闭系统。
如果系统中有僵尸进程,并且其父进程是init,
而且僵尸进程占用了大量的系统资源,那么就需要在某个时候重启机器以清除进程表了。
  1. команда killall

    Убейте все процессы в том же группе процесса. Это позволяет указать имя процесса, чтобы быть прекращенным вместо PID.

    killall httpd

Потоки Node.js

Node.js: мифы об однопоточности

const http = require('http');

const server = http.createServer();
server.listen(3000,()=>{
    process.title='程序员成长指北测试进程';
    console.log('进程id',process.pid)
})

Все еще глядя на первый абзац кода в этой статье, служба http создается и запускается процесс.Говорят, что Node.js — это один поток, поэтому количество потоков должно быть 1 после запуска Node, но почему запущено 7 потоков? Разве Javascript не один поток? Я не знаю, есть ли у моих друзей этот вопрос?

Объяснить, почему:

Ядром Node является движок v8. После запуска Node будет создан экземпляр v8. Этот экземпляр является многопоточным.

  • Основной поток: компиляция и выполнение кода.
  • Компиляция/оптимизация потока: код можно оптимизировать во время выполнения основного потока.
  • Поток профилировщика: записывает время выполнения профилируемого кода и предоставляет Crankshaft основу для оптимизации выполнения кода.
  • Несколько потоков сборки мусора.

Поэтому мы часто говорим, что Node является однопоточным, что означает, что выполнение JavaScript является однопоточным (код, написанный разработчиком, выполняется в однопоточной среде), но хост-среда Javascript, будь то Node или браузер является многопоточным. Поскольку в libuv есть концепция пула потоков, libuv будет имитировать асинхронные вызовы различных операционных систем посредством реализации подобных пулов потоков, невидимых для разработчиков.

Некоторые асинхронные операции ввода-вывода занимают дополнительные потоки.

В приведенном выше примере мы читаем файл во время работы таймера:

const fs = require('fs')
setInterval(() => {
    console.log(new Date().getTime())
}, 3000)

fs.readFile('./index.html', () => {})

Количество потоков стало 11, это связано с тем, что в Node есть некоторые операции ввода-вывода (DNS, FS) и некоторые вычисления с интенсивным использованием ЦП (Zlib, Crypto), которые включают пул потоков Node, а размер пула потоков по умолчанию равен 4, потому что пул потоков Число становится 11. Мы можем вручную изменить размер пула потоков по умолчанию:

process.env.UV_THREADPOOL_SIZE = 64

Одна строка кода легко превращает потоки в 71.

Libuv

Libuv — это кроссплатформенная библиотека асинхронного ввода-вывода, которая сочетает в себе функции libev для UNIX и IOCP для Windows. Впервые она была разработана автором Node и специально обеспечивает поддержку асинхронного ввода-вывода на нескольких платформах для Node. Сама Libuv реализована на языке C++, а не-Sussex IO в Node и базовый механизм цикла обработки событий реализованы в libuv.

схема архитектуры libuv

В среде Windows libuv напрямую использует Windows IOCP для реализации асинхронного ввода-вывода. В средах, отличных от Windows, libuv использует многопоточность для имитации асинхронного ввода-вывода.

Обратите внимание на то, что я хочу сказать ниже, асинхронные вызовы Node поддерживаются libuv.В приведенном выше примере чтения файла фактический системный вызов чтения файла завершается libuv.Node отвечает только за вызов интерфейса libuv и ожидание для данных.После возврата выполните соответствующий метод обратного вызова.

Создание потока Node.js

До выпуска Node 10.5.0 официальный представитель предоставил экспериментальный модуль worker_threads, чтобы предоставить Node настоящие возможности многопоточности.

Посмотрите на простую демонстрацию:

const {
  isMainThread,
  parentPort,
  workerData,
  threadId,
  MessageChannel,
  MessagePort,
  Worker
} = require('worker_threads');

function mainThread() {
  for (let i = 0; i < 5; i++) {
    const worker = new Worker(__filename, { workerData: i });
    worker.on('exit', code => { console.log(`main: worker stopped with exit code ${code}`); });
    worker.on('message', msg => {
      console.log(`main: receive ${msg}`);
      worker.postMessage(msg + 1);
    });
  }
}

function workerThread() {
  console.log(`worker: workerDate ${workerData}`);
  parentPort.on('message', msg => {
    console.log(`worker: receive ${msg}`);
  }),
  parentPort.postMessage(workerData);
}

if (isMainThread) {
  mainThread();
} else {
  workerThread();
}

Код открывает пять дочерних потоков в главной ните и основной поток отправляет простое сообщение Cheng Xiangzai.

Поскольку worker_thread все еще находится в экспериментальной стадии, вам нужно добавить флаг --experimental-worker при запуске, наблюдать за монитором активности после запуска и открыть 5 подпотоков.

модуль worker_thread

основной код worker_thread (адрес https://github.com/nodejs/node/blob/master/lib/worker_threads.js) В модуле worker_thread есть 4 объекта и 2 класса, вы можете сами увидеть приведенный выше исходный код.

  • isMainThread: Является ли это основным потоком, судят по threadId === 0 в исходном коде.
  • MessagePort: используется для связи между потоками, унаследованными от EventEmitter.
  • MessageChannel: экземпляр канала для создания асинхронной двусторонней связи.
  • ThreadId: идентификатор нитки.
  • Рабочий: используется для создания дочерних потоков в основном потоке. Первый параметр — это имя файла, которое представляет запись выполнения дочернего потока.
  • parentPort: объект типа MessagePort, представляющий родительский процесс в рабочем потоке, null в основном потоке.
  • workerData: используется для передачи данных (копии данных) в основном процессе дочернему процессу.

Суммировать

Многопроцессорность против многопоточности

Сравните многопоточность и многопроцессорность:

Атрибуты мультипрогресс Многопоточность Сравнивать
данные Обмен данными сложен и требует IPC; данные разделены, а синхронизация проста Поскольку данные процесса являются общими, обмен данными прост, а синхронизация сложна. У каждого свои достоинства
процессор, память Занимает много памяти, переключение затруднено, а загрузка процессора низкая. Низкое использование памяти, простое переключение и высокая загрузка ЦП Многопоточность лучше
уничтожить, переключиться Создание и уничтожение, переключение сложно, а скорость низкая Создание и уничтожение, переключение просто и быстро Многопоточность лучше
coding Простое кодирование и легкая отладка Кодирование и отладка сложны Кодирование и отладка сложны
надежность Процессы выполняются независимо и не влияют друг на друга Нить разделяет та же участь, что и дыхание многопроцессорность лучше
распределенный Может использоваться для многомашинного многоядерного распределенного, легко расширяемого Может использоваться только для многоядерных распределенных многопроцессорность лучше

Справочная статья:

Присоединяйтесь к нам, чтобы учиться вместе!

группа обмена обучением узлов