Бот был написан. Я заметил, что потребление им памяти росло с каждым запросом к API, уличив в проблеме тяжеловесный Request, я решил попробовать написать свой модуль для HTTP запросов, максимально простой, легковесный и быстрый.
В итоге вышел максимально компактный (сейчас в основном файле модуля меньше 200 строк) и не обделенный функционалом модуль, который я назвал tiny_request.
Простота использования
Для обычного GET запроса достаточно написать всего несколько строк:
var req = require('tiny_request')
req.get('http://google.com', function(body, response, err){
if (!err && response.statusCode == 200) {
console.log(body)
}
})
JSON
Так как в первую очередь модуль будет использоваться для работы с API я решил, что нужен простой механизм работы с json.
Для автоматической десериализации полученного ответа от сервера достаточно передать параметр json: true
var req = require('tiny_request')
req.get({ url: 'http://test.com/json', json: true}, function(body, response, err){
if (!err && response.statusCode == 200) {
console.log(body) //body now is parsed JSON object
}
})
GET запросы
Для запроса с GET параметрами достаточно передать query равный объекту с GET параметрами, также для изменения порта запроса достаточно передать параметр port:
req.get({ url: 'http://test.com', query: { test: 'test' }, port: 8080}, function(body, response, err){
if (!err && response.statusCode == 200) {
console.log(body)
}
})
POST Multipart
Куда же без POST запросов и передачи файлов?
var data = {
image: {
value: fs.createReadStream('photo.png'),
filename: 'photo.png',
contentType: 'image/png'
},
test: 'test'
}
req.post({
url: 'http://test.com',
multipart: data
}, function(body, response, err){
if (!err && response.statusCode == 200) {
console.log(body)
}
})
POST формы
Работа с формами так же очень проста:
var form = {
test: 'test'
}
req.post({ url: 'http://test.com', form: form}, function(body, response, err){
if (!err && response.statusCode == 200) {
console.log(body)
}
})
HTTP заголовки
Для добавления заголовков достаточно передать параметр headers:
var headers = {
'Test-Header': 'test'
}
req.post({ url: 'http://test.com', headers: headers}, function(body, response, err){
if (!err && response.statusCode == 200) {
console.log(body)
}
})
Pipe stream
Работа со стримами тоже проста:
req.get({url: url, pipe: stream})
Все исходники можно найти на GitHub: github.com/Naltox/tiny_request
Комментарии (29)
northicewind
12.08.2015 12:15Так как же теперь с потреблением памяти? Без заключительного абзаца неясно стоила ли овчинка выделки. Желательно с цифрами. Спасибо.
Altox
12.08.2015 12:44+1Потребление памяти уменьшилось ( еще сильнее оно уменьшилось при использовании ручной сборки мусора )
Сейчас провел маленький тест — 300 запросов на habrahabr.ru
Вот числа:
Request — { rss: 71733248, heapTotal: 57203968, heapUsed: 25940592 }
tiny_request — { rss: 46379008, heapTotal: 47928576, heapUsed: 11682480 }
aparamonov
12.08.2015 13:10Посмотрите в сторону functional programming (например rxjs), чтобы избавиться от бойлерплейта 'if (!err && response.statusCode == 200) {'
Я бы предпочел видеть API в таком виде:
req .post(...) .onOk((body, response) -> console.log(body)) .onStatus(404, () -> ...)
AndyGrom
12.08.2015 14:03+3А кто-то предпочёл бы увидеть следующее:
req.post(...).then(...)VasilioRuzanni
12.08.2015 14:11А кто-то и подавно
let response = await req.post(...); if (response.statusCode === 404) { ... } console.log(response.body); ...
:)inook
12.08.2015 14:18co + promises
VasilioRuzanni
12.08.2015 14:41Именно их и использовал долгое время, но они все равно создают лишний шум в коде, так что теперь babel «es7.asyncFunctions» + promises — наше все.
b1rdex
12.08.2015 19:15Зачем писать в 2015 без Promise API? Возьмите node-fetch.
MaxFactor
12.08.2015 19:55-3А вы думаете, что будущее за Promise API? Мое мнение, что нет и даже не за асинхронной архитектурой. Недавно было интересно тестануть nodejs на потребление памяти при постоянной нагрузки, и честно говоря классическая связка php/nginx — выигрывает в разы по производительности и по потреблению памяти. Nodejs использую только как commit-server не более того.
PS: было бы хорошо если бы кто-нибуть написал статью со статистикой потребления памяти при нагрузках с использованием разных сервисов (http,https,ssh и т.д.)b1rdex
12.08.2015 19:59Если php+nginx выигрывает в разы, то вы что-то делаете не так. Априори, пересоздаваемое окружение на каждый запрос вместе с блокировкой ввода-вывода будет медленнее, чем горячая точка входа и неблокирующее I/O. Ну а асинхронная архитектура имеет и плюсы, и минусы. Например, в том же сравнении php+nginx и node.js (если использовать везде 1 процесс) node.js будет быстрее, так как не будет блокировки из-за I/O.
b1rdex
12.08.2015 20:01Про PS вообще не понял что вы имеете ввиду. Потребление памяти не зависит от языка, на котором решается задача. Это зависит только от задачи и от качества написанного кода.
MarcusAurelius
13.08.2015 13:30+11. Делать err третьим параметром в callback, мягко говоря, не принято. Лучше ставить параметры в порядке убывания их важности и частоты использования: callback(err, response, body). Тут даже response важнее body, потому, что к response.statusCode точно обратятся, а к body только условно. Но это уже не так критично, главное err поставьте первым.
2. Тип ответа JSON можно не задавать, а определять по Content-Type: application/json или application/javascript (если поддерживать JSONP). А функции для парса данных разных типов можно примешивать к response. Например, так выглядит компактнее:
req.get('http://test.com/json, function(err, res) { if (!err && res.statusCode === 200) { // можем брать res.asJSON() // можем брать res.asBuffer() или res.asString() } });
3. Иметь параметры для всего именованные параметры хорошо, но альтернативно задавать все в URL намного компактнее:
Вместо: req.get({ url: 'http://test.com', query: { test: 'test' }, port: 8080}, callback);
Делать: req.get('http://test.com:8080?test=test', callback);
Да и часто все это у нас уже есть в виде одной строки URL, а тут ее парсить и потом Вы ее опять склеите же.
Drag13
Перечитайте второй абзац)
Altox
Спасибо, поправил :)
Drag13
Пожалуйста)
П.С. Во втором абзаце было дублирование, автор уже убрал. Минусы можно больше не ставить :D
Круто, еще и в карму кто то плюнул О_о. У кого то среда не задалась)
BuriK666
Об ошибках нужно писать в личку… т.к. после исправления статьи, ваш комментарий становится неактуальным.
Drag13
Спасибо, буду знать.
Drag13
Что бы так активно ставили + в карму за статьи, как ее сливают за один комментарий с желанием помочь…
Спасибо, хорошо мотивирует.
ShpuntiK
Каждую неделю на протяжении лет 4-5 вижу одну и ту же ситуацию: кто-то пишет в комментарии об опечатке в статье, ему ставят минусы, он начинает писать что-то типа «Ой-ой, за что минусы то? Вот беда то...». Новые сообщения только ещё минусов наберут. Просто не пишите в следующий раз в личку и всё.