Жабаскрипт (веде Віктор Турський)
4.63K subscribers
14 photos
242 links
Авторський контент для JavaScript розробників, але не завжди про JS:). Пишу про архітектуру, best practices, продуктивність, безпеку, інструментарій.

Viktor Turskyi (@koorchik), Cofounder at Webbylab, SWE at Google

Рекламу не розміщую!
Download Telegram
Channel name was changed to «Жабаскрипт»
В JavaScript не так давно добавили тип данных Symbol. Обычно говорят, что Symbol дает возможность хранить метаданные в объекте или возможность реализации условно приватных свойств объекта. Но есть и другая сторона, Symbol решает ряд проблем и безопасностью приложения.

Отличный пост от Дэна Абрамова на эту тему. Основная идея в том, что React узлы внутри представлены в виде JSON объектов. И код, который вы считаете безопасным <p>{message.text}</p>,
на самом деле, содержит XSS уязвимость. Если message.text содержит пользовательский ввод и мы рендерим React на сервере, то злоумышленик может вставить в message.text JSON объект, который описует любую React ноду. Для того, чтобы от этого защититься, в JSON объекте должно быть свойство $$typeof и Symbol в качестве значения. В таком случае, злоумышленик не сможет заранее предсказать Symbol.

Статья - https://overreacted.io/why-do-react-elements-have-typeof-property/

Еще отличный пример - это Sequalize. Для описания запроса раньше использовались строковые операторы

Post.findAll({
where: { 'or': [{authorId: 12}, {authorId: 13}] }
});

сейчас это:

Post.findAll({
where: { [Op.or]: [{authorId: 12}, {authorId: 13}] }
});

где Op.or это Symbol, что не позволяет злоумышленику так просто сделать ORM Injection.
Есть IoT платформа написанная на JS. Необходимо предоставить сторонним условно доверенным вендорам возможность писать сценарии и заливать их на наши устройства. То есть нужна безопасная песочница для выполнения сторонних скриптов. Есть различные варианты для решения этой задачи:

* Embedded lua. Но, поскольку плагины пишутся вендорами на JS, то и сценарии хочется писать на JS. И остается вопрос, можно ли создать с lua безопасную песочницу.
* Docker контейнеры. Они и так везде на проекте и для этой задачи они будут использоваться, но в контейнерах будет крутиться целый движок и все сценарии. Свой контейнер для каждого отдельного сценария слишком ресурсоёмко. Сценариев будет тысячи.
* Воркеры на WebAssembly - хороший вариант. То есть можно разрешить использовать в теории любой язык. Это отлично для Rust, по поводу JS (с тем же Duktape) не уверен.

В результате остановились на модуле vm2. Это как стандартный нодовский модуль vm, только для безопасного выполнения.

VM2 позволяет вам запустить чужой JavaScript код в безопасной пеcочнице. Вы можете разрешить доступ к определенным модулям или даже к опредленным методам определенных модулей. VM2 использует Proxy объекты, чтобы нельзя было вырваться из песочницы. Конечно любая такая песочница не даёт 100% гарантий, но в нашем случае это отличное решение, особенно в связке с docker.

Если вдруг будет стоять такая задача, рекомендую посмотреть на этот модуль.

VM2 - https://www.npmjs.com/package/vm2
Тред от Yehuda Katz про проблемы с кроссплатформенностью NodeJs модулей. Многие разработчики забивают на Windows. В этом нет большой проблемы, пока ты не публикуешь свой модуль на NPM.

Яхуда озвучивает 2 основные проблемы при работе под Windows:
1. Практически каждый package.json содержит команды в секции scripts, которые не работают в Windows. Основная причина - использование && и другого bash синтаксиса. От себя добавлю, часто вижу, что устанавливаются переменные окружения без использования crossenv, к примеру.
2. Это пути на файловой системе. Разработчики ожидают, что разделитель "/" - это валидный разделитель в Windows разделитель "\" ("\\"). Это создает ряд багов связанных с обработкой путей (например, когда мы обабатываем пути регулярками и тд).

Как делать правильно, можно найти в треде
https://twitter.com/wycats/status/1090307478254829569

