├── .editorconfig ├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── LICENSE ├── README.md ├── child-process ├── main.js └── worker.js ├── cluster ├── multicore.js ├── primary.js └── worker.js └── tcp ├── client.js └── server.js /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | end_of_line = lf 6 | charset = utf-8 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | 10 | [{*.js,*.mjs,*.ts,*.json,*.yml}] 11 | indent_size = 2 12 | indent_style = space 13 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "browser": true, 4 | "es6": true, 5 | "node": true 6 | }, 7 | "extends": "eslint:recommended", 8 | "parserOptions": { 9 | "ecmaVersion": "latest" 10 | }, 11 | "globals": { 12 | "BigInt": true 13 | }, 14 | "rules": { 15 | "indent": [ 16 | "error", 17 | 2 18 | ], 19 | "linebreak-style": [ 20 | "error", 21 | "unix" 22 | ], 23 | "quotes": [ 24 | "error", 25 | "single" 26 | ], 27 | "semi": [ 28 | "error", 29 | "always" 30 | ], 31 | "no-loop-func": [ 32 | "error" 33 | ], 34 | "block-spacing": [ 35 | "error", 36 | "always" 37 | ], 38 | "camelcase": [ 39 | "error" 40 | ], 41 | "eqeqeq": [ 42 | "error", 43 | "always" 44 | ], 45 | "strict": [ 46 | "error", 47 | "global" 48 | ], 49 | "brace-style": [ 50 | "error", 51 | "1tbs", 52 | { 53 | "allowSingleLine": true 54 | } 55 | ], 56 | "comma-style": [ 57 | "error", 58 | "last" 59 | ], 60 | "comma-spacing": [ 61 | "error", 62 | { 63 | "before": false, 64 | "after": true 65 | } 66 | ], 67 | "eol-last": [ 68 | "error" 69 | ], 70 | "func-call-spacing": [ 71 | "error", 72 | "never" 73 | ], 74 | "key-spacing": [ 75 | "error", 76 | { 77 | "beforeColon": false, 78 | "afterColon": true, 79 | "mode": "minimum" 80 | } 81 | ], 82 | "keyword-spacing": [ 83 | "error", 84 | { 85 | "before": true, 86 | "after": true, 87 | "overrides": { 88 | "function": { 89 | "after": false 90 | } 91 | } 92 | } 93 | ], 94 | "max-len": [ 95 | "error", 96 | { 97 | "code": 80, 98 | "ignoreUrls": true 99 | } 100 | ], 101 | "max-nested-callbacks": [ 102 | "error", 103 | { 104 | "max": 7 105 | } 106 | ], 107 | "new-cap": [ 108 | "error", 109 | { 110 | "newIsCap": true, 111 | "capIsNew": false, 112 | "properties": true 113 | } 114 | ], 115 | "new-parens": [ 116 | "error" 117 | ], 118 | "no-lonely-if": [ 119 | "error" 120 | ], 121 | "no-trailing-spaces": [ 122 | "error" 123 | ], 124 | "no-unneeded-ternary": [ 125 | "error" 126 | ], 127 | "no-whitespace-before-property": [ 128 | "error" 129 | ], 130 | "object-curly-spacing": [ 131 | "error", 132 | "always" 133 | ], 134 | "operator-assignment": [ 135 | "error", 136 | "always" 137 | ], 138 | "operator-linebreak": [ 139 | "error", 140 | "after" 141 | ], 142 | "semi-spacing": [ 143 | "error", 144 | { 145 | "before": false, 146 | "after": true 147 | } 148 | ], 149 | "space-before-blocks": [ 150 | "error", 151 | "always" 152 | ], 153 | "space-before-function-paren": [ 154 | "error", 155 | { 156 | "anonymous": "never", 157 | "named": "never", 158 | "asyncArrow": "always" 159 | } 160 | ], 161 | "space-in-parens": [ 162 | "error", 163 | "never" 164 | ], 165 | "space-infix-ops": [ 166 | "error" 167 | ], 168 | "space-unary-ops": [ 169 | "error", 170 | { 171 | "words": true, 172 | "nonwords": false, 173 | "overrides": { 174 | "typeof": false 175 | } 176 | } 177 | ], 178 | "no-unreachable": [ 179 | "error" 180 | ], 181 | "no-global-assign": [ 182 | "error" 183 | ], 184 | "no-self-compare": [ 185 | "error" 186 | ], 187 | "no-unmodified-loop-condition": [ 188 | "error" 189 | ], 190 | "no-constant-condition": [ 191 | "error", 192 | { 193 | "checkLoops": false 194 | } 195 | ], 196 | "no-console": [ 197 | "off" 198 | ], 199 | "no-useless-concat": [ 200 | "error" 201 | ], 202 | "no-useless-escape": [ 203 | "error" 204 | ], 205 | "no-shadow-restricted-names": [ 206 | "error" 207 | ], 208 | "no-use-before-define": [ 209 | "error", 210 | { 211 | "functions": false 212 | } 213 | ], 214 | "arrow-parens": [ 215 | "error", 216 | "always" 217 | ], 218 | "arrow-body-style": [ 219 | "error", 220 | "as-needed" 221 | ], 222 | "arrow-spacing": [ 223 | "error" 224 | ], 225 | "no-confusing-arrow": [ 226 | "error", 227 | { 228 | "allowParens": true 229 | } 230 | ], 231 | "no-useless-computed-key": [ 232 | "error" 233 | ], 234 | "no-useless-rename": [ 235 | "error" 236 | ], 237 | "no-var": [ 238 | "error" 239 | ], 240 | "object-shorthand": [ 241 | "error", 242 | "always" 243 | ], 244 | "prefer-arrow-callback": [ 245 | "error" 246 | ], 247 | "prefer-const": [ 248 | "error" 249 | ], 250 | "prefer-numeric-literals": [ 251 | "error" 252 | ], 253 | "prefer-rest-params": [ 254 | "error" 255 | ], 256 | "prefer-spread": [ 257 | "error" 258 | ], 259 | "rest-spread-spacing": [ 260 | "error", 261 | "never" 262 | ], 263 | "template-curly-spacing": [ 264 | "error", 265 | "never" 266 | ] 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-2023 How.Programming.Works contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Межпроцессовое взаимодействие 2 | 3 | [Задать вопрос](https://github.com/HowProgrammingWorks/LiveQA/discussions/categories/q-a) 4 | 5 | [![Межпроцессовое взаимодействие в Node.js](https://img.youtube.com/vi/2OXWZFMvfbc/0.jpg)](https://www.youtube.com/watch?v=2OXWZFMvfbc) 6 | 7 | ### Через IPC в node.js 8 | 9 | Заходим в каталог `/workers`, проект разделен на 3 файла (хотя, все можно было 10 | бы написать и в одном, но мы делаем так для нашего удобства): 11 | 12 | * `multicore.js` - это запускаемый модуль, запускаем так `node multicore` 13 | * `master.js` - модуль, который запускается в родительском процессе 14 | * `worker.js` - модуль, который запускается в дочернем процессе 15 | 16 | Запускаем 'node multicore', который подгружает сначала `master.js` и происходит 17 | порождение дочерних процессов по количеству ядер процессора 18 | `os.cpus().length` при помощи `cluster.fork()`, ссылки на воркеры 19 | складываются в массив `workers`. Родительский процесс рассылает дочерним через 20 | `worker.send()` задачу на исполнение. Дочерний процесс ловит задачу (это 21 | массив) и перемножает все его элементы на 2, после чего отправляет результат 22 | обратно в родительский процесс. Когда родительский процесс получает ответы от 23 | всех дочерних, то он выходит, завершая и дочерние. 24 | 25 | ### Через TCP сокеты 26 | 27 | Заходим в каталог `/tcpServer`. Тут лежит пример передачи объекта из адресного 28 | пространства одного процесса, в адресное пространство другого при помощи 29 | протокола TCP/IP и сокетов операционной системы, API которых обернуто в 30 | библиотеку 'net' для Node.js. 31 | 32 | * `server.js` - серверная часть, слушает TCP порт 2000 (ждет подключений); 33 | * `client.js` - клиентская часть, подключается к `127.0.0.1` на порт `2000`. 34 | 35 | Запускать нужно из двух консолей, сначала из первой консоли `node server`, потом 36 | из второй `node client`. После запуска `client.js` подключается к серверу и 37 | отправляет ему привет. При подключении клиента, сервер отправляет ему при помощи 38 | метода `socket.write(data)` объект `user`, сериализованный (преобразованный в 39 | строку) через `JSON.stringify(object)`. При подключении сервер еще выводит в 40 | консоль IP адрес клиента из `socket.localAddress`. Дальше сервер вешает событие 41 | на получение данных из клиентского сокета через `socket.on('data', function)`. 42 | Клиент, точно так же вешает на свой сокет событие получения данных, и когда они 43 | приходят, то передает их для парсинга (синтаксического разбора) в метод 44 | `JSON.parse(string)`, результатом выполнения которого, есть обычный объект 45 | JavaScript, с которым можно работать, как с объявленным в нашем процессе, 46 | например, читать и писать его свойства и обращаться к методам (функциям). 47 | Например: `console.log(user.age)` или `console.log(user['age'])` или 48 | `user.doSometging(argument)`. 49 | 50 | ## Задания 51 | 52 | 1. Сделать ту же распределенную задачу с умножением массива, пример которой 53 | приведен в `/workers`, только на TCP сокетах. 54 | 2. Усовершенствовать задачу так, чтоб массив разбивался на части, которые 55 | отправляются в разные процессы и разные части массива должны обрабатываться в 56 | разных процессах, а результаты сливаться в родительский процесс в любом порядке. 57 | 3. Сделать так, чтобы результаты склеивались в родительском процессе в том же 58 | порядке, в котором задача была разделена на части. 59 | 60 | ## Дополнительные задания 61 | 62 | 1. Реализовать работу системы с произвольным кол-вом вычислительных клиентов 63 | и отслеживать их занятость, т.е. мы имеем массив клиентов, отмечаем в нем, что 64 | некоторые получили задачу и еще не вернули ответ, и можем разделять новые задачи 65 | в любой момент на свободных клиентов. 66 | 2. Реализовать два типа клиентов, первый уже есть, он выполняет вычисления, а 67 | второй - дает задачу серверу. Таким образом, один тип клиента это customer 68 | (заказчик услуги), а второй это worker (исполнитель услуги). Сервер теперь 69 | выполняет функцию brocker (брокера услуги). 70 | 3. Применить для обмена между процессами протокол JSTP: 71 | [HowProgrammingWorks/JSTP](https://github.com/HowProgrammingWorks/JSTP) 72 | 4. Реализовать любую задачу при помощи этого примера распределенных вычислений 73 | из курса "Численные методы" или других разделов вычислительной математики. 74 | Например, решение СЛАУ. 75 | -------------------------------------------------------------------------------- /child-process/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const os = require('node:os'); 4 | const cp = require('node:child_process'); 5 | 6 | console.log('Started primary:', process.pid); 7 | 8 | const cpuCount = os.cpus().length; 9 | const workers = []; 10 | 11 | for (let i = 0; i < cpuCount; i++) { 12 | const worker = cp.fork('./worker.js'); 13 | console.log('Started worker:', worker.pid); 14 | workers.push(worker); 15 | } 16 | 17 | const task = [2, 17, 3, 2, 5, 7, 15, 22, 1, 14, 15, 9, 0, 11]; 18 | const results = []; 19 | 20 | workers.forEach((worker) => { 21 | worker.send({ task }); 22 | 23 | worker.on('exit', (code) => { 24 | console.log('Worker exited:', worker.pid, code); 25 | }); 26 | 27 | worker.on('message', (message) => { 28 | 29 | console.log('Message from worker', worker.pid); 30 | console.log(message); 31 | 32 | results.push(message.result); 33 | 34 | if (results.length === cpuCount) { 35 | process.exit(0); 36 | } 37 | 38 | }); 39 | 40 | setTimeout(() => process.exit(1), 5000); 41 | }); 42 | -------------------------------------------------------------------------------- /child-process/worker.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | console.log('Hello from worker', process.pid); 4 | 5 | const caltulations = (item) => item * 2; 6 | 7 | process.on('message', (message) => { 8 | console.log('Message to worker', process.pid); 9 | console.log('from primary:', message); 10 | 11 | const result = message.task.map(caltulations); 12 | process.send({ result }); 13 | }); 14 | -------------------------------------------------------------------------------- /cluster/multicore.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const cluster = require('node:cluster'); 4 | 5 | if (cluster.isPrimary) { 6 | require('./primary.js'); 7 | } else { 8 | require('./worker.js'); 9 | } 10 | -------------------------------------------------------------------------------- /cluster/primary.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const os = require('node:os'); 4 | const cluster = require('node:cluster'); 5 | 6 | console.log('Started primary:', process.pid); 7 | 8 | const cpuCount = os.cpus().length; 9 | const workers = []; 10 | 11 | for (let i = 0; i < cpuCount; i++) { 12 | const worker = cluster.fork(); 13 | console.log('Started worker:', worker.process.pid); 14 | workers.push(worker); 15 | } 16 | 17 | const task = [2, 17, 3, 2, 5, 7, 15, 22, 1, 14, 15, 9, 0, 11]; 18 | const results = []; 19 | 20 | workers.forEach((worker) => { 21 | worker.send({ task }); 22 | 23 | worker.on('exit', (code) => { 24 | console.log('Worker exited:', worker.process.pid, code); 25 | }); 26 | 27 | worker.on('message', (message) => { 28 | 29 | console.log('Message from worker', worker.process.pid); 30 | console.log(message); 31 | 32 | results.push(message.result); 33 | 34 | if (results.length === cpuCount) { 35 | process.exit(0); 36 | } 37 | 38 | }); 39 | 40 | setTimeout(() => process.exit(1), 5000); 41 | }); 42 | -------------------------------------------------------------------------------- /cluster/worker.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const cluster = require('node:cluster'); 4 | 5 | console.log('Hello from worker', process.pid, cluster.worker.id); 6 | 7 | const caltulations = (x) => x * 2; 8 | 9 | process.on('message', (message) => { 10 | console.log('Message to worker ', process.pid); 11 | console.log(message); 12 | 13 | const result = message.task.map(caltulations); 14 | process.send({ result }); 15 | }); 16 | -------------------------------------------------------------------------------- /tcp/client.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const net = require('node:net'); 4 | 5 | const socket = new net.Socket(); 6 | 7 | socket.connect({ 8 | port: 2000, 9 | host: '127.0.0.1', 10 | }, () => { 11 | socket.write('Hello from client'); 12 | socket.on('data', (data) => { 13 | const message = data.toString(); 14 | const user = JSON.parse(message); 15 | console.log('Data received (by client):', data); 16 | console.log('toString:', message); 17 | console.log(`Age of ${user.name} is ${user.age}`); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tcp/server.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const net = require('node:net'); 4 | 5 | const user = { name: 'Marcus Aurelius', age: 1895 }; 6 | 7 | const server = net.createServer((socket) => { 8 | console.log('Connected:', socket.localAddress); 9 | socket.write(JSON.stringify(user)); 10 | socket.on('data', (data) => { 11 | const message = data.toString(); 12 | console.log('Data received (by server):', data); 13 | console.log('toString:', message); 14 | }); 15 | }); 16 | 17 | server.listen(2000); 18 | --------------------------------------------------------------------------------