Pull to refresh
235
0
Timur Shemsedinov @MarcusAurelius

Chief Technology Architect at Metarhia

Send message
Только не на принципе middleware, с тщательно отобранными модулями и с очень хорошо вычищенным прикладным кодом. Кроме того, мы оборачиваем прикладной код в песочницы (sandboxes) см. vm.createContext и при утечке можно уничтожать песочницу, а не процесс целиком, и вся выделенная в ней память уйдет. Новую песочницу можно создавать до удаления старой и после синхронизации стейта просто подменять их.
Без перезапусков, мы сделали для этого все возможное и это себя оправдало. Несколько серверов, на каждом много процессов и все соединены через шину событий и синхронизируют состояние между процессами. Раньше делали это через ZMQ, а теперь по своему протоколу. И да, у нас свой сервер приложений Impress: habrahabr.ru/post/247543 На других фреймворках есть утечки, и они такие, что перезапуск процессов считается нормальной практикой. У нас если процессы и перезапускаются, то не все вместе, и вновь поднятые забирают состояние из соседних. Процессы падают только от ошибок в прикладном поде, которые после некоторого времени тестовой эксплуатации ликвидируются. Есть специальные процессы (менеджеры сервера приложений) которые не обрабатывают бизнес-логики и запросов от клиента вообще, поэтому они не падают и гарантировано держат состояние, кроме того, даже этих специальных процессов несколько, на каждом сервере свой.
У нас не все 256 Гб используются на ноду, на 2.5 млн соединений порядка 40 Гб уходит, но там еще много всякой бизнес-логики, структур данных в памяти, не знаю точно, сколько под сокетные буферы из этого.
По умолчанию, если не делать --max_old_space_size, то у ноды же известное ограничение по памяти, а если подымать его упомянутым ключом, то менеджер памяти становится не эффективным, залипает (в зависимости от сложности структур данных и интенсивности их выделения/освобождения) Вы знаете. У нас ~150k rps на группе серверов в целом и до 2.5 млн открытых сокетов на каждом сервере (20/40 core/thread, 256 Гб памяти). И это стало проблемой. А у людей памяти то часто бывает поменьше и для них это совсем критическая проблема — процессы перегружаются каждые 10 минут.
Дескрипторы сокетов и память накапливаются не навсегда, срок освобождения — 2 минуты, но этого достаточно, чтобы скорость утечки была выше скорости освобождения и чтобы ушла вся память (1-2Гб), а процесс залип (бывает) или вывалился (это немного лучше, чем залип). В приведенном тесте делается 15к запросов за 200 секунд, это очень мало, всего 75 rps, но уходит 100 Мб. А при 3к-5к rps (сильно зависит еще от того, как приложение расходует память) уже и достигается критический объем утечки, чтобы все навернулось.
Заранее сложно сказать, осталось еще несколько моментов, которые я хочу протестировать не только нодовскими тестами, а в условиях реальной нагрузки. Например, склеить свои изменения с правкой donnerjack13589 по поводу смены prefinish на finish, потому, что я ее случайно затер, перенеся бездумно свою правку из ноды в io.js.
Можно добавить, а потом ждать. Когда будет патч, то ничего не испортится, патч с заплаткой не конфликтуют.
1. Делать 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, а тут ее парсить и потом Вы ее опять склеите же.
Странно другое, почему они начали сразу с шифрования? Я как-то пропустил, когда они причислили к правам человека рекурсивный обход бинарного дерева, парсинг AST и регулярные выражения.
Где негодование? Нет признаков негодования.

Зачем нужна возможность именования классов по жесткой ссылке?
Замыкания относятся к документированию потому, что это не только способ написания прикладного кода, а еще и способ непрямого наследования. При использовании замыканий наследуются и переопределяются не классы и прототипы, а функции. Например каррирование — это функциональное наследование через замыкания. Например:
function curry(x) {
    return function(y) {
        return x + y;
    }
}

Можем теперь делать: или сразу var res = curry(4)(5); или получать унаследованную функцию var fn = curry(4); и потом var var res = fn(5); С первого взгляда, может быть непонятно, зачем наследовать функции, но таким способом можно реализовывать метапрограммирование (порождающее программирование), см. примеры: habrahabr.ru/post/227753 Там замыкания повсеместно для порождения модификаций используются, и как это скормить в jsdoc? Это же все в рантайме создается.

Теперь про util.inherits(); Это способ классового наследования. Это не имеет ни какого отношения к примесям в прототипы. Примеси в прототипы нужны когда мы хотим, опять же, динамически создать прототип для массового порождения объектов. Например, имеем Client.prototype, и заранее мы не знаем, будет включен или отключен модуль безопасности. Когда он включен, то делается require('security')(Client); И уже сам модуль безопасности примешивает к Client.prototype свои методы, например: Client.prototype.signIn(login, password); и перекрывает своими методами часть базовых, Client же не знал до этого что есть методы модуля безопасности, и он должен так расширить Client, чтобы новые методы объявить и вызывать их из уже имеющихся.
Вообще, меня jsdoc и IDE (все) очень огорчают. JavaScript не мыслим без примесей, замыканий и фабрик, а я вообще не могу жить без динамических примесей в прототипы. Они позволяют мне писать в 10 раз меньше кода. Пока не удалось найти систему комментирования исходников, генерации доков, или даже систему автодополнения кода для IDE, которая бы все это могла адекватно отпарсить и уразуметь. Ее конечно можно написать, но что-то подсказывает мне, что она будет очень. Обратное решение, когда все нужно декларировать явно, потребует много писанины в комментариях. Так что, единственный приличный способ сделать это, по моему мнению, это чтобы IDE подключались через дебагер к процессам и показывали содержимое прямо из памяти.
А что, jsdoc хорошо понимает добавление геттеров через Object.defineProperty?
Не использовать фабрики в js очень большие жертвы. Кстати, можно даже сделать фабрику фабрик, таким образом экранировать класс провайдера от модификации и избавиться от new.
Без Object.defineProperty эта задача прекрасно решалась, просто библиотека вместо хеша конструкторов должна возвращать функцию с одним параметром — именем провайдера. Это можно назвать фабрикой конструкторов. Фабрика делает нужный require и выглядит это гораздо лаконичнее.