Делайте свои модули кроссплатформенными, Micsrosoft сделала же VSCode под Linux и Mac :)
В догонку к позавчерашнему сообщению про vm статья о том, как Cloudflare начала использовать изоляты V8 для serverless вычислений. Идея дать возможность пользователям заливать свой код и выполнять его в безопасной песочнице (по примеру AWS Lambda или Google Cloud Functions)

Что мы имеем, в сравнение с классическим подходом, когда лямбды крутятся в docker контейнерах:
1. Холодный старт 5 ms, вместо от 500ms.
2. Потребление памяти 3mb вместо минимальных 35mb необходимых при механизме с контейнерами.
3. Стоимость в 3 раза ниже, чем AWS Lambda. Правда код нужно писать на жабаскрипте :)

СТАТЬЯ: https://blog.cloudflare.com/cloud-computing-without-containers/

После прочтения поста возникает вопрос, что такое "Isolate"? В интернете практически нет информации. Можно найти, что это deprecated фича, но на самом деле это не так.
Если посмотреть в исходники NodeJs, то можно увидеть, NodeJs workers используют изоляты -
https://github.com/nodejs/node/blob/master/src/node_worker.cc , а поверх node_worker, в свою очередь, построены новые worker_threads.

Isolate - это, по сути, отдельный экземпляр V8, со своим состоянием. Важно не перепутать:
1. Изоляты это фича v8, а не nodejs. worker_threads и тот же vm - это уже модули nodejs.
2. Cloudflare не использует NodeJs, а использует именно v8 (и изоляты).
Многие спрашивают про Test Coverage, каким он должен быть? Нужно ли стремится к 100% покрытию кода тестами?

Основная проблема, что уровень покрытия кода тестами ничего не говорит о качестве тестов. Помню мне рассказывали историю, когда покрытие было недостаточным и не могли принять реквест от разработчика. Разработчик не долго думая добавил просто вызовы функций в тесты (без проверки результата), чтобы увеличить покрытие до требуего уровня :).

Если вы стремитесь к 100% покрытию, то скорее всего тратите время не на то, что действительно важно (покрыть более сложные сценарии или граничные случаи). Главный критерий достаточного количества тестов - это количество багов на продакшене и насколько вы боитесь вносить изменения в код в связи с ожиданием дополнительных багов на продакшене.

Нужно ли использовать test coverage? Да, мы используем по следующим причинам:
1. Test coverage не позволяет быть увереным в наличие хороших тестов, но низкий test coverage явно говорит об отсутствии тестов вообще. Для бекенда для нас 80% покрытия всегда достаточно. С nyc (istanbul) отслеживать покрытие очень легко. Для фронтенда у нас нет требований по покрытию.
2. Поиск мертвых кусков кода. Про это в статье не говорится, но при помощи анализа покрытия это очень удобно делать. nyc (istanbul) позволяет в качестве репортера указать html и вы очень легко можете найти код, который никогда не вызывается и никогда не будет вызываться. Поиск мертвых кусков кода сложно переоценить. Часто этого кода бывает много и он сильно мешает потом новым разработчикам разбираться в проекте.
3. Поиск подсистем, которые вообще не покрыты тестами. Бывают ситуации, когда просто не написали тесты к какой-то функциональности по какой-то причине.

Более детально про покрытие кода тестами можно почитать у Мартина Фаулера в статье "Test Coverage" https://martinfowler.com/bliki/TestCoverage.html

Статья уже старая, но вопрос актуальный :)
Отличный пост от Дэна Абрамова про то, как концептуально работает React - https://overreacted.io/react-as-a-ui-runtime/
Пост не перегружен исходниками, просто понятное краткое описание каждого аспекта работы React.
Eсли вы используете React (независимо от вашего уровня junior, middle, senior, architect, guru, js ninja, js samurai etc) и хотите чуть лучше его понимать, то очень рекомендую.
К примеру, я никогда не задумывался про Lazy Evaluation.
Интересно, что в посте ни одного примера на классах, все на компонентах-функциях с использованием хуков. Фейбук делает хуки своим основным API, классы, я думаю, останутся только для соместимости.
Решил продолжить про тестирование.
Когда в Jest добавили снапшпоты и возможность тестировать React компоненты при помощи снапшотов, все ринулись их использовать и писать насколько жизнь стала лучше :). Но снапшот тестирование может принести больше проблем, чем пользы:
1. Снапшот тесты хрупкие. Один assert проверяет все, вместо конктетной функциональности.
2. Нарушают инкапсуляцию. На один компонент Button может опираться масса других компонентов. Его поменяли и все посыпалось (особенно, если без shallow render)
3. Возникает антипаттерн "Guru Checks Output". Когда все посыпется, джун просто запустит jest -u && git commit. То есть, всегда должен быть человек ("Гуру", который знает детали реализации), который проверит, весь вывод и опираясь на свои знания деталей реализации, скажет это мы что-то сломали или тесты посыпались, но мы ничего не сломали и можно комитить.

