Протоколы http и WebSocket Часть 9 работа с файловой системой Часть 10 стандартные модули, потоки, базы данных, node env
Скачать 1.79 Mb.
|
Пример работы с потоками Традиционный пример работы с потоками демонстрирует чтение файла с диска. Сначала рассмотрим код, в котором потоки не используются. Стандартный модуль Node.js fs позволяет прочитать файл, после чего его можно передать по протоколу HTTP в ответ на запрос, полученный HTTP-сервером: const http = require('http') const fs = require('fs') const server = http.createServer(function (req, res) { fs.readFile(__dirname + '/data.txt', (err, data) => { res.end(data) }) }) server.listen(3000) Метод readFile(), использованный здесь, позволяет прочесть файл целиком. Когда чтение будет завершено, он вызывает соответствующий коллбэк. Метод res.end(data), вызываемый в коллбэке, отправляет содержимое файла клиенту. Если размер файла велик, то эта операция займёт немало времени. Вот тот же пример переписанный с использованием потоков: const http = require('http') const fs = require('fs') const server = http.createServer((req, res) => { const stream = fs.createReadStream(__dirname + '/data.txt') stream.pipe(res) }) server.listen(3000) Вместо того, чтобы ждать того момента, когда файл будет полностью прочитан, мы начинаем передавать его данные клиенту сразу после того, как первая порция этих данных будет готова к отправке. Метод pipe() В предыдущем примере мы использовали конструкцию вида stream.pipe(res), в которой вызывается метод файлового потока pipe(). Этот метод берёт данные из их источника и отправляет их в место назначения. Его вызывают для потока, представляющего собой источник данных. В данном случае это — файловый поток, который отправляют в HTTP-ответ. Возвращаемым значением метода pipe() является целевой поток. Это очень удобно, так как позволяет объединять в цепочки несколько вызовов метода pipe(): src.pipe(dest1).pipe(dest2) Это равносильно такой конструкции: src.pipe(dest1) dest1.pipe(dest2) API Node.js, в которых используются потоки Потоки — полезный механизм, в результате многие модули ядра Node.js предоставляют стандартные возможности по работе с потоками. Перечислим некоторые из них: ● process.stdin — возвращает поток, подключённый к stdin. ● process.stdout — возвращает поток, подключённый к stdout. ● process.stderr — возвращает поток, подключённый к stderr. ● fs.createReadStream() — создаёт читаемый поток для работы с файлом. ● fs.createWriteStream()— создаёт записываемый поток для работы с файлом. ● net.connect() — инициирует соединение, основанное на потоке. ● http.request() — возвращает экземпляр класса http.ClientRequest, предоставляющий доступ к записываемому потоку. ● zlib.createGzip() — сжимает данные с использованием алгоритма gzip и отправляет их в поток. ● zlib.createGunzip() — выполняет декомпрессию gzip-потока. ● zlib.createDeflate() — сжимает данные с использованием алгоритма deflate и отправляет их в поток. ● zlib.createInflate() — выполняет декомпрессию deflate-потока. Разные типы потоков Существует четыре типа потоков: ● Поток для чтения (Readable) — это поток, из которого можно читать данные. Записывать данные в такой поток нельзя. Когда в такой поток поступают данные, они буферизуются до того момента пока потребитель данных не приступит к их чтению. ● Поток для записи (Writable) — это поток, в который можно отправлять данные. Читать из него данные нельзя. ● Дуплексный поток (Duplex) — в такой поток можно и отправлять данные и читать их из него. По существу это — комбинация потока для чтения и потока для записи. ● Трансформирующий поток (Transform) — такие потоки похожи на дуплексные потоки, разница заключается в том, что то, что поступает на вход этих потоков, преобразует то, что из них можно прочитать. Создание потока для чтения Поток для чтения можно создать и инициализировать, воспользовавшись возможностями модуля stream: const Stream = require('stream') const readableStream = new Stream.Readable() Теперь в поток можно поместить данные, которые позже сможет прочесть потребитель этих данных: readableStream.push('hi!') readableStream.push('ho!') Создание потока для записи Для того чтобы создать записываемый поток нужно расширить базовый объект Writable и реализовать его метод _write(). Для этого сначала создадим соответствующий поток: const Stream = require('stream') const writableStream = new Stream.Writable() Затем реализуем его метод _write(): writableStream._write = (chunk, encoding, next) => { console.log(chunk.toString()) next() } Теперь к такому потоку можно подключить поток, предназначенный для чтения: process.stdin.pipe(writableStream) Получение данных из потока для чтения Для того чтобы получить данные из потока, предназначенного для чтения, воспользуемся потоком для записи: const Stream = require('stream') const readableStream = new Stream.Readable() const writableStream = new Stream.Writable() writableStream._write = (chunk, encoding, next) => { console.log(chunk.toString()) next() } readableStream.pipe(writableStream) readableStream.push('hi!') readableStream.push('ho!') readableStream.push(null) Команда readableStream.push(null) сообщает об окончании вывода данных. Работать с потоками для чтения можно и напрямую, обрабатывая событие readable: readableStream.on('readable', () => { console.log(readableStream.read()) }) Отправка данных в поток для записи Для отправки данных в поток для записи используется метод write(): writableStream.write('hey!\n') Сообщение потоку для записи о том, что запись данных завершена Для того чтобы сообщить потоку для записи о том, что запись данных в него завершена, можно воспользоваться его методом end(): writableStream.end() Этот метод принимает несколько необязательных параметров. В частности, ему можно передать последнюю порцию данных, которые надо записать в поток. Основы работы с MySQL в Node.js MySQL является одной из самых популярных СУБД в мире. В экосистеме Node.js имеется несколько пакетов, которые позволяют взаимодействовать с MySQL-базами, то есть — сохранять в них данные, получать данные из баз и выполнять другие операции. Мы будем использовать пакет mysqljs/mysql . Этот проект, который существует уже очень давно, собрал более 12000 звёзд на GitHub. Для того чтобы воспроизвести следующие примеры, вам понадобится MySQL-сервер. Установка пакета Для установки этого пакета воспользуйтесь такой командой: npm install mysql Инициализация подключения к базе данных Сначала подключим пакет в программе: const mysql = require('mysql') После этого создадим соединение: const options = { user: 'the_mysql_user_name', password: 'the_mysql_user_password', database: 'the_mysql_database_name' } const connection = mysql.createConnection(options) Теперь попытаемся подключиться к базе данных: connection.connect(err => { if (err) { console.error('An error occurred while connecting to the DB') throw err } } Параметры соединения В вышеприведённом примере объект options содержал три параметра соединения: const options = { user: 'the_mysql_user_name', password: 'the_mysql_user_password', database: 'the_mysql_database_name' } На самом деле этих параметров существует гораздо больше. В том числе — следующие: ● host — имя хоста, на котором расположен MySQL-сервер, по умолчанию — localhost. ● port — номер порта сервера, по умолчанию — 3306. ● socketPath — используется для указания сокета Unix вместо хоста и порта. ● debug — позволяет работать в режиме отладки, по умолчанию эта возможность отключена. ● trace — позволяет выводить сведения о трассировке стека при возникновении ошибок, по умолчанию эта возможность включена. ● ssl — используется для настройки SSL-подключения к серверу. Выполнение запроса SELECT Теперь всё готово к выполнению SQL-запросов к базе данных. Для выполнения запросов используется метод соединения query, который принимает запрос и коллбэк. Если операция завершится успешно — коллбэк будет вызван с передачей ему данных, полученных из базы. В случае ошибки в коллбэк попадёт соответствующий объект ошибки. Вот как это выглядит при выполнении запроса на выборку данных: connection.query('SELECT * FROM todos', (error, todos, fields) => { if (error) { console.error('An error occurred while executing the query') throw error } console.log(todos) }) При формировании запроса можно использовать значения, которые будут автоматически встроены в строку запроса: const id = 223 connection.query('SELECT * FROM todos WHERE id = ?', [id], (error, todos, fields) => { if (error) { console.error('An error occurred while executing the query') throw error } console.log(todos) }) Для передачи в запрос нескольких значений можно, в качестве второго параметра, использовать массив: const id = 223 const author = 'Flavio' connection.query('SELECT * FROM todos WHERE id = ? AND author = ?', [id, author], (error, todos, fields) => { if (error) { console.error('An error occurred while executing the query') throw error } console.log(todos) }) Выполнение запроса INSERT Запросы INSERT используются для записи данных в базу. Например, запишем в базу данных объект: const todo = { thing: 'Buy the milk' author: 'Flavio' } connection.query('INSERT INTO todos SET ?', todo, (error, results, fields) => { if (error) { console.error('An error occurred while executing the query') throw error } }) Если у таблицы, в которую добавляются данные, есть первичный ключ со свойством auto_increment, его значение будет возвращено в виде results.insertId: const todo = { thing: 'Buy the milk' author: 'Flavio' } connection.query('INSERT INTO todos SET ?', todo, (error, results, fields) => { if (error) { console.error('An error occurred while executing the query') throw error }} const id = results.resultId console.log(id) ) Закрытие соединения с базой данных После того как работа с базой данных завершена и пришло время закрыть соединение — воспользуйтесь его методом end(): connection.end() Это приведёт к правильному завершению работы с базой данных. О разнице между средой разработки и продакшн-средой Создавая приложения в среде Node.js можно использовать различные конфигурации для окружения разработки и продакшн-окружения. По умолчанию платформа Node.js работает в окружении разработки. Для того чтобы указать ей на то, что код выполняется в продакшн-среде, можно настроить переменную окружения NODE_ENV: NODE_ENV=production Обычно это делается в командной строке. В Linux, например, это выглядит так: export NODE_ENV=production Лучше, однако, поместить подобную команду в конфигурационный файл наподобие .bash_profile (при использовании Bash), так как в противном случае такие настройки не сохраняются после перезагрузки системы. Настроить значение переменной окружения можно, воспользовавшись следующей конструкцией при запуске приложения: NODE_ENV=production node app.js Эта переменная окружения широко используется во внешних библиотеках для Node.js. Установка NODE_ENV в значение production обычно означает следующее: ● До минимума сокращается логирование. ● Используется больше уровней кэширования для оптимизации производительности. Например, Pug — библиотека для работы с шаблонами, используемая Express, готовится к работе в режиме отладки в том случае, если переменная NODE_ENV не установлена в значение production. Представления Express, в режиме разработки, генерируются при обработке каждого запроса. В продакшн-режиме они кэшируются. Есть и множество других подобных примеров. Express предоставляет конфигурационные хуки для каждого окружения. То, какой именно будет вызван, зависит от значения NODE_ENV: app.configure('development', () => { //... }) app.configure('production', () => { //... }) app.configure('production', 'staging', () => { //... }) Например, с их помощью можно использовать различные обработчики событий для разных режимов: app.configure('development', () => { app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); }) app.configure('production', () => { app.use(express.errorHandler()) }) Надеемся, освоив это руководство, вы узнали о платформе Node.js достаточно много для того, чтобы приступить к работе с ней. Полагаем, теперь вы, даже если начали читать первую статью этого цикла, совершенно не разбираясь в Node.js, сможете начать писать что-то своё, с интересном читать чужой код и с толком пользоваться документацией к Node.js. На всякий случай :) |