Реализация модуля storage:
module.exports = function(providerName) {
  return require('./'+providerName+'-storage.jsx');
};

Вместо этого:
var storage = require('storage');
var fsStorage = new storage.FsStorage();

Загрузка модуля с провайдером выглядит так:
var storageConstructor = require('storage')('fs'),
    fsStorage = new storageConstructor();
Одинаковая квалификация программистов не обязательна, это условие сложно реализовать, проще провести серию измерений и иметь репрезентативную выборку, чтобы разница в квалификации была сглажена. Конечно есть интуиция специалиста, основанная на опыте, и очевидно, что если человек что-то делает, то он верит в то, что делает, а то бы не делал и не писал бы нам эту статью. Я Вам скажу, что за свои 20 лет профессионального опыта программирования я неоднократно наблюдал, как один человек мог сделать лучше, чем сотни профессионалов и серьезные ИТ гиганты. Лишь потому, что этим гигантам вовсе не нужно делать хорошо, им нужно зарабатывать деньги и обеспечивать регулярность продаж, захватывать первенство на рынке, завоевывать лояльность потребителя любыми способами, вплоть до очевидного зомбирования. Для решения этих задач любой продукт должен иметь целый ряд заранее продуманных изъянов и в концепциях и в реализациях. Отсюда нарочная несовместимость, чрезмерная сложность, закрытость кода, чтобы даже научиться это делать было невозможно без вложения больших денег и сертификации.
Можно померить комплексную производительность систем, решающих одинаковые задачи, созданные на базе разных технологий. И еще очень важно померить производительность программистов, использующих эти тулы. Если можно задачу решить быстрее, то преимущество очевидно, если поддерживать систему дешевле, то этот качественный показатель уже можно подсчитать количественно. Могут быть конечно такие случаи, когда преобразование качественных показателей в количественные само по себе очень ресурсоемко и мы не можем сделать такой эксперимент. В этом случае можно использовать метод экспертных оценок. И уже в самом страшном случае мы можем признать, что вопрос выходит за пределы человеческого познания, но до таких вопросов еще добраться нужно.
А зачем что-то домысливать в нечетких терминах лучше/хуже, если можно просто сделать эксперимент и измерить.
Кстати, Степанов вещает много похожего на UNISTACK, например: youtu.be/QmuMHtbO4ug и youtu.be/Dx1MZh6KYCk Но скорее как о далекой мечте, и когда-то в СССР был банк алгоритмов, и на западе в 60х-70х много говорили о сертифицированных, много раз проверенных алгоритмах (по которым собирается статистика и глобальная степень перееиспользования для которых должна быть такой огромной, что в них уже не может быть ошибок). Вообще идея того, что должен быть один язык, одна библиотека, один подход, одна архитектура, один фюрер летает в воздухе с самого основания программирования, как дисциплины. Только в последние 10 лет началась эта отрава в умах, что «нельзя решить все задачи одним языком» или «для каждого случая хорош свой инструмент». Разве не очевидно, что эти принципы не противоречат друг-другу, только тогда есть свои инструменты для каждого случая, когда в основе всего этого разнообразия инструментов лежит один фундамент. Языки и инструменты должны быть построены в нечто вроде дерева, где на каждом более высоком слое происходит разветвление (специализация), а на каждом более низком — обобщение (унификация). В конечном счете, в основе всего лежит математика, как самый абстрактный способ моделирования и физика, как база для создания моделирующих машин. А сейчас в программировании такая ситуация, что все разнообразие порождено не необходимостью специализации, а волюнтаризмом или жадностью. Волюнтаризмом т.е. произволом разработчиков, мол захотелось — я и сделал. И жадностью гигантов ИТ индустрии — которым нужны свои, непохожие ни на что, инструменты и языки только для порождения искусственной несовместимости. Неспецифическая конкуренция, когда специфики нет, а различие придумано искусственно — загадило нам все программирование. Но как, как двигаться к юнистеку? Как двигаться к одним стандартам? Как двигаться к обобщению, если все плывут в другую сторону? У меня только один ответ — отходить в сторону и делать все самому, да, это первое время порождает +1 к хаосу и фрагментации инструментов и языков, но стоит кому-то начать, и сделать что-то безапелляционно хорошо, как вот с Линуксом, с nginx, с V8 и c другим открытым ПО получилось, то за ним потянутся.
Просто в статье написано кэш, а это подразумевает, что его можно потерять и восстановить. Тут скорее модель, основная копия объектов, где они живут как в бездисковой СУБД.
Вот этот подход с несколькими процессами подходит только для кеша в stateless приложениях, когда кеш прост и пассивен, а работать с живой моделью предметной области, то переключение неизбежно порвет ее консистентность.

Information

Rating
Does not participate
Location
Киев, Киевская обл., Украина
Date of birth
Registered
Activity