На самом деле, снапшот тесты отличный инструмент (главное их правильно использовать), но они не слепая замена обычным тестам. К примеру, снапшоты хороши для контроля регресии в легаси коде.

Хороший пост на эту тему http://randycoulman.com/blog/2016/09/06/snapshot-testing-use-with-care/
И про "Guru Checks Output" http://wiki.c2.com/?GuruChecksOutput.
Мы пишем на JavaScript, но бывают случаи, когда нужная нам библиотека есть только на Java. В моей практике была такая ситуация. Мне нужна была библиотека Semgrex со стендфордского NLP парсера. Semgrex - эта библиотека, которая позволяет применять регулярки для выбора узлов с дерева разбора предложения (гуглить dependency tree nlp).
Конечно, можно писать весь код на Java, но не сильно хочется. Другой вариант - это вызывать Java из NodeJs.
Java предоставляет JNI (Java Native Interface), что дает нам возможность вызывать методы Java с другого языка и под NodeJs есть отличный модуль https://www.npmjs.com/package/java, который является мостом к JNI.
В результате, вы без особых проблем можете использовать классы Java внутри вашего NodeJs приложения, как если бы они были классами JavaScript.

В качестве примера, можете глянуть на исходники nodejs обертки вокруг java Semgrex https://www.npmjs.com/package/semgrex
Хочу поделиться отличным постом моего коллеги из WebbyLab. В названии звучит "Redux на бекенде". На самом деле, статья про то, как удалось переиспользовать одни и те же редьюсеры в веб, в мобильном и в бекенд приложениях, автоматически получить полноценную оффлайн работу приложения практически без дополнительных усилий. Статья рассказывает про event sourcing, показывает общее и различное между Redux и event sourcing, демонстрирует необычное, но эффектинове решение на базе совмещения Redux и event sourcing.

Мы привыкли к классическим схемам с React и Redux, но этот пост расширяет кругозор и предлагает другой подход.

https://hackernoon.com/how-we-used-redux-on-backend-and-got-offline-first-mobile-app-as-a-result-b8ab5e7f7a4
Попалась мне сегодня на глаза статья "Using TensorFlow.js to Automate the Chrome Dinosaur Game (part 1)". Да, это неплохой тьюториал, но ничего особенного для тех, кто уже работает с Tensorflow (а так можете почитать), но интересна она по другой причине.

Считается, что ML/AI существует только в Python. А что же с другими языками, в том числе с JavaScript? JavaScript используется для всего сегодня и странно, если бы не началось ML движение на JS. По факту, так и происходит.

К примеру, в прошлом году мы делали проект связанный с интеллектуальным поиском, вся эта история с различными алгоритмами word embedding и тд. Изначально мы начали все делать на python, а потом переключились на JS ввиду того, что на npm есть отличные врапперы вокруг C/C++ реализаций этих алгоритмов (сейчас пишем про это статью). Зачастую эти библиотеки работа комьюнити, но в случае с TensorFlow.js - это официальная библиотека от Google.

Сначала Google выпустил deeplearn.js - библиотека для ML с аппартным ускорением, которая работает в браузере. Браузер не поддерживает CUDA, но все равно можно использовать WebGL и шейдеры для математический расчетов. Вкратце, библиотека создает шейдер, который расчитывает цвет пикселя, а число, которое описывает цвет пикселя, по сути, и есть результат нашей математической операции (еще один костыль в мире веб :)). Эта библиотека и стала ядром для TensorFlow.js и была переименована в "TensorFlow.js Core".
Это очень круто и открывает много возможностей, но это про браузер. Как же работает TensorFlow.js в NodeJS? На самом деле, достаточно долго TensorFlow.js не работал в NodeJs (только недавно добавили поддержку). В NodeJs TensorFlow.js является байндингом к C/C++ имплементации (аналогично, как работает Python версия).

