Поделитесь 10 вопросами интервью, связанными с процессом Nodejs

Node.js

Поделившись следующими 10 вопросами интервью, вы сможете лучше понять процесс и знания, связанные с потоками Node.js.

Об авторе: Мэй Джун, разработчик Nodejs, молодежь после 90-х, которая любит технологии и любит делиться, общедоступный аккаунт «Стек технологий Nodejs», проект с открытым исходным кодом Github.www.nodejs.red

Быстрая навигация

  • Что такое процессы и потоки? разница между? Ссылаться на:Interview1
  • Что такое сиротский процесс? Ссылаться на:Interview2
  • При создании мультипроцесса код имеетapp.listen(port)Почему порт не занят при выполнении форка? Ссылаться на:Interview3
  • Что такое IPC-связь и как установить IPC-связь? В каких сценариях необходимо использовать связь IPC? Ссылаться на:Interview4
  • Является ли Node.js однопоточным или многопоточным? Будут заданы дополнительные вопросы, почему это один поток? Ссылаться на:Interview5
  • О демонах, что, зачем и как их писать? Ссылаться на:Interview6
  • Реализовать простую интерактивную программу командной строки? Ссылаться на:Interview7
  • Как сделать файл js исполняемой командной программой под Linux? Ссылаться на:Interview8
  • Каков текущий рабочий каталог процесса и что он делает? Ссылаться на:Interview9
  • Проблемы с совместным использованием состояния между несколькими процессами или несколькими веб-сервисами? Ссылаться на:Interview10

Об авторе: Мэй Джун, разработчик Nodejs, молодежь после 90-х, которая любит технологии и любит делиться, общедоступный аккаунт «Стек технологий Nodejs», проект с открытым исходным кодом Github.www.nodejs.red

Interview1

Что такое процессы и потоки? разница между?

О потоке и процессе — очень базовая концепция сервера, в статьеРасширенный процесс и поток Node.jsПосле введения концепции процесса и потока дается практическое применение процесса и потока в Node.js.Для тех, кто не очень хорошо это понимает, сначала прочитайте его.

Interview2

Что такое сиротский процесс?

После того, как родительский процесс создает дочерний процесс, родительский процесс завершается, но один или несколько дочерних процессов, соответствующих родительскому процессу, все еще выполняются.Эти дочерние процессы будут приняты системным процессом инициализации, и соответствующий ppid процесса равен 1, что является сиротским процессом. Это иллюстрируется следующим примером кода.

// master.js
const fork = require('child_process').fork;
const server = require('net').createServer();
server.listen(3000);
const worker = fork('worker.js');

worker.send('server', server);
console.log('worker process created, pid: %s ppid: %s', worker.pid, process.pid);
process.exit(0); // 创建子进程之后,主进程退出,此时创建的 worker 进程会成为孤儿进程
// worker.js
const http = require('http');
const server = http.createServer((req, res) => {
	res.end('I am worker, pid: ' + process.pid + ', ppid: ' + process.ppid); // 记录当前工作进程 pid 及父进程 ppid
});

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

Пример исходного кода потерянного процесса

Консоль для тестирования, вывод pid текущего рабочего процесса и ppid родительского процесса

$ node master
worker process created, pid: 32971 ppid: 32970

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

图片描述

Проверьте еще раз, откройте интерфейс вызова консоли, вы увидите, что ppid, соответствующий рабочему процессу 32971, равен 1 (для процесса инициализации), и в настоящее время он стал потерянным процессом.

$ curl http://127.0.0.1:3000
I am worker, pid: 32971, ppid: 1

Interview3

При создании мультипроцесса код имеетapp.listen(port)Почему порт не занят при выполнении форка?

Сначала посмотрите на ситуацию, что порт занят

// master.js
const fork = require('child_process').fork;
const cpus = require('os').cpus();

for (let i=0; i<cpus.length; i++) {
    const worker = fork('worker.js');
    console.log('worker process created, pid: %s ppid: %s', worker.pid, process.pid);
}
//worker.js
const http = require('http');
http.createServer((req, res) => {
	res.end('I am worker, pid: ' + process.pid + ', ppid: ' + process.ppid);
}).listen(3000);

Конфликт занятости порта несколькими процессами Пример исходного кода

В приведенном выше примере кода консоль выполняетnode master.jsТолько один воркер может слушать порт 3000, остальные будут кидатьError: listen EADDRINUSE :::3000ошибка

Как реализовать многопортовый мониторинг в таком многопроцессном режиме? Ответ все же есть.Node.js v0.5.9 поддерживает функцию отправки хэндлов между процессами после передачи хэндла.Как его отправить? Следующим образом:

/**
 * http://nodejs.cn/api/child_process.html#child_process_subprocess_send_message_sendhandle_options_callback
 * message
 * sendHandle
 */
subprocess.send(message, sendHandle)

После установления IPC-канала между родительским и дочерним процессами сообщение отправляется через метод send объекта дочернего процесса.Вторым параметром sendHandle является дескриптор, который может быть TCP-сокетом, TCP-сервером, UDP-сокетом и т. д. Чтобы решить вышеупомянутую проблему занятости порта с несколькими процессами, мы передаем сокет основного процесса дочернему процессу и модифицируем код следующим образом:

//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'

for (let i=0; i<cpus.length; i++) {
    const worker = fork('worker.js');
    worker.send('server', server);
    console.log('worker process created, pid: %s ppid: %s', worker.pid, process.pid);
}
// worker.js
const http = require('http');
http.createServer((req, res) => {
	res.end('I am worker, pid: ' + process.pid + ', ppid: ' + process.ppid);
})

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);
		});
	}
});

Обработка передачи для решения проблемы конфликта занятости портов несколькими процессами Пример исходного кода

Проверьте это, консоль выполняетnode master.jsСледующие результаты соответствуют нашим ожиданиям, и проблема занятости порта несколькими процессами была решена.

$ node master.js
worker process created, pid: 34512 ppid: 34511
worker process created, pid: 34513 ppid: 34511
worker process created, pid: 34514 ppid: 34511
worker process created, pid: 34515 ppid: 34511

Что касается проблемы занятости нескольких процессов порта, есть статья на CNODE, которая также может быть прочитана.Реализация основной функции кластерного модуля в Node.js посредством анализа исходного кода

Interview4

Что такое IPC-связь и как установить IPC-связь? В каких сценариях необходимо использовать связь IPC?

IPC (Inter-process Communication), то есть технология межпроцессного взаимодействия.Поскольку каждый процесс после создания имеет свое независимое адресное пространство, целью реализации IPC является разделение доступа к ресурсам между процессами.Существует множество способов реализации IPC : конвейеры. , очередь сообщений, семафор, доменный сокет, Node.js реализованы через пайп.

Взгляните на демо, без IPC

// pipe.js
const spawn = require('child_process').spawn;
const child = spawn('node', ['worker.js'])
console.log(process.pid, child.pid); // 主进程id3243 子进程3244
// worker.js
console.log('I am worker, PID: ', process.pid);

консольное выполнениеnode pipe.js, выведите идентификатор основного процесса, идентификатор дочернего процесса, но дочерний процессworker.jsИнформация не выводится на консоль, поскольку вновь созданный дочерний процесс имеет собственный поток stdio.

$ node pipe.js
41948 41949

Создайте канал IPC для передачи сообщений между родительским процессом и дочерним процессом для получения выходной информации.

Измените pipe.js, чтобы установить конвейерную связь между stdio дочернего процесса и stdio текущего процесса.Вы также можете установить механизм IPC с помощью параметра stdio метода spawn().options.stdio

// pipe.js
const spawn = require('child_process').spawn;
const child = spawn('node', ['worker.js'])
child.stdout.pipe(process.stdout);
console.log(process.pid, child.pid);

Пример исходного кода IPC-связи родительско-дочернего процесса

Проверьте еще раз, консоль выполняетnode pipe.js, также распечатывается информация worker.js

$ 42473 42474
I am worker, PID:  42474

Как родительский процесс взаимодействует с дочерним процессом?

Ссылаясь на книгу Simple Node.js, родительский процесс создаст канал IPC и будет продолжать отслеживать канал перед созданием дочернего процесса, а затем начнет создавать дочерний процесс и передаст файловый дескриптор канала IPC через переменную среды ( NODE_CHANNEL_FD).Для дочернего процесса, когда дочерний процесс запускается, он связывает канал IPC в соответствии с переданным дескриптором файла, тем самым устанавливая механизм связи между родительским и дочерним процессами.

图片描述

Схема взаимодействия родительско-дочернего процесса IPC

Interview5

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

Первый вопрос: Node.js однопоточный или многопоточный? Это основной вопрос. В предыдущих интервью я иногда упоминал, что до сих пор не знаю. Javascript является однопоточным, но Node.js, который является его серверной операционной средой, не является однопоточным.

Второй вопрос, почему Javascript однопоточный? Эта проблема должна начинаться с браузера.В среде браузера для работы DOM представьте, что если несколько потоков работают с одним и тем же DOM, это будет беспорядочно, а это означает, что работа с DOM может выполняться только в один поток, чтобы избежать конфликтов рендеринга DOM. В среде браузера поток рендеринга пользовательского интерфейса и механизм выполнения JS являются взаимоисключающими, и выполнение одной стороны приведет к приостановке другой стороны, что определяется механизмом JS.

Interview6

О демонах, что, зачем и как их писать?

Демон работает в фоновом режиме и не зависит от терминала, что это значит? Студенты, изучающие разработку Node.js, могут быть знакомы с тем, когда мы открываем терминал для выполненияnode app.jsПосле запуска процесса обслуживания терминал будет все время занят, если терминал выключен, то обслуживание будет прервано, то есть режим работы foreground. Если используется метод процесса демона, этот терминал я запускаюnode app.jsПосле запуска процесса обслуживания я могу делать другие вещи на этом терминале, не влияя друг на друга.

Создать шаги

  1. создать дочерний процесс
  2. Создать новую сессию в дочернем процессе (вызвать системную функцию setsid)
  3. Измените рабочий каталог подпроцесса (например: «/» или «/usr/ и т. д.)
  4. родительский процесс завершен

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

