Жабысриптеры, привет) Я буду скидывать сюда ссылки на классные статьи про JavaScript и JavaScript библиотеки 💩
ИМХО, Kitze один из лучших спикеров прошедшего React Day Berlin. "Navigating the Hype-Driven Frontend Development World Without Going Insane" очень легкий и позитивный доклад. Возможно, доклад не сильно технический, но видно, что Kitze очень крутой жабаскриптер и все по делу.
Отличное видео, чтобы расслабиться в воскресенье :)
https://www.youtube.com/watch?v=QZ6aC6G0ufg
Отличное видео, чтобы расслабиться в воскресенье :)
https://www.youtube.com/watch?v=QZ6aC6G0ufg
YouTube
Navigating the Hype-Driven Frontend Development World Without Going Insane - Kitze
Talk recording from React Day Berlin Conference 2018 https://reactday.berlin
The obsession to always move on to the latest and greatest, and the fear of missing out while working with something that's older than a few months is currently dominating the dev…
The obsession to always move on to the latest and greatest, and the fear of missing out while working with something that's older than a few months is currently dominating the dev…
Много лет я писал на Perl и там огромное количество удобных библиотек для написания тестов. В случае с JS все не так хорошо и много лет использую mocha + chai, но недостатков у этих библиотек хватает.
Недавно нашел AVA и понял, что в JS тоже случаются замечательные вещи :)
AVA - это новый инструмент, который решает большинство этих проблем.
1. Не опирается на глобальную доступность тестовых утилит.
2. Позволяет запускать тесты параллельно.
3. Простой и удобный API и очень простая конфигурация (например, даже с mocha использовал qunit интерфейс. Имхо, более читабельны). В случае с AVA сетап всего очень прост.
4. Современный ES из коробки. Я могу писать тесты с использованием всех самых современных фич JS, включая import и тд. Что более важно, тестируемый код будет работать как есть (без транспайлинга). Это позволяет мне быть уверенным, что мой код работает на конкретной версии ноды.
5. Основные ассерты из коробки. Часто нужна только AVA и ничего больше.
6. Ну и поддержка TAP, как крассплатформеный формат репортинга для тестов. Кстати, TAP пришел из Perl, в Perl - это аутпут всех библиотек для тестирования :)
Перевел несколько своих библиотек на AVA (к примеру, тесты на AVA для LIVR js), результатом очень доволен.
AVA - https://www.npmjs.com/package/ava
Недавно нашел AVA и понял, что в JS тоже случаются замечательные вещи :)
AVA - это новый инструмент, который решает большинство этих проблем.
1. Не опирается на глобальную доступность тестовых утилит.
2. Позволяет запускать тесты параллельно.
3. Простой и удобный API и очень простая конфигурация (например, даже с mocha использовал qunit интерфейс. Имхо, более читабельны). В случае с AVA сетап всего очень прост.
4. Современный ES из коробки. Я могу писать тесты с использованием всех самых современных фич JS, включая import и тд. Что более важно, тестируемый код будет работать как есть (без транспайлинга). Это позволяет мне быть уверенным, что мой код работает на конкретной версии ноды.
5. Основные ассерты из коробки. Часто нужна только AVA и ничего больше.
6. Ну и поддержка TAP, как крассплатформеный формат репортинга для тестов. Кстати, TAP пришел из Perl, в Perl - это аутпут всех библиотек для тестирования :)
Перевел несколько своих библиотек на AVA (к примеру, тесты на AVA для LIVR js), результатом очень доволен.
AVA - https://www.npmjs.com/package/ava
npm
npm: ava
Node.js test runner that lets you develop with confidence.. Latest version: 6.4.1, last published: 2 months ago. Start using ava in your project by running `npm i ava`. There are 499 other projects in the npm registry using ava.
В JavaScript не так давно добавили тип данных Symbol. Обычно говорят, что Symbol дает возможность хранить метаданные в объекте или возможность реализации условно приватных свойств объекта. Но есть и другая сторона, Symbol решает ряд проблем и безопасностью приложения.
Отличный пост от Дэна Абрамова на эту тему. Основная идея в том, что React узлы внутри представлены в виде JSON объектов. И код, который вы считаете безопасным
на самом деле, содержит XSS уязвимость. Если message.text содержит пользовательский ввод и мы рендерим React на сервере, то злоумышленик может вставить в message.text JSON объект, который описует любую React ноду. Для того, чтобы от этого защититься, в JSON объекте должно быть свойство $$typeof и Symbol в качестве значения. В таком случае, злоумышленик не сможет заранее предсказать Symbol.
Статья - https://overreacted.io/why-do-react-elements-have-typeof-property/
Еще отличный пример - это Sequalize. Для описания запроса раньше использовались строковые операторы
сейчас это:
где
Отличный пост от Дэна Абрамова на эту тему. Основная идея в том, что 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.overreacted.io
Why Do React Elements Have a $$typeof Property? — overreacted
It has something to do with security.
Есть 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
* 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 :)
Яхуда озвучивает 2 основные проблемы при работе под Windows:
1. Практически каждый package.json содержит команды в секции scripts, которые не работают в Windows. Основная причина - использование && и другого bash синтаксиса. От себя добавлю, часто вижу, что устанавливаются переменные окружения без использования crossenv, к примеру.
2. Это пути на файловой системе. Разработчики ожидают, что разделитель "/" - это валидный разделитель в Windows разделитель "\" ("\\"). Это создает ряд багов связанных с обработкой путей (например, когда мы обабатываем пути регулярками и тд).
Как делать правильно, можно найти в треде
https://twitter.com/wycats/status/1090307478254829569
Делайте свои модули кроссплатформенными, Micsrosoft сделала же VSCode под Linux и Mac :)
Twitter
Yehuda Katz 🥨
I've been using Windows for a few years. I originally switched because I noticed that there were a lot of Windows users trying to program in JavaScript, but that the dominant perspective in OSS (at the time) was "nobody uses Windows so it doesn't matter".
В догонку к позавчерашнему сообщению про 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 (и изоляты).
Что мы имеем, в сравнение с классическим подходом, когда лямбды крутятся в 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 (и изоляты).
The Cloudflare Blog
Cloud Computing without Containers
We have a cloud computing platform called Workers. Unlike essentially every other cloud computing platform I know of, it doesn’t use containers or virtual machines. We believe that is the future of Serverless and cloud computing in general, and I’ll try to…
Многие спрашивают про 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
Статья уже старая, но вопрос актуальный :)
Основная проблема, что уровень покрытия кода тестами ничего не говорит о качестве тестов. Помню мне рассказывали историю, когда покрытие было недостаточным и не могли принять реквест от разработчика. Разработчик не долго думая добавил просто вызовы функций в тесты (без проверки результата), чтобы увеличить покрытие до требуего уровня :).
Если вы стремитесь к 100% покрытию, то скорее всего тратите время не на то, что действительно важно (покрыть более сложные сценарии или граничные случаи). Главный критерий достаточного количества тестов - это количество багов на продакшене и насколько вы боитесь вносить изменения в код в связи с ожиданием дополнительных багов на продакшене.
Нужно ли использовать test coverage? Да, мы используем по следующим причинам:
1. Test coverage не позволяет быть увереным в наличие хороших тестов, но низкий test coverage явно говорит об отсутствии тестов вообще. Для бекенда для нас 80% покрытия всегда достаточно. С nyc (istanbul) отслеживать покрытие очень легко. Для фронтенда у нас нет требований по покрытию.
2. Поиск мертвых кусков кода. Про это в статье не говорится, но при помощи анализа покрытия это очень удобно делать. nyc (istanbul) позволяет в качестве репортера указать html и вы очень легко можете найти код, который никогда не вызывается и никогда не будет вызываться. Поиск мертвых кусков кода сложно переоценить. Часто этого кода бывает много и он сильно мешает потом новым разработчикам разбираться в проекте.
3. Поиск подсистем, которые вообще не покрыты тестами. Бывают ситуации, когда просто не написали тесты к какой-то функциональности по какой-то причине.
Более детально про покрытие кода тестами можно почитать у Мартина Фаулера в статье "Test Coverage" https://martinfowler.com/bliki/TestCoverage.html
Статья уже старая, но вопрос актуальный :)
martinfowler.com
bliki: Test Coverage
Test coverage is useful for finding untested parts of a codebase, but it's of little use as a numeric statement of test quality.
Отличный пост от Дэна Абрамова про то, как концептуально работает 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, классы, я думаю, останутся только для соместимости.
Пост не перегружен исходниками, просто понятное краткое описание каждого аспекта работы React.
Eсли вы используете React (независимо от вашего уровня junior, middle, senior, architect, guru, js ninja, js samurai etc) и хотите чуть лучше его понимать, то очень рекомендую.
К примеру, я никогда не задумывался про Lazy Evaluation.
Интересно, что в посте ни одного примера на классах, все на компонентах-функциях с использованием хуков. Фейбук делает хуки своим основным API, классы, я думаю, останутся только для соместимости.
overreacted.io
React as a UI Runtime — overreacted
An in-depth description of the React programming model.
Решил продолжить про тестирование.
Когда в Jest добавили снапшпоты и возможность тестировать React компоненты при помощи снапшотов, все ринулись их использовать и писать насколько жизнь стала лучше :). Но снапшот тестирование может принести больше проблем, чем пользы:
1. Снапшот тесты хрупкие. Один assert проверяет все, вместо конктетной функциональности.
2. Нарушают инкапсуляцию. На один компонент Button может опираться масса других компонентов. Его поменяли и все посыпалось (особенно, если без shallow render)
3. Возникает антипаттерн "Guru Checks Output". Когда все посыпется, джун просто запустит jest -u && git commit. То есть, всегда должен быть человек ("Гуру", который знает детали реализации), который проверит, весь вывод и опираясь на свои знания деталей реализации, скажет это мы что-то сломали или тесты посыпались, но мы ничего не сломали и можно комитить.
На самом деле, снапшот тесты отличный инструмент (главное их правильно использовать), но они не слепая замена обычным тестам. К примеру, снапшоты хороши для контроля регресии в легаси коде.
Хороший пост на эту тему https://randycoulman.com/blog/2016/09/06/snapshot-testing-use-with-care/
И про "Guru Checks Output" https://wiki.c2.com/?GuruChecksOutput.
Когда в Jest добавили снапшпоты и возможность тестировать React компоненты при помощи снапшотов, все ринулись их использовать и писать насколько жизнь стала лучше :). Но снапшот тестирование может принести больше проблем, чем пользы:
1. Снапшот тесты хрупкие. Один assert проверяет все, вместо конктетной функциональности.
2. Нарушают инкапсуляцию. На один компонент Button может опираться масса других компонентов. Его поменяли и все посыпалось (особенно, если без shallow render)
3. Возникает антипаттерн "Guru Checks Output". Когда все посыпется, джун просто запустит jest -u && git commit. То есть, всегда должен быть человек ("Гуру", который знает детали реализации), который проверит, весь вывод и опираясь на свои знания деталей реализации, скажет это мы что-то сломали или тесты посыпались, но мы ничего не сломали и можно комитить.
На самом деле, снапшот тесты отличный инструмент (главное их правильно использовать), но они не слепая замена обычным тестам. К примеру, снапшоты хороши для контроля регресии в легаси коде.
Хороший пост на эту тему https://randycoulman.com/blog/2016/09/06/snapshot-testing-use-with-care/
И про "Guru Checks Output" https://wiki.c2.com/?GuruChecksOutput.
Randycoulman
Snapshot Testing: Use With Care
Snapshot Testing has been getting a fair bit of attention recently with some new tool support that makes it easy to use. But is that a good thing?
Мы пишем на 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
Конечно, можно писать весь код на 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
Мы привыкли к классическим схемам с React и Redux, но этот пост расширяет кругозор и предлагает другой подход.
https://hackernoon.com/how-we-used-redux-on-backend-and-got-offline-first-mobile-app-as-a-result-b8ab5e7f7a4
Hackernoon
How We Used Redux on Backend and Got Offline-First Mobile App as a Result
Today I want to share with you our experience of building an offline-first React-Native application, using Redux-like approach on our <a href="https://hackernoon.com/tagged/nodejs" target="_blank">NodeJS</a> backend server.
Попалась мне сегодня на глаза статья "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
Считается, что 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
Fritz ai
Using TensorFlow.js to Automate the Chrome Dinosaur Game - Fritz ai
In this blog post, we’ll be learning how to automate the Chrome Dinosaur Game using neural networks with TensorFlow.js. If you haven’t played it before, it’s a side scrolling game available offline (for when Chrome or your Internet crashes) where… Continue…
👍1
Отличная статья про микросовервисную архитектуру в Medium. Это тот случай, когда все сделано опираясь на здравый смысл. Изначально, у медиум был монолит на NodeJS, но ввиду ряда ограничений в 2018 они перехали на микросервисы.
Из ключевого:
1. Не делайте микросервисы ради микросервисов. Выносите в микросервисы только то, что имеет смысл выносить.
2. Микросервисы это не про количество строк кода. Микросервисы могут быть большими, главное чтобы они решали одну конкретную задачу.
3. Каждый микросервис имеет свой набор данных (разные способы достижения этого описаны в статье). Использование одной общей базы плохая идея.
4. RPC больше подходит чем REST для коммуникации между микросервисами. Medium использует gRPC.
5. Не обязательно делать микросервисы с нуля. Можно взять часть кода вашего монолита и вынести в отдельный сервис.
6. Плохо сконструированные микросервисы могут быть хуже монолита. Если вы не можете построить рабочий монолит, то с чего вы взяли, что сможете построить микросервисную архитектуру 😁
7. Микросервисы это еще не повод втянуть максимальное количество различных технологий, поскольку это может увеличить стоимость поддержки и фрагментировать команду.
8. Для небольших команд монолит будет лучшим решением, просто делайте его модульным.
СТАТЬЯ: https://medium.engineering/microservice-architecture-at-medium-9c33805eb74f
Из ключевого:
1. Не делайте микросервисы ради микросервисов. Выносите в микросервисы только то, что имеет смысл выносить.
2. Микросервисы это не про количество строк кода. Микросервисы могут быть большими, главное чтобы они решали одну конкретную задачу.
3. Каждый микросервис имеет свой набор данных (разные способы достижения этого описаны в статье). Использование одной общей базы плохая идея.
4. RPC больше подходит чем REST для коммуникации между микросервисами. Medium использует gRPC.
5. Не обязательно делать микросервисы с нуля. Можно взять часть кода вашего монолита и вынести в отдельный сервис.
6. Плохо сконструированные микросервисы могут быть хуже монолита. Если вы не можете построить рабочий монолит, то с чего вы взяли, что сможете построить микросервисную архитектуру 😁
7. Микросервисы это еще не повод втянуть максимальное количество различных технологий, поскольку это может увеличить стоимость поддержки и фрагментировать команду.
8. Для небольших команд монолит будет лучшим решением, просто делайте его модульным.
СТАТЬЯ: https://medium.engineering/microservice-architecture-at-medium-9c33805eb74f
Medium
Microservice Architecture at Medium
We would love to share our experiences of moving to microservice architecture effectively and avoiding “microservice syndromes”.
"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 и аргументы неправильного типа приводили к крашу всего приложения. Статическая типизация позволяла избежать этих ситуаций.
В целом, эти же аргументы описывает Эрик в своей статье, но еще он затрагивает вопросы рекрутинга, обучения, рефакторинга кода и тд.
Эрик правильно говорит, что нельзя сравнивать просто 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 и аргументы неправильного типа приводили к крашу всего приложения. Статическая типизация позволяла избежать этих ситуаций.
В целом, эти же аргументы описывает Эрик в своей статье, но еще он затрагивает вопросы рекрутинга, обучения, рефакторинга кода и тд.
Medium
The TypeScript Tax
A Cost vs Benefit Analysis
❤1
Наткнулся на интересный тред в твиттере.
Чувак увидел, что кто-то форкнул его проект, не только форкнул, но и поменял авторство. В результате оказалось, что есть ребята, которые форкают известные репозитарии, называют их по другому, меняют авторство на свое, переписывают историю коммитов на свои аккаунты на Github. В результате, аккаунт, который был создан недавно имеет отличную историю коммитов, красивые графики, море активности и тд. И таких аккаунтов не один, они друг друга фоловят и тд. Зачастую это проекты на JavaScript.
Остается вопрос зачем они это делают. Может это какой-то бот-нет и подготовка к атаке на npm, а может кому-то нужен красивый акк, чтобы получить проект на upwork :). Гипотезы можете найти в Twitter треде.
Чувак увидел, что кто-то форкнул его проект, не только форкнул, но и поменял авторство. В результате оказалось, что есть ребята, которые форкают известные репозитарии, называют их по другому, меняют авторство на свое, переписывают историю коммитов на свои аккаунты на Github. В результате, аккаунт, который был создан недавно имеет отличную историю коммитов, красивые графики, море активности и тд. И таких аккаунтов не один, они друг друга фоловят и тд. Зачастую это проекты на JavaScript.
Остается вопрос зачем они это делают. Может это какой-то бот-нет и подготовка к атаке на npm, а может кому-то нужен красивый акк, чтобы получить проект на upwork :). Гипотезы можете найти в Twitter треде.
Twitter
Sven Sauleau
Someone forked my project, rewrote all my commits and changed the name on the licence - https://t.co/sr9xL8nz1N. Oleg Kureichyk 🖕
Почти всегда, при разговоре о производительности веб-сайта, мы говорим о времени ожидания пользователем какого-то события ("First Meaningful Paint" и тд). Мы часто обсуждаем оптимизации фронтенда и бэкенда. И это круто, но остается корень всех бед - скорость света. Часто JavaScript разработчики упускают это проблему из виду 😀
Может ли случится такое, что через 30 лет Интернет будет настолько быстрым, что сайт с сервера в Сан-Франциско будет моментально открываться в Киеве? Физики говорит, что нет.
Допустим:
1. Твой бекенд рендерит страницу (или формирует JSON) за 20мс.
2. Не существует никакого WiFi, провайдеров, маршрутизации и тд. Есть просто оптоволоконный кабель, который одним концом вставлен в твой ноутбук, а вторым напрямую в сервер в Сан-Франциско. Растояние по прямой от Киева до Сан-Франциско - 9,848км (возьмем 10 тыс км для простоты счета).
3. Скорость света в вакуме 300 тыс км в секунду, скорость света в оптоволокне будет ниже - 200 тыс км в секунду.
Если мы посчитаем время, которое проведет наш запрос в пути, то мы получим 100 мс (10 тыс / 200 тыс * 2). Быстрее получить ответ не позволит скорость света. Добавляем время оработки запроса и мы получим 120мс - в 6 раз дольше, чем наш запрос обрабатывает наш бэкенд.
Хорошо, мы уже выяснили, что никогда не поиграем в CS:GO с ребятами с Сан-Франциско с пингом ниже 100мс. Давайте дальше :)
Перед тем как запросить данные с сервера мы должны установить сетевое соединение. Протокол HTTP работает поверх TCP, следовательно нам нужно TCP соединение с сервером.
Для установки TCP соедения используется так называемое "тройное рукопожатие" ("TCP 3-way handshake") и теперь наш запрос выглядит:
Мы не тратим дополнительные 50ms после TCP хендшейка, поскольку мы можем сразу начать отправлять запрос после отправки ack, нам не нужно ждать ответ от сервера. Сервер, как примет ack, посчитает соединение открытым и сразу начнет обрабатывать наш запрос.
То есть ответ пользователь получит через 220ms, в 11 раз дольше, чем отрабатывал наш бэкенд.
Но мы используем HTTPS и нам нужно SSL/TLS соединение и оно устанавливается поверх TCP, и у него есть свой механизм рукопожатия для обменя ключами шифрования, и это нужно сделать до момента, как мы отправим наш запрос на сервер.
Наша схема превращается в:
То есть в условиях, которые не могут даже существовать, когда пользователь имеет оптоволоконный кабель длинной в 10тыс км от своего ноутбука к серверу, он получит ответ через 420мс, в 21 раз дольше чем отрабатывает наш бэкенд. Это без учета того, что нам нужно еще вначале сбегать к DNS, чтобы получить ip-адрес сервера.
Если мы разрабатываем веб-приложения (не важно фронтенд или бекэнд), то обязаны понимать азы работы веба.
Продолжение следует...
Может ли случится такое, что через 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-адрес сервера.
Если мы разрабатываем веб-приложения (не важно фронтенд или бекэнд), то обязаны понимать азы работы веба.
Продолжение следует...
🔥3
Нормализация массива через "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. Ассоциативность.
2. Коммутативность
Есть еще требование к идемпотентности, но для нашего случая оно малоприменимо.
В целом, это все лишь рекомендации и идеи на подумать. Сам JavaScript не запрещает нам ничего.
Набросал воскресным утречком бенчмарк 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 не запрещает нам ничего.
Twitter
Sophie Alpert
my rule for .reduce(): only use it when the result has the same type as the items and the reducer is associative, like .reduce((a, b) => a + b, 0) ✅ summing some numbers ✅ multiplying some numbers 🚫 building up a list or object 🚫 just about anything else…
👍2
Вот и продолжение: "Скорость света и Веб - часть 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.
Возьмем те же локациии - Киев и Сан-Франциско.
Самое интересное, что если бы наш JS файл был бы 40KB, то мы получили бы его на 100ms раньше.
Нам может казаться, что немного больший размер данных не влияет ни на что, если у пользоватлей быстрый интернет, но мы видим, что это не так.
Поэтому используйте Gzip компрессию c HTTP, следите за Cookie (они могут быть большими), сжимайте картинки и удаляйте с них метаданные . Конечно, не забывайте про CDN (может дать существенный выигрышь).
Дальше я попробую описать более детально, что у нас есть, чтобы сделать наши веб-приложения более быстрыми.
Продолжение следует...
Мы уже разобрались, что есть скорость света и она влияет на задержки при передаче данных. У нас есть задержки на 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 соединение ========Скорость в 100Мбит/с говорит о том, что мы получим 50KB через 4ms, но на самом деле у нас это займет 620ms.
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
Самое интересное, что если бы наш JS файл был бы 40KB, то мы получили бы его на 100ms раньше.
Нам может казаться, что немного больший размер данных не влияет ни на что, если у пользоватлей быстрый интернет, но мы видим, что это не так.
Поэтому используйте Gzip компрессию c HTTP, следите за Cookie (они могут быть большими), сжимайте картинки и удаляйте с них метаданные . Конечно, не забывайте про CDN (может дать существенный выигрышь).
Дальше я попробую описать более детально, что у нас есть, чтобы сделать наши веб-приложения более быстрыми.
Продолжение следует...
👍1