Также важно, что Keras API уже давно есть частью TensorFlow и поддерживается JS версией ( правда небольшие отличия все же есть) и вы можете работать с привычным высокоуровневым API.

Осталось дождаться порта scikit-learn на JS 😁

СТАТЬЯ: https://heartbeat.fritz.ai/automating-chrome-dinosaur-game-part-1-290578f13907
Отличная статья про микросовервисную архитектуру в Medium. Это тот случай, когда все сделано опираясь на здравый смысл. Изначально, у медиум был монолит на NodeJS, но ввиду ряда ограничений в 2018 они перехали на микросервисы.

Из ключевого:
1. Не делайте микросервисы ради микросервисов. Выносите в микросервисы только то, что имеет смысл выносить.
2. Микросервисы это не про количество строк кода. Микросервисы могут быть большими, главное чтобы они решали одну конкретную задачу.
3. Каждый микросервис имеет свой набор данных (разные способы достижения этого описаны в статье). Использование одной общей базы плохая идея.
4. RPC больше подходит чем REST для коммуникации между микросервисами. Medium использует gRPC.
5. Не обязательно делать микросервисы с нуля. Можно взять часть кода вашего монолита и вынести в отдельный сервис.
6. Плохо сконструированные микросервисы могут быть хуже монолита. Если вы не можете построить рабочий монолит, то с чего вы взяли, что сможете построить микросервисную архитектуру 😁
7. Микросервисы это еще не повод втянуть максимальное количество различных технологий, поскольку это может увеличить стоимость поддержки и фрагментировать команду.
8. Для небольших команд монолит будет лучшим решением, просто делайте его модульным.

СТАТЬЯ: https://medium.engineering/microservice-architecture-at-medium-9c33805eb74f
"The TypeScript Tax: A Cost vs Benefit Analysis" от Эрика Эллиота - пост, который поднимает вопрос дает ли на самом деле TypeScript выигрыш на крупных проектах. Если вы его не читали еще, то очень советую прочитать. К тому же, 11 февраля он был расширен кейсом от AirBnB.

Эрик правильно говорит, что нельзя сравнивать просто TypeScript против JavaScript. Нужно сравнивать JavaScript+tooling+процессы против TypeScript+tooling+процессы. И в таком случае TypeScript дает слишком мало преимуществ.
Когда у вас есть инструменты, отстроены процессы разработки, то из 1000 багов вы находите 900, с остатка максимум 20% вам позволит найти TypeScript. То есть это 20 багов из 1000, и сразу возникает вопрос возврата вложений в технологию. Стоит ли оно того?

Мы попробовали TypeScript и Flow.js у себя на нескольких проектах и вот, что получили:
1. VSCode умеет делать автокомплит и для чистого JavaScript без аннотаций. Иногда удивляет, когда есть фабричный метод, который возвращает объект, в котором экземпляр класса, в котором есть свойства, которые отлично автокомплитятся :)
2. Мы по максимуму используем eslint для статического анализа и это покрывает основную часть проблем. Если нет нужного плагина, то мы пишем свои "eslint-plugin-more"
3. Я согласен с Эриком, что для перехода на TypeScript, разработчику нужно 2-3 месяца и 6-8 месяцев, чтобы им овладеть полностью.
4. Описание аннотаций занимает достаточно большое количество времени и при тех же временных затратах тесты более эффективны. Кроме того, после тестов, типизация дает не настолько существенный выигрыш.
5. Типы можно описывать по разному и достичь какого-то стандарта кросс-проектно достаточно тяжело. Кто-то считает, что нужно выжать все с TypeScript, кто-то - что нужно минимум аннотаций.
6. Далеко не всем JavaScript разработчикам нравится TypeScript

Я не против TypeScript, мне нравится идея статической типизации, считаю TypeScript отличным продуктом, но всегда нужно взвешивать за и против от использования той или иной технологии. Для меня чистый JavaScript это выбор по умолчанию, а TypeScript/Flow уже опция. И для полноты картины, могу привести случай, когда статическая типизация дала нам большой выигрыш. Мы использовали GTK байндинг к Spidermonkey и аргументы неправильного типа приводили к крашу всего приложения. Статическая типизация позволяла избежать этих ситуаций.

