Протоколы http и WebSocket Часть 9 работа с файловой системой Часть 10 стандартные модули, потоки, базы данных, node env
Скачать 1.79 Mb.
|
Часть 10: стандартные модули, потоки, базы данных, NODE_ENV Модуль Node.js os Модуль os даёт доступ ко многим функциям, которые можно использовать для получения информации об операционной системе и об аппаратном обеспечении компьютера, на котором работает Node.js. Это стандартный модуль, устанавливать его не надо, для работы с ним из кода его достаточно подключить: const os = require('os') Здесь имеются несколько полезных свойств, которые, в частности, могут пригодиться при работе с файлами. Так, свойство os.EOL позволяет узнать используемый в системе разделитель строк (признак конца строки). В Linux и macOS это \n, в Windows — \r\n. Надо отметить, что упоминая тут «Linux и macOS», мы говорим о POSIX-совместимых платформах. Ради краткости изложения менее популярные платформы мы тут не упоминаем. Свойство os.constants.signals даёт сведения о константах, используемых для обработки сигналов процессов наподобие SIGHUP, SIGKILL, и так далее. Здесь можно найти подробности о них. Свойство os.constants.errno содержит константы, используемые для сообщений об ошибках — наподобие EADDRINUSE, EOVERFLOW. Теперь рассмотрим основные методы модуля os. os.arch() Этот метод возвращает строку, идентифицирующую архитектуру системы, например — arm, x64, arm64. os.cpus() Возвращает информацию о процессорах, доступных в системе. Например, эти сведения могут выглядеть так: [ { model: 'Intel(R) Core(TM)2 Duo CPU P8600 @ 2.40GHz', speed: 2400, times: { user: 281685380, nice: 0, sys: 187986530, idle: 685833750, irq: 0 } }, { model: 'Intel(R) Core(TM)2 Duo CPU P8600 @ 2.40GHz', speed: 2400, times: { user: 282348700, nice: 0, sys: 161800480, idle: 703509470, irq: 0 } } ] os.endianness() Возвращает BE или LE в зависимости от того, какой порядок байтов (Big Engian или Little Endian) был использован для компиляции бинарного файла Node.js. os.freemem() Возвращает количество свободной системной памяти в байтах. os.homedir() Возвращает путь к домашней директории текущего пользователя. Например — '/Users/flavio'. os.hostname() Возвращает имя хоста. os.loadavg() Возвращает, в виде массива, данные о средних значениях нагрузки, вычисленные операционной системой. Эта информация имеет смысл только в Linux и macOS. Выглядеть она может так: [ 3.68798828125, 4.00244140625, 11.1181640625 ] os.networkInterfaces() Возвращает сведения о сетевых интерфейсах, доступных в системе. Например: { lo0: [ { address: '127.0.0.1', netmask: '255.0.0.0', family: 'IPv4', mac: 'fe:82:00:00:00:00', internal: true }, { address: '::1', netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff', family: 'IPv6', mac: 'fe:82:00:00:00:00', scopeid: 0, internal: true }, { address: 'fe80::1', netmask: 'ffff:ffff:ffff:ffff::', family: 'IPv6', mac: 'fe:82:00:00:00:00', scopeid: 1, internal: true } ], en1: [ { address: 'fe82::9b:8282:d7e6:496e', netmask: 'ffff:ffff:ffff:ffff::', family: 'IPv6', mac: '06:00:00:02:0e:00', scopeid: 5, internal: false }, { address: '192.168.1.38', netmask: '255.255.255.0', family: 'IPv4', mac: '06:00:00:02:0e:00', internal: false } ], utun0: [ { address: 'fe80::2513:72bc:f405:61d0', netmask: 'ffff:ffff:ffff:ffff::', family: 'IPv6', mac: 'fe:80:00:20:00:00', scopeid: 8, internal: false } ] } os.platform() Возвращает сведения о платформе, для которой был скомпилирован Node.js. Вот некоторые из возможных возвращаемых значений: ● darwin ● freebsd ● linux ● openbsd ● win32 os.release() Возвращает строку, идентифицирующую номер релиза операционной системы. os.tmpdir() Возвращает путь к заданной в системе директории для хранения временных файлов. os.totalmem() Возвращает общее количество системной памяти в байтах. os.type() Возвращает сведения, позволяющие идентифицировать операционную систему. Например: ● Linux — Linux. ● Darwin — macOS. ● Windows_NT — Windows. os.uptime() Возвращает время работы системы в секундах с последней перезагрузки. Модуль Node.js events Модуль events предоставляет нам класс EventEmitter, который предназначен для работы с событиями на платформе Node.js. Мы уже немного говорили об этом модуле в седьмой части этой серии материалов. Вот документация к нему. Здесь рассмотрим API этого модуля. Напомним, что для использования его в коде нужно, как это обычно бывает со стандартными модулями, его подключить. После этого надо создать новый объект EventEmitter. Выглядит это так: const EventEmitter = require('events') const door = new EventEmitter() Объект класса EventEmitter пользуется стандартными механизмами, в частности — следующими событиями: ● newListener — это событие вызывается при добавлении обработчика событий. ● removeListener — вызывается при удалении обработчика. Рассмотрим наиболее полезные методы объектов класса EventEmitter (подобный объект в названиях методов обозначен как emitter). emitter.addListener() Псевдоним для метода emitter.on(). emitter.emit() Генерирует событие. Синхронно вызывает все обработчики события в том порядке, в котором они были зарегистрированы. emitter.eventNames() Возвращает массив, который содержит зарегистрированные события. emitter.getMaxListeners() Возвращает максимальное число обработчиков, которые можно добавить к объекту класса EventEmitter. По умолчанию это 10. При необходимости этот параметр можно увеличить или уменьшить с использованием метода setMaxListeners(). emitter.listenerCount() Возвращает количество обработчиков события, имя которого передаётся данному методу в качестве параметра: door.listenerCount('open') emitter.listeners() Возвращает массив обработчиков события для соответствующего события, имя которого передано этому методу: door.listeners('open') emitter.off() Псевдоним для метода emitter.removeListener(), появившийся в Node 10. emitter.on() Регистрируеn коллбэк, который вызывается при генерировании события. Вот как им пользоваться: door.on('open', () => { console.log('Door was opened') }) emitter.once() Регистрирует коллбэк, который вызывается только один раз — при первом возникновении события, для обработки которого зарегистрирован этот коллбэк. Например: const EventEmitter = require('events') const ee = new EventEmitter() ee.once('my-event', () => { //вызвать этот коллбэк один раз при первом возникновении события }) emitter.prependListener() При регистрации обработчика с использованием методов on() или addListener() этот обработчик добавляется в конец очереди обработчиков и вызывается для обработки соответствующего события последним. При использовании метода prependListener() обработчик добавляется в начало очереди, что приводит к тому, что он будет вызываться для обработки события первым. emitter.prependOnceListener() Этот метод похож на предыдущий. А именно, когда обработчик, предназначенный для однократного вызова, регистрируется с помощью метода once(), он оказывается последним в очереди обработчиков и последним вызывается. Метод prependOnceListener() позволяет добавить такой обработчик в начало очереди. emitter.removeAllListeners() Данный метод удаляет все обработчики для заданного события, зарегистрированные в соответствующем объекте. Пользуются им так: door.removeAllListeners('open') emitter.removeListener() Удаляет заданный обработчик, который нужно передать данному методу. Для того чтобы сохранить обработчик для последующего удаления соответствующий коллбэк можно назначить переменной. Выглядит это так: const doSomething = () => {} door.on('open', doSomething) door.removeListener('open', doSomething) emitter.setMaxListeners() Этот метод позволяет задать максимальное количество обработчиков, которые можно добавить к отдельному событию в экземпляре класса EventEmitter. По умолчанию, как уже было сказано, можно добавить до 10 обработчиков для конкретного события. Это значение можно изменить. Пользуются данным методом так: door.setMaxListeners(50) Модуль Node.js http В восьмой части этой серии материалов мы уже говорили о стандартном модуле Node.js http. Он даёт в распоряжение разработчика механизмы, предназначенные для создания HTTP-серверов. Он является основным модулем, применяемым для решения задач обмена данными по сети в Node.js. Подключить его в коде можно так: const http = require('http') В его состав входят свойства, методы и классы. Поговорим о них. Свойства http.METHODS В этом свойстве перечисляются все поддерживаемые методы HTTP: > require('http').METHODS [ 'ACL', 'BIND', 'CHECKOUT', 'CONNECT', 'COPY', 'DELETE', 'GET', 'HEAD', 'LINK', 'LOCK', 'M-SEARCH', 'MERGE', 'MKACTIVITY', 'MKCALENDAR', 'MKCOL', 'MOVE', 'NOTIFY', 'OPTIONS', 'PATCH', 'POST', 'PROPFIND', 'PROPPATCH', 'PURGE', 'PUT', 'REBIND', 'REPORT', 'SEARCH', 'SUBSCRIBE', 'TRACE', 'UNBIND', 'UNLINK', 'UNLOCK', 'UNSUBSCRIBE' ] http.STATUS_CODES Здесь содержатся коды состояния HTTP и их описания: > require('http').STATUS_CODES { '100': 'Continue', '101': 'Switching Protocols', '102': 'Processing', '200': 'OK', '201': 'Created', '202': 'Accepted', '203': 'Non-Authoritative Information', '204': 'No Content', '205': 'Reset Content', '206': 'Partial Content', '207': 'Multi-Status', '208': 'Already Reported', '226': 'IM Used', '300': 'Multiple Choices', '301': 'Moved Permanently', '302': 'Found', '303': 'See Other', '304': 'Not Modified', '305': 'Use Proxy', '307': 'Temporary Redirect', '308': 'Permanent Redirect', '400': 'Bad Request', '401': 'Unauthorized', '402': 'Payment Required', '403': 'Forbidden', '404': 'Not Found', '405': 'Method Not Allowed', '406': 'Not Acceptable', '407': 'Proxy Authentication Required', '408': 'Request Timeout', '409': 'Conflict', '410': 'Gone', '411': 'Length Required', '412': 'Precondition Failed', '413': 'Payload Too Large', '414': 'URI Too Long', '415': 'Unsupported Media Type', '416': 'Range Not Satisfiable', '417': 'Expectation Failed', '418': 'I\'m a teapot', '421': 'Misdirected Request', '422': 'Unprocessable Entity', '423': 'Locked', '424': 'Failed Dependency', '425': 'Unordered Collection', '426': 'Upgrade Required', '428': 'Precondition Required', '429': 'Too Many Requests', '431': 'Request Header Fields Too Large', '451': 'Unavailable For Legal Reasons', '500': 'Internal Server Error', '501': 'Not Implemented', '502': 'Bad Gateway', '503': 'Service Unavailable', '504': 'Gateway Timeout', '505': 'HTTP Version Not Supported', '506': 'Variant Also Negotiates', '507': 'Insufficient Storage', '508': 'Loop Detected', '509': 'Bandwidth Limit Exceeded', '510': 'Not Extended', '511': 'Network Authentication Required' } http.globalAgent Данное свойство указывает на глобальный экземпляр класса http.Agent. Он используется для управления соединениями. Его можно считать ключевым компонентом HTTP-подсистемы Node.js. Подробнее о классе http.Agent мы поговорим ниже. Методы http.createServer() Возвращает новый экземпляр класса http.Server. Вот как пользоваться этим методом для создания HTTP-сервера: const server = http.createServer((req, res) => { //в этом коллбэке будут обрабатываться запросы }) http.request() Позволяет выполнить HTTP-запрос к серверу, создавая экземпляр класса http.ClientRequest. http.get() Этот метод похож на http.request(), но он автоматически устанавливает метод HTTP в значение GET и автоматически же вызывает команду вида req.end(). Классы Модуль HTTP предоставляет 5 классов — Agent, ClientRequest, Server, ServerResponse и IncomingMessage. Рассмотрим их. http.Agent Глобальный экземпляр класса http.Agent, создаваемый Node.js, используется для управления соединениями. Он применяется в качестве значения по умолчанию всеми HTTP-запросами и обеспечивает постановку запросов в очередь и повторное использование сокетов. Кроме того, он поддерживает пул сокетов, что позволяет обеспечить высокую производительность сетевой подсистемы Node.js. При необходимости можно создать собственный объект http.Agent. http.ClientRequest Объект класса http.ClientRequest, представляющий собой выполняющийся запрос, создаётся при вызове методов http.request() или http.get(). При получении ответа на запрос вызывается событие response, в котором передаётся ответ — экземпляр http.IncomingMessage. Данные, полученные после выполнения запроса, можно обработать двумя способами: ● Можно вызвать метод response.read(). ● В обработчике события response можно настроить прослушиватель для события data, что позволяет работать с потоковыми данными. http.Server Экземпляры этого класса используются для создания серверов с применением команды http.createServer(). После того, как у нас имеется объект сервера, мы можем воспользоваться его методами: ● Метод listen() используется для запуска сервера и организации ожидания и обработки входящих запросов. ● Метод close() останавливает сервер. http.ServerResponse Этот объект создаётся классом http.Server и передаётся в качестве второго параметра событию request при его возникновении. Обычно подобным объектам в коде назначают имя res: const server = http.createServer((req, res) => { //res - это объект http.ServerResponse }) В таких обработчиках, после того, как ответ сервера будет готов к отправке клиенту, вызывают метод end(), завершающий формирование ответа. Этот метод необходимо вызывать после завершения формирования каждого ответа. Вот методы, которые используются для работы с HTTP-заголовками: ● getHeaderNames() — возвращает список имён установленных заголовков. ● getHeaders() — возвращает копию установленных HTTP-заголовков. ● setHeader('headername', value) — устанавливает значение для заданного заголовка. ● getHeader('headername') — возвращает установленный заголовок. ● removeHeader('headername') — удаляет установленный заголовок. ● hasHeader('headername') — возвращает true если в ответе уже есть заголовок, имя которого передано этому методу. ● headersSent() — возвращает true если заголовки уже отправлены клиенту. После обработки заголовков их можно отправить клиенту, вызвав метод response.writeHead(), который, в качестве первого параметра, принимает код состояния. В качестве второго и третьего параметров ему можно передать сообщение, соответствующее коду состояния, и заголовки. Для отправки данных клиенту в теле ответа используют метод write(). Он отправляет буферизованные данные в поток HTTP-ответа. Если до этого заголовки ещё не были установлены командой response.writeHead(), сначала будут отправлены заголовки с кодом состояния и сообщением, которые заданы в запросе. Задавать их значения можно, устанавливая значения для свойств statusCode и statusMessage: response.statusCode = 500 response.statusMessage = 'Internal Server Error' http.IncomingMessage Объект класса http.IncomingMessage создаётся в ходе работы следующих механизмов: ● http.Server — при обработке события request. ● http.ClientRequest — при обработке события response. Его можно использовать для работы с данными ответа. А именно: ● Для того чтобы узнать код состояния ответа и соответствующее сообщение используются свойства statusCode и statusMessage. ● Заголовки ответа можно посмотреть, обратившись к свойству headers или rawHearders (для получения списка необработанных заголовков). ● Метод запроса можно узнать, воспользовавшись свойством method. ● Узнать используемую версию HTTP можно с помощью свойства httpVersion. ● Для получения URL предназначено свойство url. ● Свойство socket позволяет получить объект net.Socket, связанный с соединением. Данные ответа представлены в виде потока так как объект http.IncomingMessage реализует интерфейс Readable Stream. Работа с потоками в Node.js Потоки — это одна из фундаментальных концепций, используемых в Node.js-приложениях. Потоки — это инструменты, которые позволяют выполнять чтение и запись файлов, организовывать сетевое взаимодействие систем, и, в целом — эффективно реализовывать операции обмена данными. Концепция потоков не уникальна для Node.js. Они появились в ОС семейства Unix десятки лет назад. В частности, программы могут взаимодействовать друг с другом, передавая потоки данных с использованием конвейеров (с применением символа конвейера — |). Если представить себе, скажем, чтение файла без использования потоков, то, в ходе выполнения соответствующей команды, содержимое файла будет целиком считано в память, после чего с этим содержимым можно будет работать. Благодаря использованию механизма потоков файлы можно считывать и обрабатывать по частям, что избавляет от необходимости хранить в памяти большие объёмы данных. Модуль Node.js stream представляет собой основу, на которой построены все API, поддерживающие работу с потоками. О сильных сторонах использования потоков Потоки, в сравнении с другими способами обработки данных, отличаются следующими преимуществами: ● Эффективное использование памяти. Работа с потоком не предполагает хранения в памяти больших объёмов данных, загружаемых туда заранее, до того, как появится возможность их обработать. ● Экономия времени. Данные, получаемые из потока, можно начать обрабатывать гораздо быстрее, чем в случае, когда для того, чтобы приступить к их обработке, приходится ждать их полной загрузки. |