Логика обработки файлов index.js Использование Spawn Создайте детский процесс для завершения первого шага в работе выше. Options.DATHEDED НАСТОЯЩЕЕ НАСТОЯЩЕЙ ДЕТСКИЙ ПРОЦЕСС ПРОДОЛЖИТЬ (STATSID STATSID STOTSID STORSID STORSID STORSID), когда выходит из родительского процесса, ссылкаoptions_detachedЭто второй шаг. Options.cwd Указывает текущий рабочий каталог подпроцесса. Если вы не задали наследование по умолчанию для текущего рабочего каталога, это третий шаг. Запустите daemon.unref() для выхода из родительского процесса, см.options.stdio, что является четвертым шагом.

// index.js
const spawn = require('child_process').spawn;

function startDaemon() {
    const daemon = spawn('node', ['daemon.js'], {
        cwd: '/usr',
        detached : true,
        stdio: 'ignore',
    });

    console.log('守护进程开启 父进程 pid: %s, 守护进程 pid: %s', process.pid, daemon.pid);
    daemon.unref();
}

startDaemon()

Логика обработки в файле daemon.js запускает таймер на выполнение каждые 10 секунд, чтобы ресурс не вышел, и записывает лог в текущую рабочую директорию дочернего процесса

// /usr/daemon.js
const fs = require('fs');
const { Console } = require('console');

// custom simple logger
const logger = new Console(fs.createWriteStream('./stdout.log'), fs.createWriteStream('./stderr.log'));

setInterval(function() {
	logger.log('daemon pid: ', process.pid, ', ppid: ', process.ppid);
}, 1000 * 10);

Реализация демона Версия Node.js Исходный адрес

запустить тест

$ node index.js
守护进程开启 父进程 pid: 47608, 守护进程 pid: 47609

Откройте монитор активности для просмотра, в настоящее время есть только один процесс 47609, это процесс, который нам нужно демонизировать.

图片描述

Рекомендация чтения демона

Краткое описание демона

Я хорошо знаком с демонами в реальной работе, такими как PM2, Egg-Cluster и т. д. Вышеприведенное является простой демонстрацией для объяснения демонов.В реальной работе требования к надежности демонов все еще очень высоки, например: Мониторинг исключений процессов, планирование управления рабочими процессами, перезапуск после зависания процесса и т. д. Все это по-прежнему требует от нас постоянного мышления.

Interview7

Возьмем метод spawn дочернего процесса child_process следующим образом:

const spawn = require('child_process').spawn;
const child = spawn('echo', ["简单的命令行交互"]);
child.stdout.pipe(process.stdout); // 将子进程的输出做为当前进程的输入,打印在控制台
$ node execfile
简单的命令行交互

Interview8

Как сделать файл js исполняемой командной программой под Linux?

  1. Создайте новый файл hello.js, необходимо добавить заголовок#!/usr/bin/env node, что указывает на то, что текущий скрипт анализируется с помощью Node.js.
  2. Дайте исполняемый файл разрешений chmod +x chmod +x /${dir}/hello.js, каталог настроен
  3. Создайте файл программной ссылки в каталоге /usr/local/bin.sudo ln -s /${dir}/hello.js /usr/local/bin/hello, имя файла - это имя, которое мы используем в терминале
  4. Выполнение hello в терминале эквивалентно входу в node hello.js
#!/usr/bin/env node

console.log('hello world!');

терминальный тест

$ hello
hello world!

Interview9

Каков текущий рабочий каталог процесса и что он делает?

Текущий рабочий каталог процесса можно получить с помощью команды process.cwd(). По умолчанию это текущий запущенный каталог. Если создается дочерний процесс, он наследует каталог родительского процесса. Его можно сбросить через процесс .chdir(), например, с помощью команды spawn.Созданный дочерний процесс может указать параметр cwd для установки рабочего каталога дочернего процесса.

каков эффект? Например, при чтении файла через fs, если для него задан относительный путь, он будет искаться относительно каталога, в котором запущен текущий процесс, поэтому, если каталог запуска указан неверно, правильный результат не будет полученный. В другом случае сторонний модуль, на который ссылается программа, также ищется по каталогу, в котором запущен текущий процесс.

// 示例
process.chdir('/Users/may/Documents/test/') // 设置当前进程目录

console.log(process.cwd()); // 获取当前进程目录

Interview10

Проблемы с совместным использованием состояния между несколькими процессами или несколькими веб-сервисами?

В многопроцессном режиме каждый процесс независим друг от друга.Например, после входа пользователя сессия сохраняется.Если она сохраняется в сервисном процессе, то если у меня 4 рабочих процесса, то ненужно сохранить копию каждого процесса. , при перезапуске службы данные также будут потеряны. То же самое верно для нескольких веб-сервисов.Также будет казаться, что я создал сеанс на машине А, и мне нужно создать копию после того, как балансировка нагрузки будет распределена на машину Б. Общий подход заключается в обмене данными через Redis или базу данных.

Впервые опубликовано на MOOC.com,Вухуу. ИМО OC.com/article/288…