В целом, эти же аргументы описывает Эрик в своей статье, но еще он затрагивает вопросы рекрутинга, обучения, рефакторинга кода и тд.
Наткнулся на интересный тред в твиттере.
Чувак увидел, что кто-то форкнул его проект, не только форкнул, но и поменял авторство. В результате оказалось, что есть ребята, которые форкают известные репозитарии, называют их по другому, меняют авторство на свое, переписывают историю коммитов на свои аккаунты на Github. В результате, аккаунт, который был создан недавно имеет отличную историю коммитов, красивые графики, море активности и тд. И таких аккаунтов не один, они друг друга фоловят и тд. Зачастую это проекты на JavaScript.
Остается вопрос зачем они это делают. Может это какой-то бот-нет и подготовка к атаке на npm, а может кому-то нужен красивый акк, чтобы получить проект на upwork :). Гипотезы можете найти в Twitter треде.
Почти всегда, при разговоре о производительности веб-сайта, мы говорим о времени ожидания пользователем какого-то события ("First Meaningful Paint" и тд). Мы часто обсуждаем оптимизации фронтенда и бэкенда. И это круто, но остается корень всех бед - скорость света. Часто JavaScript разработчики упускают это проблему из виду 😀

Может ли случится такое, что через 30 лет Интернет будет настолько быстрым, что сайт с сервера в Сан-Франциско будет моментально открываться в Киеве? Физики говорит, что нет.

Допустим:
1. Твой бекенд рендерит страницу (или формирует JSON) за 20мс.
2. Не существует никакого WiFi, провайдеров, маршрутизации и тд. Есть просто оптоволоконный кабель, который одним концом вставлен в твой ноутбук, а вторым напрямую в сервер в Сан-Франциско. Растояние по прямой от Киева до Сан-Франциско - 9,848км (возьмем 10 тыс км для простоты счета).
3. Скорость света в вакуме 300 тыс км в секунду, скорость света в оптоволокне будет ниже - 200 тыс км в секунду.

Если мы посчитаем время, которое проведет наш запрос в пути, то мы получим 100 мс (10 тыс / 200 тыс * 2). Быстрее получить ответ не позволит скорость света. Добавляем время оработки запроса и мы получим 120мс - в 6 раз дольше, чем наш запрос обрабатывает наш бэкенд.

========= Запрос к бэкенду ======
50ms: Kyiv -------запрос-----> SF
20ms: работа бэкенда
50ms: Kyiv <------ответ------- SF


Хорошо, мы уже выяснили, что никогда не поиграем в CS:GO с ребятами с Сан-Франциско с пингом ниже 100мс. Давайте дальше :)

Перед тем как запросить данные с сервера мы должны установить сетевое соединение. Протокол HTTP работает поверх TCP, следовательно нам нужно TCP соединение с сервером.

Для установки TCP соедения используется так называемое "тройное рукопожатие" ("TCP 3-way handshake") и теперь наш запрос выглядит:

========= TCP соединение ========
50ms: Kyiv -------syn--------> SF
50ms: Kyiv <------syn/ack----- SF
50ms: Kyiv -------ack--------> SF
========= Запрос к бэкенду ======
Kyiv -------запрос-----> SF
20ms: работа бэкенда
50ms: Kyiv <------ответ------- SF


Мы не тратим дополнительные 50ms после TCP хендшейка, поскольку мы можем сразу начать отправлять запрос после отправки ack, нам не нужно ждать ответ от сервера. Сервер, как примет ack, посчитает соединение открытым и сразу начнет обрабатывать наш запрос.

То есть ответ пользователь получит через 220ms, в 11 раз дольше, чем отрабатывал наш бэкенд.

Но мы используем HTTPS и нам нужно SSL/TLS соединение и оно устанавливается поверх TCP, и у него есть свой механизм рукопожатия для обменя ключами шифрования, и это нужно сделать до момента, как мы отправим наш запрос на сервер.

Наша схема превращается в:

========= TCP соединение ========
50ms: Kyiv -------syn--------> SF
50ms: Kyiv <------syn/ack----- SF
50ms: Kyiv -------ack--------> SF
========= TLS соединение ========
Kyiv ---представление--> SF
50ms: Kyiv <--сертификаты----- SF
50ms: Kyiv ---обмен ключами--> SF
50ms: Kyiv <--обмен ключами--- SF
========= Запрос к бэкенду ======
50ms: Kyiv -------запрос-----> SF
20ms: работа бэкенда
50ms: Kyiv <------ответ------- SF


То есть в условиях, которые не могут даже существовать, когда пользователь имеет оптоволоконный кабель длинной в 10тыс км от своего ноутбука к серверу, он получит ответ через 420мс, в 21 раз дольше чем отрабатывает наш бэкенд. Это без учета того, что нам нужно еще вначале сбегать к DNS, чтобы получить ip-адрес сервера.

Если мы разрабатываем веб-приложения (не важно фронтенд или бекэнд), то обязаны понимать азы работы веба.

Продолжение следует...
Нормализация массива через "for of" и "reduce".

Набросал воскресным утречком бенчмарк https://jsperf.com/normalize-array. Для данного случая реализация через "reduce" работает в 5 раз медленее в Chrome и в 50 раз медленее в FF.
Необходимо понимать причины такой разницы в производительности. Сам "reduce" такой же быстрый как и "for of", просто мы имеем n^2 ввиду создания нового объекта и копирования свойств на каждой итерации.
Конечно, можно мутировать объект в редьюсере, но это противоречит функциональной природе редьюса, наша функция должна быть без побочных эффектов.

Да и вообще нормализация списка через "for of" выглядит в разы понятнее (имхо).
Вот отличная рекомендация по использованию reduce: используйте reduce, когда результат имеет тот же тип, что и элементы и сам редьюсер ассоциативный.

складываем числа
умножаем числа
🚫 строим список объектов
🚫 все остальное (используй цикл)

Обратите внимание на требования к ассоциативности. К примеру, если вы знакомы с парадигмой распределенных вычислений "MapReduce" и посмотрите на "MapReduce" в MongoDB, то найдете там похожий ряд требования к reduce функции:
1. Ассоциативность.

const reducer = (a, b) => a + b;

// Оба варианта должны дать одинаковый результат
[1, 2, 3, 4].reduce(reducer);
[1, 2, [3, 4].reduce(reducer)].reduce(reducer);


2. Коммутативность

const reducer = (a, b) => a + b;
// Оба варианта должны дать одинаковый результат
[1, 2, 3, 4].reduce(reducer);
[4, 2, 1, 3].reduce(reducer);


Есть еще требование к идемпотентности, но для нашего случая оно малоприменимо.

В целом, это все лишь рекомендации и идеи на подумать. Сам JavaScript не запрещает нам ничего.
Вот и продолжение: "Скорость света и Веб - часть 2" :)

Мы уже разобрались, что есть скорость света и она влияет на задержки при передаче данных. У нас есть задержки на TCP и TLS рукопожатия, также есть время в пути запроса и ответа. Можем ли мы говорить, что это максимальные задержки, которые мы получаем?

На самом деле все сложнее, и даже при самой высокой пропускной способности сети у нас будут дополнительные задержки в передаче данных.

Есть 2 нюанса, которые важны:
1. TCP контролирует доставку пакетов и для того, чтобы понять, что пакеты были доставлены нужно какое-то подтверждение от получателя. Для этого в ответ отправляется пакет с флагом "ack" (acknowledge).
2. Клиент и сервер изначально не знают доступной на данный момент пропускной способности сети. Она зависит от возможностей сервера, от возможностей промежуточных узлов, от активности других узлов в этой же сети и тд. Единственный способ узнать - это пробовать передавать данные с разной скоростью и смотреть доходят ли они (ждать подтверждения, что вторая сторона получила их).

Как это работает?
Когда мы делаем запрос к серверу он сначала отправляет нам часть данных, потом ждет подтверждения, потом увеличивает объем передаваемых данных в 2 раза и опять ждет ответ. Если все ок, еще раз увеличивает и так далее до момента пока он не достигнет максимального объема данных, которые готов принимать клиент.

Как это все называется?
✳️ Механизм постепенного увеличения скорости передачи данных называется "TCP Slow Start"
✳️ Лимит отправителя на объем данных в пути называется "Congestion window size" (CWND). После отправки этого объема данных, отправитель должен ждать подтверждения о том, что данные дошли. Увеличения этого лимита и есть "TCP Slow Start". ВАЖНО: про этот лимит знает только отправитель и он сам для себя его регулирует. CWND имеряется в "сегментах" (сегмент обычно не более 1,46KB). Стартовое значение по стандарту - 10 сегментов (14.6KB)
✳️ Также есть ограничение получателя на объем данных, которое он может принять - "Receiver window size" (RWND). Получатель отправляет отправителю RWND в каждом пакете с подтвержджением (с флагом ack). Поскольку передача дынных происходит в обе стороны, то каждая сторона может выступать как получателем, так и отправителем. Получатель может передать RWND равным нулю, это говорит о том, что отправитель должен приостановить передачу.

Обе переменные ограничивают количество данных, которое можно отправить, это всегда минимум с CWND и RWND.

Теперь давайте нарисуем, что на самом деле происходит, когда браузер хочет скачать наш JavaScript файл на 50KB.
Возьмем те же локациии - Киев и Сан-Франциско.

========= TCP соединение ========
50ms: Kyiv -------syn--------> SF
50ms: Kyiv <------syn/ack----- SF
50ms: Kyiv -------ack--------> SF
========= TLS соединение ========
Kyiv ---представление--> SF
50ms: Kyiv <--сертификаты----- SF
50ms: Kyiv ---обмен ключами--> SF
50ms: Kyiv <--обмен ключами--- SF
====== HTTP запрос к серверу ====
50ms: Kyiv -------запрос-----> SF
20ms: работа бекенда
50ms: Kyiv <-----14.6KB------- SF
50ms: Kyiv -------ack--------> SF
50ms: Kyiv <-----29.2KB------- SF
50ms: Kyiv -------ack--------> SF
50ms: Kyiv <-----6.2KB-------- SF

Скорость в 100Мбит/с говорит о том, что мы получим 50KB через 4ms, но на самом деле у нас это займет 620ms.
Самое интересное, что если бы наш JS файл был бы 40KB, то мы получили бы его на 100ms раньше.

Нам может казаться, что немного больший размер данных не влияет ни на что, если у пользоватлей быстрый интернет, но мы видим, что это не так.

Поэтому используйте Gzip компрессию c HTTP, следите за Cookie (они могут быть большими), сжимайте картинки и удаляйте с них метаданные . Конечно, не забывайте про CDN (может дать существенный выигрышь).

Дальше я попробую описать более детально, что у нас есть, чтобы сделать наши веб-приложения более быстрыми.

Продолжение следует...
Обычно я не скидывают ссылки просто на новости, но это интересный ресерч, который провели ребята из Snyk (Open Source Security Platform).

В статье говорится, что официальный докер образ с NodeJs содержит множество уязвимостей.

* Стандартный образ "node:10" содержит 580 известных уязвимостей.
* Слим образ "node:10-slim" содержит 71 известную уязвимость.
* Образ node:10-alpine не содержит известных уязвимостей.

Более того, если взять топ-10 образов на dockerhub, то каждый из них содержит минимум 30 уязвимостей.

Основная проблема в том, что многие пакеты собираются на базе одних и тех же базовых образов. То есть, NodeJs сборки обновляются, а базовый образ старый и его никто не обновляет и не исправляет. Зачастую достаточно пересборки контейнера с другим базовым образом, чтобу улучшить ситуацию с уязвимостями.

Если вы используете NodeJs, то смотрите в сторону node:10-alpine. Alpine Linux - это один из самых легковесный дистрибутивов линукса. Чистый образ Alpine Linux занимает 5МБ.
По сути, это musl libc (минималистчиная замена glibc, нужен нам для коммуникация с ядром и базовых операций) и busubox (маленький бинарь, который содержит самые необходимые unix утилиты. К примеру, android его использует).

На базе Alpine есть образы не только для NodeJs, но и для другого софта. Рекомендую свои образы строить тоже на базе Alpine Linux.

СТАТЬЯ: https://snyk.io/blog/top-ten-most-popular-docker-images-each-contain-at-least-30-vulnerabilities/
Я время от времени делаю доклады про архитектуру веб-приложений. И там всегда есть 2 важных пункта:

1. Архитектура вашего приложения не должна зависеть от веб-фреймворка. Нужно писать код так, чтобы вы могли заменить веб-фреймворк (скорее всего вы делать это не будете, но это хорошая мысленная проверка на связанность).
2. Model (из MVC) - это ваша бизнес-логика и логика приложения. Model может быть реализован совершенно разными способами. Чаще всего у нас это Services(use cases) + Domain Model. И слой Model не зависит от Controller и View.

Если все сделано правильно, то вы сможете без проблем заменить JSON в вашем REST API на XML, и вам нужно будет только обновить контроллеры. Или даже больше, вы меняете веб-интерефейс на интерфейс командной строки и вам не нужно переписывать бизнес-логику, поскольку все смещенно в слой Model.

Да, мы очень редко переписываем веб-интерфейс на CLI, но мы часто пишем различные скрипты для управления пользователями, для пакетного импорта данных, для многих других задач.

И если у вас все хорошо с архитектурой, то вы не работаете с базой данных напрямую. Вы подключаете Domain Model или нужные сервисы в ваши скрипты и работаете уже с ними. Это обеспечивает целостность ваших данных. Кроме того, для многих операций в будущем может появится Web-интерфейс.

API ваших скриптов - это аргументы командной строки, их нужно парсить. Поскольку в WebbyLab мы используем не только NodeJs, то для парсинга аргументов нам зашел http://docopt.org/ Есть реализация под различные языки (есть docopt модуль и на npm). Основная идея - вы пишите справку по использованию, а docopt автоматически парсит аргументы командной строки в соотвествии со справкой.

Отличный пример на сайте docopt, копируете его и правите под себя :)

Naval Fate.

Usage:
naval_fate ship new <name>...
naval_fate ship <name> move <x> <y> [--speed=<kn>]
naval_fate ship shoot <x> <y>
naval_fate mine (set|remove) <x> <y> [--moored|--drifting]
naval_fate -h | --help
naval_fate --version

Options:
-h --help Show this screen.
--version Show version.
--speed=<kn> Speed in knots [default: 10].
--moored Moored (anchored) mine.
--drifting Drifting mine.


и теперь ваше приложение разбирает все аргументы в соотвествии с Usage документацией.
Иногда, для более простых случаев мы можем использовать minimist.

Но совсем недавно мне скинули неплохой тьюториал для новичков про то, как оформить ваше CLI приложение в npm пакет "A guide to creating a NodeJS command-line package"

Он будет хорошим дополнением ко всему вышесказанному.

Основные моменты статьи:
1. Не забываете про шебанг #!/usr/bin/env node
2. Делайте файл исполняемым chmod +x cli.js
3. Добавьте секцию bin в ваш package.json. Например, bin: {myapp: './cli.js'}. Тогда при установке вашего пакета npm создаст в директории с бинарями симлинк "myapp", который будет указывать на ваш "cli.js".
4. npm link линкует не только пакеты, но и скрипты с секции bin. Этого я сам не знал :)
Сегодня хочу поделиться отличной статьей от одного из моих коллег из WebbyLab. Статья про JavaScript, машинное обучение и Игру Престолов 😁

Не так давно мы делали интересный проект связанный и интелектуальным поиском по медицинским данным. Основная задача - поиск по смыслу (не путайте с полнотекстовым поиском). Например, если мы ищем "рак", то система должна находить "меланома". Мы можем искать по аналогиям вида, для лечения болезни "А" используется "Б", что тогда используется для лечени болезни "C". Чтобы это реализовать, для каждого слова необходимо построить некоторое представление, которое хранит его смысл (смысл определяется словами вокруг целевого слова и называется контекстом). Таким представлением выступает вектор в N-мерном пространстве (обычно от 100 до 300). Имея такие вектора для каждого слова, можно делать математические операция над векторами, что позволяет искать похожие слова по смыслу и тд. Это называется word embeddings и есть различые модели построения векторов (word2vec, GloVe, fastText и тд). Все детали есть в статье.

Но для того, чтобы не было скучно, автор решил обучать модели текстами романа "Игра Престолов" и искать связи между героями 🙈. Что из этого получилось - найдете в статье.

Кроме того, ты сможешь повторить все это самостоятельно с любым текстом (код прилагается).

Статья на Medium: https://medium.com/@WebbyLab/how-to-train-word2vec-model-with-nodejs-in-5-steps-41b602080c1a

Подписывайтесь на наш аккаунт на Medium :)