Программирование на Python и Objective-C в Mac OS

Программирование на Python и Objective-C под Mac OS и для iPhone / iPod Touch

Бэкэнд ожидания: Backend-разработчик (удаленно)

Содержание

Мой извилистый путь от охранника до backend-разработчика

24 Июля, 2017,
12:00

6292

Научиться программировать может каждый. Даже испанский охранник, учивший в колледже горное дело. Свою историю пути от охранника к backend-разработчику и основателю локального opensource-сообщества в блоге на Medium рассказал Хосе Мануэль Галлего. Редакция AIN.UA приводит полный перевод его истории.

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

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

Я работал на своем 400 событии на Campus Madrid Google после года карьеры в качестве охранника. Несколькими месяцами ранее моя жена родила сына. На кампусе меня окружили амбициозные предприниматели. Я видел как они делают важные шаги и идут на риск. Но я чувствовал себя в ловушке безнадежной работы, пытающийся свести концы с концами и обеспечить свою растущую семью.

На сцену поднялся спикер. Я стоял рядом и слушал каждое слово. Тогда он произнес предложение, которое изменило мою жизнь навсегда:

Любой может научиться программировать и работать разработчиком.

Сперва это заявление вызвало у меня скепсис: ивент имел отношение к дорогому буткемпу для разработчиков. К тому же, в Испании профессии, требующие интеллектуального труда, отведены людям с университетским дипломом. Но спикер сказал, что из-за высокого спроса работодатели идут на исключения. Он утверждал, что все, что необходимо для работы в разработке ПО, — пробиться через заграждение задач по программированию и интервью в процессе собеседования.

Я помню тот конкретный момент, когда закончил изучать все его заявления. Для меня стало откровением, что его слова не были просто маркетингом, а имели корни в экономике нашего времени. Тогда я принял решение. Я собирался стать разработчиком. Чего бы мне это не стоило.

В поиске курсов

У меня не было времени бездельничать, поэтому я решил воспользоваться лучшим способом обучения программированию. Я начал с расспрашивания друзей-разработчиков в Campus Madrid вроде Карлоса Эрнандеза, СЕО Gamify. Он порекомендовал начать с сайтов вроде Codecademy, а затем ставить конкретные цели и достигать их в его приложении. Я продолжал поиски онлайн, прыгая с одного ресурса на другой. День за днем я пробовал каждый из курсов, который находил.

Затем я поговорил с Даниелем Мери, основателем локального митапа по Haskell. Он был уверен, что я могу научиться программировать бесплатно, использую opensource-ресурсы. Я послушался его совета — попробовал freeCodeCamp. И мне понравилось.

Вкратце, магия freeCodeCamp это:

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

Мои ошибки

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

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

В конце концов я понял горькую правду: эти нерешенные проблемы не исчезнут, а будут только возвращаться, чтобы кусать меня. Мне нужно было научиться «ползать» прежде, чем «учиться» ходить. Реальность в том, что лучше иметь один завершенный проект, чем тысячи неготовых.

Еще одна ошибка — я постоянно терял ФОКУС. Когда я искал решение своей проблемы, я мог заинтересоваться какой-то другой темой. А потом еще одной и еще одной… и прежде чем я успевал опомниться, день подходил к концу, а моя изначальная проблема оставалась нерешенной. Годом позже я услышал, как Пабло Альмуние описывал этот феномен как «охоту за белками». Вспоминайте эту сцену каждый раз, когда отвлекаетесь:

Следующий шаг

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

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

Тогда я встретил разработчика по имени Улис Гаскон. Он посоветовал закончить проекты, которые я начал. Он даже предложил помощь. Поэтому, я вернулся к «легким» вещам, которые забросил. Сделав это, я начал задавать новые вопросы и сталкиваться с новыми вызовами.

Советы Гаскона и Эрнандеса сыграли ключевую роль в моем обучении. Поэтому, я с уверенностью могу сказать, что следующим шагом для меня стало то, что я попросил помощи у друзей.

Вхождение в сообщество

Мое обучение ускорялось. Я заканчивал все больше проектов и хотел строить новые, но понятия не имел над чем поработать.

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

Я же хотел создавать вещи с профессионалами всех уровней, но подходящего сообщества не было. Именно в этот момент я начал размышлять как настоящий программист. Если у тебя нет подходящего инструмента… сделай его сам! Поэтому я поговорил с друзьями, помогавшими мне ранее, и вместе мы основали Open Source Weekends.

После долгих недель тяжелой работы, спонсоров, которые отказывались от участия в последний момент, множества вложенных времени и денег, мы смогли сделать наш первый ивент. Спустя семь месяцев Open Source Weekends выиграл Open Award за лучшее открытое сообщество. В итоге, участие в жизни сообщества стало для меня критичным шагом. Это помогло мне улучшить социальные навыки и стать не просто «парнем, который учиться программировать».

Автор текста (третий слева) с наградой Open Award

Первая работа

После 11 месяцев обучения программированию, множества провалов и все новых попыток, основания opensource-комьюнити, я получил письмо через рассылку нашего Open Source Weekends.

Компания Kubide искала backend-разработчика. Я решил сфокусироваться именно на back-end всего пару месяцев назад. Мне не слишком понравилась frontend-разработка, но я с удовольствием проводил часы за решением проблем в back-end. Kubide прислали мне тестовое задание: клон Twitter API. Это было сложно, но не катастрофически.

Затем я попал на собеседование с СЕО компании. Его больше интересовали мои навыки в обучении, чем успехи в разработке. Он также хотел нанять человека, с которым познакомился через сферу стартапов, а я для него ассоциировался с Campus Madrid (хоть я и был просто охранником).

Спустя три дня после встречи я получил звонок с предложением работы. Не могу передать свои чувства в тот момент. Все вокруг были так рады за меня. Наконец вся тяжелая работа, которую я вложил в обучение, была вознаграждена.

Мои первые впечатления от работы

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

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

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

Однажды вы увидите кого-то, решающего проблему, на которую вам требовался день, за минуту. Вы снова почувствуете себя изгоем. Но через некоторое время вы сами поможете кому-то решить проблему и снова почувствуете себя лучше. В конце концов, вы поймете сами или вам скажет руководитель, что вас наняли не за навыки, которые у вас были. Вас взяли на работу за навыки, которым вы могли научиться. Потому что лучшая стратегия — нанимать людей, которые умеют учиться и страстно хотят этого.

«Самое опасное препятствие — вы сами», — Анхель Луис Кесада.

Как только вы поймете это, вы почувствуете себя лучше. Вы станете быстрее развиваться. Вы будете расти вместе с партнерами. Вы будете любить работу сильнее и сильнее и вы будете счастливы.

Автор в компании счастливых коллег

В заключение:

  • Помните, зачем вы начали учиться программированию.
  • Не гоняйтесь за белками.
  • Заканчивайте проекты.
  • Будьте упорны.

az network lb rule | Microsoft Docs

Управление правилами балансировки нагрузки.

В этой статье

Команды

az network lb rule create

Создайте правило балансировки нагрузки.

az network lb rule create --backend-port
                          --frontend-port
                          --lb-name
                          --name
                          --protocol {All, Tcp, Udp}
                          --resource-group
                          [--backend-pool-name]
                          [--backend-pools-name]
                          [--disable-outbound-snat {false, true}]
                          [--enable-tcp-reset {false, true}]
                          [--floating-ip {false, true}]
                          [--frontend-ip-name]
                          [--idle-timeout]
                          [--load-distribution {Default, SourceIP, SourceIPProtocol}]
                          [--probe-name]
                          [--subscription]

Примеры

Создайте правило балансировки нагрузки, которое назначает интерфейсную IP-конфигурацию и порт для пула адресов и порта.

az network lb rule create -g MyResourceGroup --lb-name MyLb -n MyLbRule --protocol Tcp \
    --frontend-ip-name MyFrontEndIp --frontend-port 80 \
    --backend-pool-name MyAddressPool --backend-port 80

Создайте правило балансировки нагрузки, которое назначает интерфейсную IP-конфигурацию и порт для пула адресов и порта с помощью функции плавающего IP-адреса.

az network lb rule create -g MyResourceGroup --lb-name MyLb -n MyLbRule --protocol Tcp \
    --frontend-ip-name MyFrontEndIp --backend-pool-name MyAddressPool  \
    --floating-ip true --frontend-port 80 --backend-port 80

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

az network lb rule create -g MyResourceGroup --lb-name MyLb -n MyHAPortsRule \
    --protocol All --frontend-port 0 --backend-port 0 --frontend-ip-name MyFrontendIp \
    --backend-pool-name MyAddressPool

Обязательные параметры

—backend-port

—frontend-port

—lb-name

Имя подсистемы балансировки нагрузки.

—name -n

Имя правила балансировки нагрузки.

—protocol

Протокол сетевого транспорта.

допустимые значения: All, Tcp, Udp

—resource-group -g

Имя группы ресурсов. Вы можете настроить расположение по умолчанию с помощью az configure --defaults group=<name>.

Необязательные параметры

—backend-pool-name

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

—backend-pools-name

Список имен серверного пула адресов.

—disable-outbound-snat

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

допустимые значения: false, true

—enable-tcp-reset

Получение двунаправленного сброса TCP на время ожидания простоя потока TCP или непредвиденное завершение соединения. Используется, только если для параметра Протокол задано значение TCP.

допустимые значения: false, true

—floating-ip

Включить плавающий IP-адрес.

допустимые значения: false, true

—frontend-ip-name

Имя внешней IP-конфигурации. Если существует только один параметр, не используйте его в качестве значения по умолчанию.

—idle-timeout

Время ожидания простоя в минутах.

—load-distribution

Параметры правил сходства.

допустимые значения: Default, SourceIP, SourceIPProtocol

значение по умолчанию: default

—probe-name

Имя существующей проверки, связываемой с этим правилом.

—subscription

Имя или идентификатор подписки Вы можете настроить подписку по умолчанию с помощью az account set -s NAME_OR_ID .

Глобальные параметры

—debug

Повышение уровня детализации журнала для включения всех журналов отладки.

—help -h

Отображение этого справочного сообщения и выход.

—only-show-errors

Показывать только ошибки, блокируя предупреждения.

—output -o

Формат вывода.

—query

Строка запроса JMESPath. Дополнительные сведения и примеры см. в разделе http://jmespath.org/.

—verbose

Повышение уровня детализации журнала. Чтобы включить полные журналы отладки, используйте параметр —debug.

az network lb rule delete

Удаление правила балансировки нагрузки.

az network lb rule delete --lb-name
                          --name
                          --resource-group
                          [--subscription]

Примеры

Удаление правила балансировки нагрузки.

az network lb rule delete -g MyResourceGroup --lb-name MyLb -n MyLbRule

Обязательные параметры

—lb-name

Имя подсистемы балансировки нагрузки.

—name -n

Имя правила балансировки нагрузки.

—resource-group -g

Имя группы ресурсов. Вы можете настроить расположение по умолчанию с помощью az configure --defaults group=<name>.

Необязательные параметры

—subscription

Имя или идентификатор подписки Вы можете настроить подписку по умолчанию с помощью az account set -s NAME_OR_ID .

Глобальные параметры

—debug

Повышение уровня детализации журнала для включения всех журналов отладки.

—help -h

Отображение этого справочного сообщения и выход.

—only-show-errors

Показывать только ошибки, блокируя предупреждения.

—output -o

Формат вывода.

—query

Строка запроса JMESPath. Дополнительные сведения и примеры см. в разделе http://jmespath.org/.

—verbose

Повышение уровня детализации журнала. Чтобы включить полные журналы отладки, используйте параметр —debug.

Перечисление правил балансировки нагрузки.

az network lb rule list --lb-name
                        --resource-group
                        [--query-examples]
                        [--subscription]

Примеры

Перечисление правил балансировки нагрузки.

az network lb rule list -g MyResourceGroup --lb-name MyLb -o table

Обязательные параметры

—lb-name

Имя подсистемы балансировки нагрузки.

—resource-group -g

Имя группы ресурсов. Вы можете настроить расположение по умолчанию с помощью az configure --defaults group=<name>.

Необязательные параметры

—query-examples

Рекомендуемая строка JMESPath. Можно скопировать один из запросов и вставить его после параметра—query в двойных кавычках, чтобы увидеть результаты. Можно добавить одно или несколько позиций ключевых слов, чтобы мы могли предоставлять предложения на основе этих ключевых слов.

—subscription

Имя или идентификатор подписки Вы можете настроить подписку по умолчанию с помощью az account set -s NAME_OR_ID .

Глобальные параметры

—debug

Повышение уровня детализации журнала для включения всех журналов отладки.

—help -h

Отображение этого справочного сообщения и выход.

—only-show-errors

Показывать только ошибки, блокируя предупреждения.

—output -o

Формат вывода.

—query

Строка запроса JMESPath. Дополнительные сведения и примеры см. в разделе http://jmespath.org/.

—verbose

Повышение уровня детализации журнала. Чтобы включить полные журналы отладки, используйте параметр —debug.

Получение сведений о правиле балансировки нагрузки.

az network lb rule show --lb-name
                        --name
                        --resource-group
                        [--query-examples]
                        [--subscription]

Примеры

Получение сведений о правиле балансировки нагрузки.

az network lb rule show -g MyResourceGroup --lb-name MyLb -n MyLbRule

Обязательные параметры

—lb-name

Имя подсистемы балансировки нагрузки.

—name -n

Имя правила балансировки нагрузки.

—resource-group -g

Имя группы ресурсов. Вы можете настроить расположение по умолчанию с помощью az configure --defaults group=<name>.

Необязательные параметры

—query-examples

Рекомендуемая строка JMESPath. Можно скопировать один из запросов и вставить его после параметра—query в двойных кавычках, чтобы увидеть результаты. Можно добавить одно или несколько позиций ключевых слов, чтобы мы могли предоставлять предложения на основе этих ключевых слов.

—subscription

Имя или идентификатор подписки Вы можете настроить подписку по умолчанию с помощью az account set -s NAME_OR_ID .

Глобальные параметры

—debug

Повышение уровня детализации журнала для включения всех журналов отладки.

—help -h

Отображение этого справочного сообщения и выход.

—only-show-errors

Показывать только ошибки, блокируя предупреждения.

—output -o

Формат вывода.

—query

Строка запроса JMESPath. Дополнительные сведения и примеры см. в разделе http://jmespath.org/.

—verbose

Повышение уровня детализации журнала. Чтобы включить полные журналы отладки, используйте параметр —debug.

az network lb rule update

Обновите правило балансировки нагрузки.

az network lb rule update [--add]
                          [--backend-pool-name]
                          [--backend-pools-name]
                          [--backend-port]
                          [--disable-outbound-snat {false, true}]
                          [--enable-tcp-reset {false, true}]
                          [--floating-ip {false, true}]
                          [--force-string]
                          [--frontend-ip-name]
                          [--frontend-port]
                          [--idle-timeout]
                          [--ids]
                          [--lb-name]
                          [--load-distribution {Default, SourceIP, SourceIPProtocol}]
                          [--name]
                          [--probe-name]
                          [--protocol {All, Tcp, Udp}]
                          [--remove]
                          [--resource-group]
                          [--set]
                          [--subscription]

Примеры

Обновите правило балансировки нагрузки, чтобы изменить протокол на UDP.

az network lb rule update -g MyResourceGroup --lb-name MyLb -n MyLbRule --protocol Udp

Обновите правило балансировки нагрузки для поддержки портов высокой доступности.

az network lb rule update -g MyResourceGroup --lb-name MyLb -n MyLbRule \ --protocol All --frontend-port 0 --backend-port 0

Обновите правило балансировки нагрузки. автоматически сформированный

az network lb rule update --disable-outbound-snat true --lb-name MyLb --name MyLbRule --resource-group MyResourceGroup

Обновите правило балансировки нагрузки. автоматически сформированный

az network lb rule update --idle-timeout 5 --lb-name MyLb --name MyLbRule --resource-group MyResourceGroup

Необязательные параметры

—add

Добавьте объект в список объектов, указав путь и пары «ключ-значение». Пример:—добавить свойство. Листпроперти <ключ = значение, строка или строка JSON>.

—backend-pool-name

Имя серверного пула адресов.

—backend-pools-name

Список имен серверного пула адресов.

—backend-port

—disable-outbound-snat

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

допустимые значения: false, true

—enable-tcp-reset

Получение двунаправленного сброса TCP на время ожидания простоя потока TCP или непредвиденное завершение соединения. Используется, только если для параметра Протокол задано значение TCP.

допустимые значения: false, true

—floating-ip

Включить плавающий IP-адрес.

допустимые значения: false, true

—force-string

При использовании «Set» или «Add» сохраните строковые литералы вместо того, чтобы пытаться преобразовать их в JSON.

—frontend-ip-name

Имя внешней IP-конфигурации.

—frontend-port

—idle-timeout

Время ожидания простоя в минутах.

—ids

Один или несколько идентификаторов ресурсов (с разделителями-пробелами). Это должен быть полный идентификатор ресурса, содержащий все сведения об аргументах «Resource id». Необходимо указать идентификаторы или другие аргументы «Resource id».

—lb-name

Имя подсистемы балансировки нагрузки.

—load-distribution

Параметры правил сходства.

допустимые значения: Default, SourceIP, SourceIPProtocol

значение по умолчанию: default

—name -n

Имя правила балансировки нагрузки.

—probe-name

Имя существующей проверки, связываемой с этим правилом.

—protocol

Протокол сетевого транспорта.

допустимые значения: All, Tcp, Udp

—remove

Удаление свойства или элемента из списка. Пример:—Remove Property. List или—Remove пропертиторемове.

—resource-group -g

Имя группы ресурсов. Вы можете настроить расположение по умолчанию с помощью az configure --defaults group=<name>.

—set

Обновите объект, указав путь к свойству и значение, которое необходимо задать. Пример:—Set свойство1. свойство2 =.

—subscription

Имя или идентификатор подписки Вы можете настроить подписку по умолчанию с помощью az account set -s NAME_OR_ID .

Глобальные параметры

—debug

Повышение уровня детализации журнала для включения всех журналов отладки.

—help -h

Отображение этого справочного сообщения и выход.

—only-show-errors

Показывать только ошибки, блокируя предупреждения.

—output -o

Формат вывода.

—query

Строка запроса JMESPath. Дополнительные сведения и примеры см. в разделе http://jmespath.org/.

—verbose

Повышение уровня детализации журнала. Чтобы включить полные журналы отладки, используйте параметр —debug.



Приглашаем Вас на работу в Angry

Молодая и очень активно развивающаяся компания Angry занимается сбором, анализом и обработкой обращений клиентов из открытых источников, социальных сетей, СМИ и блогосферы. Нам требуются специалисты по машинному обучению на очень, очень и очень интересные
задачи по созданию крутого продукта!


Какие задачи нужно будет решать:

  • эмоциональная оценка коротких и длинных текстов, написанных с использование сленга и специфичной лексики (sentiment analysis)
  • классификация текстов по категориям (конечное множество)
  • выделение в тексте различных данных
  • в режиме реального времени в потоке текстовых сообщений находить популярные тренды (темы)
  • специфичный антиспам (определять релевантность текста заданной теме)

Необходимые навыки и знания:

  • системы машинного обучения включая распознавание, классификацию, выделение признаков, нейросети, обучение без учителя
  • обработка естественного языка, компьютерная лингвистика, синтаксический анализ текстов на русском языке
  • Python, scikit-learn и другие библиотеки для машинного обучения

Что мы предлагаем:

  • работа над нетривиальным проектом
  • офис 5 минут от м. Шаболовская
  • оформление по ТК РФ, белая заработная плата
  • дружный коллектив
  • возможность развиваться профессионально, получая бесценный опыт.

Ключевые навыки:

Машинное обучениеPythonBig Data

Разработчик front-end


Компания «Перспективный мониторинг» 10 лет работает на рынке информационной безопасности. Мы активно развиваем продуктовое направление и ищем фронтендера в команду разработки киберполигона Ampire.


Благодаря учебно-тренировочной платформе Ampire сотрудники служб информационной безопасности и ИТ-служб могут в изолированной среде отработать навыки выявления и противодействия хакерским атакам, не переживая за сервисы на проде.


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


Вся инфраструктура, которую моделирует Ampire, очень динамична. Нужно будет сделать интерактивную страницу с наглядным отображением состояния инфраструктуры, сервисов и возможностей подключиться к серверам или АРМ виртуального предприятия.


Во время киберучений Ampire собирает много статистической информации — кто и какие ресурсы атаковал, логи виртуальных машин и сервисов, действия и оценки обучающихся. Нужно будет сделать так, чтобы со всеми этими данными было удобно работать в веб-интерфейсе и преподавателям, и студентам.


Задачи:


  • проектирование и разработка веб-интерфейсов


  • рефакторинг имеющегося проекта, поддержка и развитие кодовой базы


  • взаимодействие с backend разработчиками


  • наставничество младших разработчиков


Ожидания от квалификации:

  • уверенное знание JavaScript (ES5+), умение писать структурированный и читаемый код
  • уверенные знания HTML, CSS, SCSS
  • знание Vue\React\Angular
  • опыт проектирования клиентской части


Будет плюсом следующий опыт:

  • проектирование UI\UX
  • оптимизация клиентской стороны веб-приложения
  • опыт в роли наставника


Условия работы:

  • Работа в офисе в Технопарке «Отрадное», метро Отрадное.
  • полное соблюдение трудового законодательства РФ, оплачиваемые отпуска и больничные листы, «белая» заработная плата.
  • начало работы с 8.00 до 11.00, продолжительность рабочего дня 8 часов + 1 час на обед.
  • оплачиваемое работодателем питание в офисе или кафе.
  • ДМС (добровольное медицинское страхование) — различные варианты программ, страховка от несчастных случаев.
  • изучение английского в офисе.
  • возможность получения профессиональных сертификатов и прохождения курсов повышения квалификации за счёт компании.

Из сисадминов в бэкенд-программисты. Долгий путь к новой работе

Всем привет! Меня зовут Дмитрий, мне 31 год, и я начинающий бэкенд-разработчик. Я расскажу историю о том, как новые знания меняют цели в карьере, как перейти в новую профессию, когда тебе 31, у тебя есть семья и нет финансовой подушки, чтобы всё бросить и искать новую работу.

Кем работал

Моим первым и единственным местом работы была небольшая компания, занимающаяся обслуживанием оргтехники и локальных сетей. Там я работал на должности системного администратора, где занимался всем — от ремонта компьютеров и ноутбуков, до настройки СУБД и веб-серверов для 1С.

Всё шло хорошо. У нас сложился дружный коллектив, работать было комфортно, я старался развиваться этой сфере. Иногда мне попадались простые задачи, связанные с вебом: подкорректировать главную страницу на сайте клиентов, перенести сайт на другой хостинг, добавить счётчик «Яндекс.Метрики». Временами мой товарищ, который был веб-разработчиком, просил помочь ему с наполнением или вёрсткой. На тот момент я знал немного про вёрстку, заглядывал на сайт Htmlbook, пытался изучать программирование по редким на тот момент курсам или книгам. Самообразование мне давалось туго, я думал, что это просто не моё — и меня это устраивало.

Мое знакомство с веб-разработкой

Через пару лет нашей компании понадобилось переделать сайт. Был давно готов макет, оставалось только найти разработчика и сделать. Мне стало интересно попробовать сверстать настоящий макет самостоятельно — и я взялся. Тем более, что по срокам меня никто не ограничивал. А в качестве «движка» я выбрал CMS, написанную моим знакомым. Прошло немного времени, и всё было готово и размещено на хостинге. Мне понравилось, что я самостоятельно сверстал и запустил сайт, пусть и самый примитивный. Это придало уверенности, что научиться разработке вполне возможно, и я начал подыскивать курсы по программированию. К тому времени их стало немного больше.

Попытки учиться

Первым мне попался сайт Html Academy, где я ознакомился с HTML и CSS. Но интерес к нему быстро остыл, потому что программирование по-прежнему казалось чем-то далеким, а знание HTML/CSS уверенности не придавало. Насколько я помню, на тот момент там ещё не было уроков по программированию.

Следующим ресурсом был Itvdn. Здесь я купил и успешно прошел курс по HTML/CSS, но учиться там дальше не захотел — было сложно высидеть двухчасовой урок, а затем выполнять задание, вспоминая, на какой же минуте видео рассказывалась нужная информация. В общем, такой формат мне не зашел.

Обучение на Хекслете

Примерно в это же время коллега по работе рассказал о неплохих курсах по программированию Hexlet. Заинтересовавшись, я прошел пару бесплатных уроков. Прохождение не вызывало желания закрыть урок, приглянулась подача материала — короткое видео о чем-то конкретном с тестами в конце. Так я прошел бесплатный курс по введению в программирование, и, поняв в процессе, что мне это подходит, оформил подписку. К этому шагу располагала цена в 24$, что было очень даже приемлемо за такой материал. Ещё одним из решающих факторов для меня было то, что здесь учат фундаментальным вещам, которые применимы к другим языкам, а не только к тому, на котором учишься. Одним словом, здесь учат программированию, а не только синтаксису языка. В начале обучения я начал проходить профессию «PHP-разработчик», так как мой кругозор был ограничен тем, что PHP — на бэкенде, а JavaScript — на фронтенде.

Проходил курсы по вечерам, когда засыпал ребенок, и иногда на работе, когда было свободное время. Руководитель был не против, что я занимаюсь саморазвитием в IT.

Трудности

Первым моим камнем преткновения стал курс «PHP: Построение абстракций», такой СИКП на PHP. Вообще, если учишься на Хекслете, то эту аббревиатуру услышишь ещё не раз. Мозг кипел, было много непонятного. В комментариях к урокам встречались сообщения о том, что это очень сложно. Там же отвечали, что этот же материал дан в курсе по JS более понятно, и стоит пройти его там, если не понятно. Так я и сделал.

Читайте и другие истории успеха:

Как благодаря Хекслету я устроился в EPAM, стал сеньором и уехал в США.

Действительно, мне удалось пройти этот курс на JS. В процессе я узнал про применение Javascript на сервере, Node.JS для бэкенда, и решил продолжить изучение этого языка. Тем более, что я был уверен — не важно, какой язык первым изучаешь на Хекслете.

Далее ещё были сложности в обучении — в основном связанные с тем, когда ты что-то не усвоил в одном уроке, подсмотрел решение учителя в практике, ничего не понял и пошёл дальше. Дальше это незнание накапливается, и ты не понимаешь вообще ничего. Это очень деморализовало, и на пару недель обучение забрасывалось. Позже я научился с этим справляться — заново прошёл все курсы и закрыл пробелы в знаниях, старался не подсматривать решения, а разбираться с задачей самому, иногда возвращаясь к предыдущим темам.

Продолжительность обучения

В моём обучении был большой перерыв в 3-4 месяца, связанный с бытовыми делами, и пара небольших, связанных как раз с неумением справиться со сложностью. В итоге прохождение профессии без проектов заняло у меня примерно полтора года.

Проекты я начал проходить после пары собеседований, когда понял, что в них смогу получить начальный опыт. Прохождение проектов заняло больше года. Первые три шли по плану — по одному проект в месяц. Четвертый затянулся почти на год в связи болезнями, рабочими проблемами во время пандемии и устройством на новую работу. Сейчас, когда на работе всё более менее устаканилось, я возобновил работу над проектом и надеюсь в скором времени его сдать.

Поиск работы

К окончанию обучения мои планы насчет работы изменились — я уже не собирался подрабатывать «допиливанием» сайтов на вордпресс, а хотел работать в команде разработчиков, выполнять сложные и интересные задачи. У моего начальства не было планов по организации отдела разработки, а заниматься этим самостоятельно казалось невозможным — не было знаний и практики в построении приложений, в настройке необходимых для этого библиотек и фреймворков. Кроме того, мне надоело заниматься тем, чем я занимался на работе, и захотелось сменить деятельность. В общем, я приступил к поиску новой работы, сделал резюме и начал его рассылать.

В Краснодаре найти оплачиваемую работу новичку оказалось достаточно сложно, везде требуется опыт работы, в том числе «в коммерческой разработке». Но я отправлял резюме даже в компании с такими требованиями. За время поисков я прошёл четыре собеседования и выполнил одно тестовое задание.

Первая попытка

Первое собеседование состоялось у одного зеленого мобильного оператора. В вакансии указывалось, что требуется «Джуниор JavaScript-разработчик». Тестового задания не было. На собеседовании были пара ребят из местного офиса, HR и разработчик из другого города на удалёнке. По технической части вопросов было мало, в основном про жизнь, почему меняешь работу и так далее. Затем объяснили, чем придется заниматься: это оказалась техподдержка в B2B-кабинете, в которой программирование занимало 10%, и то в какой-то своей системе. После собеседования сказали, что перезвонят, но мне уже самому не хотелось, чтобы перезванивали.

Вторая попытка

Знакомый разработчик скинул мне контакты руководителя местной IT-компании, занимающейся разного рода разработкой. Я связался, меня пригласили пообщаться. Там требовались разработчики по двум направлениям: бэкенд на PHP и фронтенд. В PHP я практически не разбирался, с фронтом было получше, но все же недостаточно, чтобы начать работать. Вообще получилась интересная ситуация — JS-разработчик, который не умеет во фронтенд. После беседы предложили месяц неоплачиваемой стажировки, за время которой я должен был подтянуть HTML/CSS и прочие дела, которые происходят во фронтенде, а после стажировки зарплату раза в два меньше моих ожиданий. Надо заметить, что мои зарплатные ожидания были немного завышены, и связано это было с необходимостью получать определенный минимум для содержания семьи. Поэтому я поблагодарил ребят за предложение и продолжил свои поиски.

После второй попытки стало понятно, что мне не хватает практического опыта, поэтому я решил пройти проекты на Хекслете.

Третья попытка

Ещё одна попытка попасть во фронтенд. Это была местная компания, которая занимается разработкой пары популярных медицинских сайтов с рейтингами докторов и клиник. Отправил им резюме, получил тестовое задание. Требовалось сделать страницу, где нужно делать запросы к тестовому API, получать данные и изображения, и выводить их в определенном порядке. Из библиотек предлагалось использовать JQuery. Задание оказалось несложным, и я выполнил его без особых проблем. После него меня пригласили на собеседование.

На собеседовании присутствовали фронтенд-разработчик и кто-то из технического руководства. Разработчик давал задачи на программирование, а руководитель спрашивал «за жизнь». С задачами на алгоритмы я справился без проблем, с вопросами по верстке немного посыпался.

Через несколько дней HR мне ответила, что они не готовы взять меня на работу. Возможно, не стоило на собеседовании на фронтенд спрашивать: «Нельзя ли в дальнейшем перейти в бэкенд?».

Четвертая попытка

HR следующей компании сама меня нашла на hh.ru и предложила прийти на собеседование. Компания занимается рекламой в интернете и в соцсетях, у неё есть большой отдел разработчиков. В Краснодаре как раз открывался филиал, куда меня и пригласили.

Тестового задания не было — они обратили внимание на мой недоделанный четвертый проект Хекслета, который лежал на Github. На собеседовании общался с бекенд-разработчиком. Были вопросы по работе NodeJS, асинхронности, SQL. На вопросах про SQL я начал немного «плавать», потому что прошло много времени после прохождения курса, а я не подумал повторить. Затем открыли мой четвертый проект и по нему я быстро рассказал, что там происходит.

После моего провала с SQL, я подумал, что со мной вежливо попрощаются, но интервьюер пообщался с руководством, и мне сразу предложили пройти месяц оплачиваемой стажировки. Я согласился. Когда выходил из офиса, то не мог поверить, что меня берут на работу бэкенд-разработчиком на стек, который я знаю. Мое удивление было связано с тем, что я выбрал не слишком популярное направление для Краснодара. Куда проще было устроиться «PHP разработчиком на Битрикс».

Как работается

Через две недели после собеседования я вышел на новую работу, получил достаточно мощный компьютер с парой мониторов. Я сам поставил на него привычный дистрибутив линукс и начал осваиваться на рабочем месте. Меня практически сразу отправили «в бой» — поручили работать с одним из партнеров компании над поддержкой и доработкой внутреннего сервиса средних размеров. Сначала я довольно сильно переживал, что на меня свалилась такая большая ответственность, но коллектив оказывал моральную поддержку. Бороться с переживаниями помогли рассуждения Кирилла на одном из вебинаров по поводу софт-скиллов — о том, что нужно не бояться задавать вопросы, но при этом сначала пробовать делать что-то самому. Также помогал метод «выяснить, что делать дальше» из популярной книги по GTD.

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

Планы

В первую очередь — доделать четвертый проект, выполнение которого я затянул. Понемногу осваиваю Python, чтобы не зацикливаться на JS. Интересный и очевидный факт — второй язык на Хекслете осваивать в разы проще. Это говорит о фундаментальных знаниях, которые получаешь здесь. Также хочется поучаствовать в опенсорс-проектах Хекслета. Вообще, много планов про изучение языков или технологий, ребята на Хекслете вдохновляют меня своими статьями и вебинарами, за что им огромное спасибо!

Выводы

— Изучать можно любой язык, который нравится. Или пробовать несколько, а потом выбирать. На Хекслете всему учат хорошо.

— Умение правильно задавать вопросы — это навык, который вы тоже здесь получите, поэтому не замыкайтесь с нерешенными задачами и мыслью «это не мое, это слишком сложно для меня».

— Проекты — это важно, не экономьте на этом.

Ещё

Хочется отметить крутую атмосферу, которая царит вокруг Хекслета:

  • Полезные обсуждения в слаке — от программирования до трудоустройства;
  • Помощь с обучением в том же слаке или в обсуждениях к заданиям;
  • Вдохновляющие вебинары на Youtube. Я не знаю как, но у Кирилла получается мотивировать, просто рассказывая «что нового на Хекслете»;
  • Шикарные статьи и гайды по разным технологиям и языкам программирования.

Логика фильтров должна быть на frontend или backend?

Я создаю веб-приложение

Frontend-reactjs и backend-java.

Frontend и backend общаются друг с другом через rest.

В пользовательском интерфейсе я показываю список элементов. И мне нужно отфильтровать их по некоторым параметрам.

Вариант 1: логика фильтра находится на переднем конце

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

Плюсы : для этого мне не нужно отправлять данные в бэк-энд и ждать ответа. Скорость обновления списка должна быть быстрее.

Минусы: Если мне понадобится несколько фронтенд-клиентов. Скажем, мобильное приложение. Чем мне нужно снова создавать фильтры в этом приложении.

Вариант 2: логика фильтра находится на заднем конце

В этом случае я получаю все элементы списка при загрузке приложения. После того, как пользователь изменит параметры фильтра, мне нужно отправить запрос get с параметрами фильтров и дождаться ответа.
После этого обновите список элементов на UI.

Плюсы: логика фильтра пишется только один раз.

Минусы: скорость, вероятно, будет намного медленнее. Потому что требуется время, чтобы отправить запрос и получить результат обратно.

Вопрос: где должна быть логика фильтра? В frontend или в backend? Или, может быть, что является лучшей практикой?

java

reactjs

rest

Поделиться

Источник


Alex Belke    

15 сентября 2018 в 16:32

4 ответа


  • Вызовите BackEnd-метод из FrontEnd, написанный в бэкэнде

    это немного сложно объяснить, но я надеюсь, что смогу прояснить ситуацию: В моем BackEnd у меня есть StringBuilder, в котором я пишу HTML-код для FrontEnd и назначаю его Div.InnerHTML, чтобы показать. Теперь в этом коде у меня должна быть возможность вызвать BackEnd-метод, как я обычно делаю с…

  • FrontEnd или шифрование BackEnd?

    Я создаю систему, требующую пароля. Мой вопрос в том, должен ли я зашифровать его frontend или backend? Если я сделаю это frontend (вероятно, с javascript), то каждый сможет взломать шифрование = большая проблема безопасности. Но если я сделаю это бэкендом, то простой пароль должен быть каким-то…



28

Фильтр и ограничение на заднем конце. Если бы у вас был миллион записей и сто тысяч пользователей, пытающихся получить доступ к этим записям одновременно, вы действительно хотели бы отправить миллион записей пользователю EVERY? Это убило бы ваш сервер и пользовательский опыт (ожидание распространения миллиона записей с заднего конца для каждого пользователя AND, а затем распространение на переднем конце заняло бы целую вечность по сравнению с простым получением 20-100 записей, а затем нажатием кнопки (разбиение на страницы) для получения следующего 20-100). Кроме того, чтобы отфильтровать миллион записей на интерфейсе, опять же, потребуется очень много времени и, в конечном счете, не очень практично.

С точки зрения реального мира, большинство веб-сайтов имеют какой-то лимит записей: Ebay = 50-200 записи, Amazon = ~20, Цель = ~20… и т. Д. Это обеспечивает быстрые ответы сервера и плавный пользовательский интерфейс для каждого пользователя.

Поделиться


Matt Carlotta    

15 сентября 2018 в 16:49



11

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

В случае, если у вас меньше данных, вы можете выполнить логику фильтрации на переднем конце после получения данных.

Давайте разберемся в этом на примере.
Предположим, у вас есть сущность, имеющая 1 000 000 записей, и вы хотите показать ее в сетке.
В этом случае лучше получить 10 записей о каждом вызове и показать их в сетке.
Если вы хотите выполнить какую-либо операцию фильтрации, лучше сделать запрос к БД на бэкэнде и получить результаты

В случае, если у вас есть только 1000 записей в вашей сущности, будет полезно получить все данные и выполнить все операции фильтрации на интерфейсе.

Поделиться


Sagar Chaudhary    

15 сентября 2018 в 16:52



8

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

Учитывая, что вам в первую очередь нужна фильтрация, я предполагаю, что у вас достаточно данных, чтобы их можно было просматривать. В этом случае вам необходимо иметь фильтрацию на внутреннем сервере.

Допустим, у вас размер страницы 20. После применения фильтра вы ожидаете, что в UI появится страница из 20 сущностей, соответствующих этим конкретным критериям фильтрации. Этого невозможно достичь, если вы извлекаете 20 объектов, сохраняете их в интерфейсе и затем применяете к ним фильтр.

Кроме того, если у вас достаточно данных, извлечение всех из них в интерфейсе будет невозможно из-за ограничений памяти.

Поделиться


Unknown    

15 сентября 2018 в 16:49


  • Frontend и Backend с express

    В настоящее время я работаю над веб-приложением с express.js. Я хочу иметь интерфейс и бэкэнд. Интерфейс должен показывать некоторое содержимое из базы данных, в бэкэнде я хочу создать это содержимое (аналогично cms). Я начал с этой структуры папок: app/ ├── frontend/ │ ├── public //Javascript,…

  • разделение symfony на frontend и backend серверы

    В настоящее время у меня есть приложение symfony, которое работает на одном сервере (frontend, backend, database) Теперь мне нужно реализовать это приложение на крупном клиенте, но он не позволяет мне подключиться к базе данных из DMZ. Мне нужно реализовать сервер внутри сети, который будет…



3

Скорее всего, начните с внешнего интерфейса (если только вы не имеете дело с огромными объемами данных):

  1. Реализуйте фильтрацию на интерфейсе (если только по какой-то причине это не проще сделать на бэкэнде, что я нахожу маловероятным).
  2. Повторяйте до тех пор, пока функциональность фильтрации не станет несколько стабильной.
  3. Проанализируйте свой трафик, посмотрите, есть ли смысл вкладывать усилия в реализацию внутренней фильтрации. Посмотрите, какой процент запросов на самом деле фильтруется и какую экономию вы получите от внутренней фильтрации.
  4. Реализовать (или нет) бэкенд-фильтрацию в зависимости от результатов #3.

Как личное замечание, принятый ответ-ужасный совет:

  • «Если бы у вас был миллион записей и сто тысяч пользователей, пытающихся получить доступ к этим записям одновременно»; ничто не заставляет сто тысяч пользователей использовать фильтрацию, ваша система должна быть в состоянии справиться с этим сценарием конца света. Бэкэнд-фильтрация должна быть просто оптимизацией, а не решением.
  • как только вы сделаете фильтрацию на бэкэнде, вы, вероятно, захотите также сделать разбиение на страницы; это не тривиальная функция, если вы хотите получить последовательные результаты.
  • выполнение бэкэнд-фильтрации, скорее всего, станет намного сложнее, чем просто фронтэнд-фильтрация; вы должны знать, что потратите значительное количество времени (не только на первоначальную реализацию, но и на текущее обслуживание), и спросить себя, не является ли это преждевременной оптимизацией.

TL/DR: делайте то, что вам проще, и не беспокойтесь об этом, пока не появится смысл начать оптимизацию.

Поделиться


Tom    

08 декабря 2019 в 11:20


Похожие вопросы:

Как создать файл cookie, с помощью frontend или backend?

Насколько я понимаю, cookie-это какой-то способ сделать наше веб-приложение статусным. Поскольку файлы cookie могут быть созданы как в javascript (frontend), так и из http response (by backend),…

Magento запрос-Frontend, Backend или API?

Хорошо — я прочитал и мне нравится обсуждение Magento frontend/backend и согласен с ответом Бена в запросе Magento-Frontend или Backend? А теперь давайте поднимемся на ступеньку выше. У нас есть…

Архитектура Frontend

На данный момент у нас есть очень тяжелый frontend (frontend+backend в одном приложении на самом деле). Frontend содержит всю логику: UI, бизнес-логику, логику персистентности и так далее. Это очень…

Вызовите BackEnd-метод из FrontEnd, написанный в бэкэнде

это немного сложно объяснить, но я надеюсь, что смогу прояснить ситуацию: В моем BackEnd у меня есть StringBuilder, в котором я пишу HTML-код для FrontEnd и назначаю его Div.InnerHTML, чтобы…

FrontEnd или шифрование BackEnd?

Я создаю систему, требующую пароля. Мой вопрос в том, должен ли я зашифровать его frontend или backend? Если я сделаю это frontend (вероятно, с javascript), то каждый сможет взломать шифрование =…

Frontend и Backend с express

В настоящее время я работаю над веб-приложением с express.js. Я хочу иметь интерфейс и бэкэнд. Интерфейс должен показывать некоторое содержимое из базы данных, в бэкэнде я хочу создать это…

разделение symfony на frontend и backend серверы

В настоящее время у меня есть приложение symfony, которое работает на одном сервере (frontend, backend, database) Теперь мне нужно реализовать это приложение на крупном клиенте, но он не позволяет…

Typescript сборка интерфейса и бэкенда с общей папкой

Я подумываю о переходе на typescript, но борюсь с тем, как сделать общую папку между интерфейсом и бэкендом? Я придумал эту структуру проекта: frontend — src — server.ts — functions — dist -…

Масштабирование конструирование с frontend и backend экземпляров

Я разрабатываю серию микросервисов с использованием Spring Boot и планирую развернуть их на Kubernetes. Некоторые микросервисы состоят из API, который записывает сообщения в очередь Кафки, и…

Reactify Django или разделенные frontend и backend

Я хотел бы знать, почему sould reactify Django или почему мы должны использовать разделенный django backend и react frontend. какая реализация более масштабируема Я надеюсь, что вы могли бы мне…

HighLoad Junior Блог

Андрей Смирнов


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

Какую часть я называю веб-сервисом, бэкендом, application-сервером? В классической архитектуре это то, что стоит за http rеverse proxy или load-балансировщиком, а с другой стороны у него находятся БД, memcashed и др. Вот только об этом бэкенде и будет идти речь.

Чем занят бэкенд?


Если посмотреть на соотношение скорости процессора и возможности сетевых соединений, то отличия – на пару порядков. Например, на этом слайде сжатие 1 Кб
данных занимает 3 мкс, в то время как round trip в одну сторону даже внутри одного дата-центра – это уже 0,5 мс. Любое сетевое взаимодействие, которое
нужно бэкенду (например, отправка запроса в БД), потребует, как минимум 2х round trip-ов и по сравнению с тем процессорным временем, которое он тратит на
обработку данных, – это совершенно незначительно. Большую часть времени обработки запроса бэкенд ничего не делает, ждет. Почему он ждет? Существенную часть
относительно сложной работы берет на себя rеverse proxy или load-балансировщик, который стоит перед ним. Это и буферизация запросов и ответов, и валидация
http, «борьба» с медленными клиентами, шифрование https. До бэкенда доходит чисто http-валидный запрос, уже буферизованный, буквально в паре tcp-пакетов.
Ответ также proxy готов буферизовать за бэкенд, ему нет необходимости этим заниматься.


Бэкенд – один из самых больших бездельников в веб-архитектуре. У него есть всего 2 задачи:

  1. сетевой ввод-вывод – это общение с одной стороны с proxy – прием http-запроса и ответ на него, а с другой стороны общение со всевозможными сервисами,
    которые хранят данные – это могут быть БД, очереди, memcaсhed и т.п.
  2. склеивание строк – стерилизовать данные в JSON, сформировать шаблон на основе html, посчитать sh2 или md5? выполнить сжатие данных.


А что такое бизнес-логика в бэкенде? Это проверки наподобие «если значение переменных больше 3-х, делай это», «если пользователь авторизован, покажи одно,
если не авторизован – покажи другое». Бывают, конечно, отдельные задачи, например, по изменению размера картинки, переконвертации видео, но чаще всего
такие задачи решаются вне бэкенда с использованием очередей, воркеров и т.д.


Параллелизм запросов


Если мы говорим о бэкенде, а его производительность во многом будет определять производительность в целом нашего продукта, то у нас может быть 2 цели по
оптимизации:

  1. заставить его «переваривать» все большее количество запросов в секунду, т.е. увеличить его производительность,
  2. вторая цель – продуктовая – это уменьшение времени отклика, т.е., чтобы каждый запрос выполнялся намного быстрее, для пользователя результат появлялся
    быстрее и т.п.


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


С другой стороны, если мы хотим оптимизировать время отклика, то что влияет на время отклика? Это то, чем занимается бэкенд – склеиванием строк и сетевым
вводом-выводом. Сетевой ввод-вывод занимает на порядок больше времени, поэтому нужно оптимизировать его. Для этого можно все время ожидания распараллелить
– отправить все запросы одновременно, дождаться всех ответов, сформировать блок для клиента и отправить обратно. Тем самым значительно сокращается время
отклика, если, конечно, бизнес-логика нам позволяет какие-то запросы отправлять одновременно.


Сетевой ввод-вывод


Начнем с сетевого ввода-вывода. Существует 3 варианта организации ввода-вывода: блокирующийся, неблокирующийся и асинхронный. Последний с сетевыми не
работает, остается 2 варианта – блокирующийся, неблокирующийся.


Рассмотрим их на примере API-сокетов, BSD-сокетов, которые есть в UNIX-e, в Windows все то же самое – будут по-другому называться вызовы, но логика та же
самая. Как выглядит API низкоуровневый для работы с tcp-сокетом? Это некий набор вызовов. Если мы говорим о сервере, то он должен создать сокет, должен его
забиндить к некоему адресу, на котором он слушает, сделать listen и сообщить об ожидании входящих соединений. Далее есть вызов accept, который отдает нам
новый сокет, новое соединение с конкретным клиентом, в рамках этого соединения мы можем писать и читать данные из этого сокета, т.е. получать запрос,
отправлять ответ и, в конце концов, мы этот сокет закрываем.


Если у нас ввод-вывод блокирующийся, то большинство важных операций заблокируется до тех пор, пока не появятся данные, новые соединения, или до тех пор,
пока не будет свободен для записи системный сетевой буфер. Наш поток исполнения будет ждать окончания выполнения какой-то операции. Из этого следует
простейший вывод: в рамках одного потока мы не можем обслуживать более одного соединения.


С другой стороны, этот вариант самый простой с точки зрения разработки.


Но существует второй вариант – неблокирующийся ввод-вывод. На поверхности отличия элементарны – вместо того, чтобы заблокироваться, любая операция
завершается немедленно. Если данные не готовы, возвращается специальный код ошибки, по которому понятно, что следует попробовать вызов позднее. При таком
варианте мы можем из одного потока выполнять несколько сетевых операций одновременно. Но, т.к. неизвестно, готов ли сокет к вводу-выводу, пришлось бы
обращаться к каждому сокету по очереди с соответствующими запросами и, по сути, крутиться в вечном цикле, что неэффективно. Необходим механизм опроса
готовности, в который мы могли бы запустить все сокеты, а он бы сообщал нам, которые из них готовы к вводу-выводу. С готовыми мы провели бы все нужные
операции, после чего могли бы заблокироваться, ожидая сокетов, снова готовых к вводу-выводу. Таких механизмов опроса готовности несколько, они отличаются
производительностью, деталями, но обычно он находится «под капотом» и нам не виден.


Как сделать неблокирующийся ввод-вывод? Мы соединяем опрос готовности и операции ввода-вывода с теми и только теми сокетами, которые сегодня готовы. Опрос
готовности у нас блокируется до тех пор, пока не появятся какие-то данные хотя бы в одном сокете.


Второй вопрос по поводу того, что расположено «под капотом» – это вопрос многозадачности. Как мы можем обеспечить одновременную обработку нескольких
запросов (мы договорились, что нам это необходимо)?


Есть 3 базовых варианта:


Отдельные процессы


Самый простой и исторически первый – это на обработку каждого запроса мы запускаем отдельный процесс. Это хорошо, потому что мы можем использовать
блокирующийся ввод-вывод. Если процесс вдруг упадет, это повлияет только на тот запрос, который он обрабатывал, но ни на какие другие.


Из минусов – достаточно сложная коммуникация. Между процессами формально почти нет ничего общего, и любой механизм нетривиальной коммуникации, который мы
хотим организовать, требует дополнительных усилий по синхронизации доступа и т.д. Как эта схема выглядит – вариантов несколько, но обычно запускается 1й
процесс, он делает, например, listen, далее порождает какой-то набор процессов у воркеров, каждый из которых делает accept на том же самом сокете и ожидает
входящих соединений.


Как только появляется входящее соединение, один из процессов разблокируется, получает это соединение, обрабатывает его от начала до конца, закрывает сокет
и снова готов выполнять следующий запрос. Возможны вариации – процесс может порождаться на каждое входящее соединение или они все запущены заранее и т.п.
Это может влиять на характеристики производительности, но это не так принципиально для нас.


Примеры таких систем: FastCGI для тех, кто чаще всего запускает PHP, Phusion Passenger для тех, кто пишет на «рельсах», из БД – это PostgresSQL. На каждое
соединение выделяется отдельный процесс.


Нити операционной системы


В рамках одного процесса мы порождаем несколько потоков, также может быть использован блокирующийся ввод-вывод, потому что будет заблокирован только 1
поток. О нитях знает ОС, она умеет разбрасывать их между процессорами. Нити более легковесны, чем процессы. По сути это означает, что мы можем больше нитей
породить на той же самой системе. Мы вряд ли сможем запустить 10 тыс. процессов, а вот 10 тыс. нитей может быть. Не факт, что это будет эффективно с 10
тысячами, но, тем не менее, они несколько более легковесны.


С другой стороны, отсутствует изоляция, т.е. если происходит какой-то краш, он закрашит весь процесс целиком, а не отдельную нить. И самая большая
сложность – если мы, все-таки, имеем какие-то общие данные в процессе, который обрабатывается в бэкенде, то между нитями отсутствует изоляция. Общая
память, а это означает, что к ней нужно будет синхронизировать доступ. И вопрос синхронизации доступа к общей памяти – это в самом простом случае,
например, может быть соединение с БД, или пул соединений с БД, который общий для всех потоков внутри бэкенда, который обрабатывает входящие соединения.
Синхронизацию доступа сложно провести корректно.


Есть 2 класса сложности:

  1. если потенциальная проблема – это deadlock-и в процессе синхронизации, когда у нас какая-то часть блокируется намертво и невозможно продолжить
    исполнение;
  2. недостаточная синхронизация, когда у нас происходит конкурентный доступ к общим данным и, грубо говоря, 2 потока эти данные изменяют одновременно и
    портят их. Такие программы отлаживать сложнее, не все баги проявляются сразу. Например, знаменитый GIL – Global Interpreter Lock – это один из простейших
    способов сделать многопоточное приложение. Мы говорим, что все структуры данных, вся наша память защищена всего одной блокировкой на весь процесс. Казалось
    бы, это означает, что многопоточное исполнение невозможно, ведь может выполняться только 1 поток, есть только одна блокировка, и кто-то ее захватил, все
    остальные не могут сработать. Да, это так, но вспомним, что мы большую часть времени не работаем на процессы, а ожидаем сетевого ввода-вывода, поэтому в
    момент, когда происходит обращение к какой-то блокирующейся операции ввода-вывода, GIL опускается, поток сбрасывает и по сути происходит переключение на
    другой поток, который готов к выполнению. Поэтому с точки зрения бэкенда использование GIL может быть не так страшно.


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


    Примеры. Из БД это MySQL, где на обработку запроса выделяется отдельный поток. Еще Varnish HTTP Cache, в котором воркерами являются нити, обрабатывающие
    отдельные запросы.


Кооперативная многозадачность


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


С точки зрения ОС кооперативная многозадачность – это просто один поток выполнения, но внутри него само приложение переключается между обработкой отдельных
запросов. Как только пришли какие-то данные, я их прочитал, разобрал http-запрос, подумал, что мне надо сделать, отправил запрос memcached, а это
блокирующаяся операция, я буду ждать, пока придет ответ от memcached, и вместо того, чтобы ждать, я начинаю обрабатывать другой запрос.


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


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


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


Второй вариант – неявный, когда мы пишем программу так, что, вроде бы, никакой кооперативной многозадачности нет. Мы делаем блокирующуюся операцию, как мы
ее и делали, и ожидаем результата прямо здесь. На самом деле, существует где-то «черная магия под капотом» – уже нашел framework языка программирования,
runtime, который в этот момент блокирующуюся операцию превращают в неблокирующуюся и передает управление некоему другому потоку исполнения, но не в смысле
нити ОС, а логическому потоку исполнения, который есть внутри. Такой вариант называется грин треды (green threads).


Внутри кооперативной многозадачности всегда есть такое центральное звено, которое отвечает за всю обработку ввода-вывода. Оно называется реактор. Это некий
паттерн разработки. Интерфейс реактора выглядит следующим образом: он говорит: «Дай мне кучу своих сокетов и свои callback-и, и когда этот сокет будет
готов к вводу-выводу, я тебя вызову».


Второй сервис, который предоставляет реактор, это таймер – » Вызови менять через столько-то миллисекунд, вот мой callback, который надо вызвать». Эта штука
будет встречаться везде, где будет кооперативная многозадачность в явном виде или в неявном.


Внутри обычно реактор устроен довольно просто. У него есть отсортированный по времени срабатывания список таймеров. Соответственно, он берет список
сокетов, который ему дали, отправляет их в механизм опроса готовности. А у механизма опроса готовности всегда есть еще один параметр – он говорит, сколько
времени можно заблокироваться, если нет никакой сетевой активности. В качестве времени блокировки он указывает время срабатывания ближайшего таймера.
Соответственно, либо будет какая-то сетевая активность, какой-то из сокетов будет готов к вводу-выводу, либо мы дождемся срабатывания ближайшего таймера,
разблокируемся и передадим управление в тот или иной callback, по сути в логический поток выполнения.


Вот как выглядит кооперативная многозадачность с явными callback-ами.


Пример на node.js, где мы выполняем какую-то блокирующуюся операцию – на самом деле net.connect. «Под капотом» она неблокирующаяся, все хорошо и
регистрируем callback-и. Если все будет успешно, делай то, а если будет неуспешно, делай это.


Проблема callback-ов в том, что в конечном итоге они превращаются в «лапшу», но мы к этому вопросу еще вернемся.


Второй пример.


Здесь тоже кооперативная многозадачность, хотя никаких следов ее в программе не видно.


Здесь мы видим, что запускаются несколько потоков, которые одновременно параллельно? Хотя, на самом деле, кооперативная многозадачность друг за другом
выполняет блокирующуюся операцию – они скачивают некоторые url-ы. Это функция urlopen на самом деле блокирующаяся, но gevent делает некую «черную магию» и
все эти блокирующиеся сетевые операции становятся неблокирующимися, кооперативная многозадачность, переключение контекстов – этого всего мы не видим,
пишем, вроде бы, обычный совершенно последовательный код, но внутри все работает достаточно эффективно.


Примеры систем с кооперативной многозадачностью: Redis, memcached (он не совсем чисто кооперативная многозадачность, хотя многие думают, что это так). В
чем их особенность, почему они себе могут это позволить сделать? Это хранилища данных, но все операции, все данные находятся в памяти, поэтому они, как и
бэкенд – их процессорное время, которое они тратят на обработку одного запроса крайне маленькое. Т.е. в простейшем случае, чтобы обработать get запрос,
надо найти ключ к внутренним хэшам, найти блок данных и вернуть его – просто записать его в сокет в качестве ответа. Поэтому кооперативная многозадачность
для них эффективна.


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


Если кто-то помнит Redis 3-4 года, может, чуть больше лет назад, там была попытка у автора сделать некую виртуальную память, возможность хранить часть
данных на диске. Это называлось virtual memory. Он попробовал это сделать, но быстро понял, что это не работает, потому что как только начинается дисковый
ввод-вывод, время отклика Redis-а сразу уходит на несколько порядков вниз, и это означает, что смысл в нем теряется.


Но на самом деле ни один из этих трех вариантов не является идеальным. Лучше всего работает комбинированный вариант, потому что выигрывает обычно
кооперативная многозадачность, особенно в той ситуации, если ваши соединения долго висят. Например, веб-сокет – соединение долгоживущее, может жить час.
Если вы на обработку одного веб-сокета выделяете один процесс или одну нить, вы существенно ограничиваете то, сколько всего соединений вы можете на одном
бэкенде держать одновременно. А так как соединение живет долго, держать много одновременных соединений важно, в то время, как работы по каждому соединению
будет немного.


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


2 классических примера – это nginx, в котором вы настраиваете количество воркеров, имеет смысл увеличивать количество воркеров до числа ядер в вашей
системе, это отдельные процессы. Внутри воркера каждый воркер использует неблокирующийся ввод-вывод и кооперативную многозадачность, чтобы обслужить
большое количество одновременных соединений. Воркеры нужны только для того, чтобы распараллелиться между отдельными процессорами.


Второй пример – это memcached, который я уже приводил. У него есть опция запуститься на несколько потоков, несколько нитей ОС. Тогда у нас запускается
несколько нитей, внутри каждой из них крутится реактор, обеспечивающий неблокирующийся ввод-вывод и кооперативную многозадачность, а несколько потоков
позволяют использовать эффективно несколько ядер процессора. Ну, а общей памятью memcached является ведь кэш, который собственно он и обслуживает. Все эти
потоки читают и пишут из того же самого кэша.


Еще один вопрос. Мы все время сейчас говорили о том, как бэкенд обрабатывает, ну большую часть времени, по крайней мере, входящие http-соединения на те
запросы, которые поступают на вход. Но бэкенд делает и исходящие запросы, и таких запросов может быть множество – в сервис-ориентированной архитектуре к
другим сервисам по http, к БД, к Redis-у, к memcached, очередям… И это тот самый сетевой ввод-вывод, который будет сильно влиять на характеристики
бэкенда, как мы об этом договорились изначально.


Посмотрим, как может быть устроен этот драйвер (условно!) базы данных, и как его сделать эффективнее. Во-первых, такая картинка для начала архитектурная:


Предполагаем, что у нас есть несколько серверов, на каждом из них запущен один или несколько экземпляров нашего бэкенда, и существуют какие-то хранилища
данных, которые здесь условно обозначены DB, к которым идут соединения от наших application-серверов. Первый вопрос, если вы используете соединения на один
запрос, т.е. на 1 входящий http-запрос вы открываете соединения с вашей БД с чем угодно и т.д., вы теряете огромное количество времени.


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


Второй вопрос – а почему надо ждать ответа на запрос, прежде чем отправить следующий? Если между запросами нет логической связи и, по сути, поток запросов
состоит из отдельных, никак не связанных между собой запросов, почему бы нам не отправлять их сразу, не дожидаясь ответа, а потом ждать всех ответов?


Мы, конечно, можем. Это называется pipelining.


Так, например, PostgreSQL умеет делать pipelining.


Вы можете существенно сократить время отклика от БД, а значит уменьшить время отклика бэкенда в целом.


Еще одна вещь. Можно между вашим бэкендом и БД поставить proxy.


Здесь нарисована такая немножко утрированная ситуация, здесь две штуки proxy на пути – одна расположена на хосте с application-сервером, другая – перед БД.
Это не обязательно так, я просто попытался на одной картинке нарисовать два случая.


Зачем нужен, вообще говоря, proxy? Если у вас хороший драйвер базы данных, то есть вы делаете все эффективно, у вас постоянное соединение, pipelining и
т.д., то proxy, вообще говоря, для производительности не нужен, более того, с точки зрения производительности он вреден, потому что он ухудшает время
отклика.


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


С третьей стороны, proxy может использоваться еще для кучи других вещей, например, с помощью proxy можно сделать единую точку входа в БД, в memcached,
можно сделать шардинг, переконфигурацию, переключение без участия приложений. Приложение работает с proxy-сервером, оно не знает, что там за ним, а proxy
можно перенастраивать произвольным образом.


Но существуют proxy-сервера, которые нужны. Вот, например, если вы используете PostgreSQL, вы везде прочитаете, что необходимо перед ним запустить
PgBouncer, и жизнь будет ваша гораздо лучше. Почему? Причина простая – как мы уже говорили, PostgreSQL на обслуживание каждого соединения запускает
отдельный процесс, форкается, это достаточно дорогостоящая операция. Много процессов, много инстансов на каждое соединение держать тоже невыгодно и
неудобно, и proxy, расположенный перед PostgreSQL, позволяет это дело оптимизировать. Он на себя принимает сколько угодно соединений, сколько бы ни было
application-серверов, и их отображает на меньшее количество соединений к PostgreSQL, примерно на 100.


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


Реальный мир


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


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


Итак, если вы пишете на JavaScript. JavaScript однопоточный, кроме веб-воркеров, но они являются изолированной сущностью. Он однопоточный
с точки зрения модели вычислений, в нем асинхронный ввод-вывод, не блокирующийся, в нем есть некий реактор, в котором вы регистрируете таймеры с
callback-ами и т.д. Что бы вы ни делали, если вы просто будете писать код на JavaScript, в конечном итоге у вас получится «лапша» из callback-ов. К
счастью, в последнее время JavaScript-мир узнал о такой штуке как Deferred или Promise, узнал, что это круто, и она активно внедряется. Это некая
абстракциия, она может быть полезна в любом языке программирования, где у вас кооперативная многозадачность в явном виде с callback-ами, которая позволяет
развязать эту «лапшу» и сделать ее более стройной. Deferred или Promise – это концепция отложенного результата, т.е. это обещание вернуть результат в
пустой блок, когда результат придет. Я могу на этом пустом блоке регистрировать обработчики ошибочных или успешных ситуаций, выстраивать их в цепочки,
связывать одни Promise-ы с другими и, по сути, моделировать те самые паттерны программирования обычного синхронного и существенно упрощать себе жизнь.


PHP
. Он формально поддерживает многопоточный режим выполнения, но на практике это не работает в силу тех или иных причин исторических. В большинстве случаев,
если вы запускаете PHP, его существенным недостатком является то, что на каждый входящий запрос мы очищаем все и начинаем все сначала. Поэтому есть всякие
PHP-акселераторы, кэширование и т.п. и т.д. Чаще всего, соответственно, это многопроцессное выполнение запросов, внутри блокирующийся ввод-вывод,
постоянное соединение, скажем, с БД в виде какой-то отдельной «нашлепки», как некоего состояния, которое может сохраняться между обработкой отдельных
запросов и т.д. и.т.д.


Ruby on Rails
– некая вещь, больше имеющая воздействие на мир. До Ruby 1.9, если я не ошибаюсь, потоки внутри Ruby были грин тредами, т.е. на самом деле были
кооперативной многозадачностью. это честные потоки ОС. Есть различные варианты. Самый базовый – это многопроцессный и блокирующийся ввод-вывод. Есть
framework EventMachine, который был списан с Python-овского framework-а Twisted, позволяющий сделать кооперативную многозадачность. У него есть свои плюсы
и минусы, есть реализации, использующие EventMachine.


Есть Python. Python счастлив тем, что на нем можно написать любой вариант. Можно написать многопроцессный сервер, можно многопоточный,
можно с кооперативной многозадачностью, с callback-ами или с грин тредами. Все доступно в различных вариантах и комбинациях, но в принципе все опять
довольно скучно, все то же самое.


Есть Java со своей виртуальной машиной и все, соответственно, языки которые работают на JVM, потоки ОС уже давно, когда-то тоже в самом
начале были грин треды. Есть возможность делать блокирующий и неблокирующий ввод-вывод, и как в любом мире enterprise есть какой-нибудь framework, который
я могу воткнуть в середину, и он для меня вообще все это дело абстрагирует. Мне все равно, я ему просто сказал, что делать, а моя задача – только писать
что то сверху.


Есть .NET, который все то же самое – потоки ОС и т.д., некое отступление, есть конструкция языка async/await, которая напоминает чем-то
Deferred или Promise – такое движение к чему-то более светлому. Почему более светлому? Потому что до этого все очень одинаковое и грустное.


Есть Go, который появился относительно недавно, поэтому смог уже на старте оторваться от преследователей и сделать сразу что-то
интересное. В Go есть горутины, которые являются по сути своей грин тредами, т.е. не являются потоками ОС, но с другой стороны внутренний механизм
выполнения основан на том, что может быть запущено несколько потоков ОС, на которые будут шедулиться горутины, т.е. это комбинация кооперативной
многозадачности и многопоточности. Внутри «под капотом» всегда неблокирующийся ввод-вывод. Из моей горутины я делаю операции, как будто бы они
блокирующиеся, но на самом деле происходит переключение между горутинами, как только я заблокировался, будет выполняться другая горутина. В Go много всего
еще интересного, там есть каналы, своя концепция конкурентного программирования, но у нас разговор не об этом.


Есть Erlang, который старше чем Go, и он тоже интересен тем, что у него своя модель конкурентного программирования, соответственно, в
Erlang-е процессы, а в отличие от горутинов ы Go,l они больше похожи на реальный процесс с точки зрения логики, т.е. они полностью изолированы друг от
друга, а горутины работают в общем адресном пространстве и видят всю память. С точки зрения реализации это те же самые неблокирующийся ввод-вывод,
кооперативная многозадачность и использование потоков ОС для того, чтобы использовать все доступные ядра процессора.


Почему Go и Erlang я разместил в конце и почему они интереснее? Потому что уже в язык встроен по сути весь framework, чтобы делать эффективный сетевой
ввод-вывод и эффективную многозадачность. И он уже есть в языке, и вы не замечаете, что это происходит, вы можете сразу начинать писать свои приложения.
Например, если вы пишете http-сервер на Go, используя стандартную библиотеку Go, обработка каждого входящего запроса будет выполняться в отдельный
горутине. Поэтому в этой горутине можете делать все, что угодно, это никоим образом не повлияет на другие, параллельно обрабатывающиеся запросы.


Все что угодно – это можете делать любые сетевые обращения, на чем-то блокироваться, будут выполняться другие потоки.


Вопрос из зала
: Хочется узнать Ваше мнение про libevent. В каких случаях стоит использовать, не стоит? Может, что-то поподробнее, более практическое применение?


Ответ
: Идет речь о С-шной библиотеке. Есть несколько библиотек, вся их задача – это реализовать паттерн реактора на С. Основная идея в том, что они абстрагируют
механизм опроса готовности, который в каждой операционной системе немножко свой, и сверху предоставляют некий унифицированный интерфейс. Есть libevent и
libev, есть еще несколько. Проще всего посмотреть на те или иные С-шные большие проекты – что они используют. Я помню, что в libevent-е были какие-то свои
баги, в libev-e – свои, и в конечном итоге каждый большой проект выбирал себе «под капотом» что-то свое в зависимости от ситуации. Надо использовать что-то
такое, если вы пишете на С.


Вопрос из зала
: Я пишу на PHP и интересует такой момент: при формировании страницы мне нужно, например, сделать 2 сокет-запроса. Что Вы посоветовали бы, чтобы не
дожидаясь ответа от этих сокет-запросов, можно было бы продолжить выполнение кода, но, получив ответ, «выплюнуть» все сразу в браузер – контент,
сформированный по логике после вызова сокетов, и с тем контентом, который отдают сокеты?


Ответ
: Я, к стыду своему, не знаю, как это сделать на PHP? и не знаю, есть ли что-то доступное. С точки зрения чисто теоретической можно написать С-шное
расширение, которое бы это делало. Никакой проблемы принципиальной нет, надо использовать неблокирующийся ввод-вывод и т.д.


Вопрос из зала
: Такой практически вопрос: у Вас в докладе была одна часть про то, что гораздо оптимальнее было бы отправлять запросы в базу, например, или еще каким-то
сервисом с бэкенда все вместе сразу, если это позволительно. Есть ли какие-то советы, как это сделать в реальном мире? Потому что приложение уже есть и оно
не рассчитывало на это. Переписывать бэкенд? Тоже надо полностью логику… или можно будет proxy какой-нибудь поставить, который будет принимать в себя
запросы и сам отправлять их пачками?


Ответ
: Proxy вам не нужен. Если у вас есть бэкенд, есть две части ответа на этот вопрос. Первая – техническая, которая перекликается с предыдущим вопросом.
Можете ли вы это в принципе сделать из того framework языка программирования, на котором вы пишете. В большинстве случаев это возможно, потому что это
можно сделать на С. А вторая часть этого вопроса – как вашу логику бэкенда изменить. И самое страшное – это, мне кажется, изменение логики бэкенда. Когда
вам нужно отбросить последовательную модель программирования, которая есть в голове. Наверное, надо что-то переписывать, без этого никуда, но если вы
хотите добиться уменьшения времени отклика, придется. Надо усложнять логику ради улучшения характеристик.

Facebook

Twitter

Вконтакте

Google+

← HighLoad++ для начинающих

Общая логика масштабирования →

Не нужно ждать бэкенд: Решения и последствия

Фото
ремикс доступен благодаря любезности
мрипп.
CC BY 2.0

Как фронтенд-разработчик, вы часто стремитесь обеспечить наилучшие возможности для конечных пользователей вашего приложения . В стандартном приложении Rails все довольно просто — пользователь нажимает кнопку отправки и ждет обновления. Затем пользователь видит полностью обновленные данные. Из-за асинхронной природы динамических интерфейсов часто упускается из виду, что происходит «в середине» транзакции вашего пользователя. — нажимается кнопка, и пользователь ожидает какого-то уведомления о том, что его задача завершена.Что должно отображаться? Что делать, если произойдет сбой? Чтобы ответить на эти вопросы, вы можете принять как минимум два решения.

Решение №1: Дождитесь бэкэнда, затем обновите.

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

Часто это единственный выбор для решения проблемы согласованности — есть действия, которые имеют эффекты, которые мы не можем вычислить во внешнем интерфейсе из-за отсутствия необходимой информации.Рассмотрим форму входа — мы не можем быть уверены, что пользователь вошел в систему или нет, пока серверная часть не завершит свою логику.

Реализация часто довольно проста — мы просто выполняем некоторый вызов AJAX, ждем, пока обещание не будет выполнено (вы можете прочитать об этом более подробно здесь), а затем выполняем обновление ваших представлений.

Пример:

Представьте, что у вас есть простое приложение со списком дел — одна из его функций — пользователи могут добавлять в него задачи. Существует шина событий, где вы можете подписаться на события, опубликованные вашим представлением.Ваши данные хранятся в объекте ReadModel — вы можете попросить его вернуть текущий список задач и обновить его с помощью метода addTask . Такие обновления автоматически обновляют представление.

Ваш класс Dispatcher ( Glue ) может выглядеть следующим образом:

 
класс Диспетчер
  конструктор: (@eventBus, @commands, @readModel, @flashMessages) ->
    @ eventBus.on ('addTask', (taskText) ->
      response = @ commands.addTask (taskText)
      отклик
        .успех ((json) => @ readModel.addTask (json.id, taskText))
        .fail (=> @ flashMessages.error («Не удалось добавить задачу.»))
    )
  

Здесь вы ждете завершения вашей команды addTask — она ​​в основном отправляет POST-запрос к вашему бэкэнду Rails, а данные задачи возвращаются через JSON. Вы определенно видели этот шаблон много раз — это самый «распространенный» способ обработки обновлений.

Плюсов:

  • Реализация проста — вам не нужно вводить никаких специальных шаблонов.
  • Он хорошо согласуется с соглашениями Rails — давайте возьмем небольшую часть кода, представленного выше:

      (json) => @ readModel.addTask (json.id, taskText)
      

    Как видите, ID данной задачи возвращается в ответе JSON. В основном такой шаблон предоставляется по соглашению в типичном приложении Rails — первичные ключи предоставляются из вашей базы данных, и такие знания должны передаваться от бэкэнда к интерфейсу. Обработка таких вариантов использования в методе «Подождите, пока серверная часть, затем обновится» вообще не требует изменения соглашений Rails.

  • Все интерфейсные данные сохраняются — нет проблем с «поддельными» данными, которые могут быть введены только во внешнем интерфейсе. Это означает, что в любой момент времени у вас может быть меньше данных, чем на сервере.

Минусы:

  • Обратная связь для пользователя задерживается — пользователь по-прежнему вынужден ждать завершения своей задачи, прежде чем будет предоставлена ​​надлежащая обратная связь. Это решение делает наш интерфейс менее отзывчивым.
  • Разработчики вынуждены предоставлять и поддерживать различные виды визуальной обратной связи — ожидания без визуальной обратной связи недостаточно.Если для выполнения действия требуется значительное количество времени, отсутствие визуальной обратной связи вынудит пользователя повторить свои запросы (обычно путем нажатия кнопки дважды или более), поскольку такое время будет неверно истолковано как «приложение не работает».

    Это означает, что нам нужно реализовать еще одно решение — наиболее распространенные «взломы» здесь — это отключение входов, изменение значения кнопки на что-то вроде «Отправка…», предоставление некоего визуального индикатора «Загрузка» и т. Д. Такие «временные» после неудачи или успеха решение необходимо очистить.Ошибки, связанные с отсутствием очистки таких «временных» визуальных обратных связей, — это то, что пользователи видят напрямую, и это часто их очень раздражает — они просто видят, что здесь что-то «сломано»!

  • Трудно добиться «конечной согласованности» с этим подходом — и с учетом сегодняшних требований есть большая вероятность, что вы захотите это сделать. Если вы реализуете свой код с «подождите, пока серверная часть, а затем обновите», может быть сложно подготовить архитектуру к «автономному режиму» или отложить синхронизацию (например, с функцией автосохранения).

Советы:

  • Вы можете использовать хранилища Reflux, чтобы легко «привязать» считываемые обновления модели к вашим компонентам React.
  • Обещает помочь, если одно бизнес-действие включает в себя множество процессов, с которыми нужно консультироваться с серверной частью или каким-либо внешним инструментом. Вы можете использовать $ .when для одновременного ожидания множества обещаний.
  • Если вы структурируете свой код, используя подход хранилища, поощряемый Flux, было бы хорошо предоставить какие-то UserMessageStore и IntermediateStateStore для централизации ваших визуальных отзывов.
  • Вы можете прослушивать «событий» ajaxSend , чтобы обеспечить простейшую визуальную обратную связь о том, что что-то обрабатывается на сервере. Это простой фрагмент кода, который вы можете использовать по своему усмотрению (используя jQuery):

      UPDATE_TYPES = ['PUT', 'POST', 'DELETE']
    $ .activeTransforms = 0
    
    $ (документ) .ajaxSend (e, xhr, настройки) ->
        вернуть, если settings.type? .toUpperCase () в UPDATE_TYPES
        $ .activeTransforms + = 1
    
    $ (документ) .ajaxComplete (e, xhr, настройки) ->
        вернуть, если настройки.введите? .toUpperCase () в UPDATE_TYPES
        $ .activeTransforms - = 1
      

    Мы связываемся с ajaxSend и ajaxComplete «событиями», чтобы отслеживать количество активных транзакций AJAX. Затем вы можете запросить эту переменную, чтобы получить визуальную обратную связь. Один из самых простых — предоставить предупреждение, когда пользователь хочет покинуть страницу:

      $ (окно) .on 'beforeunload', ->
        если $ .activeTransforms
          '' 'Есть некоторые ожидающие обработки сетевых запросов, которые
             означает, что закрытие страницы может привести к потере несохраненных данных.'' '
      

Решение №2: Обновите, затем дождитесь бэкэнда.

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

Этот подход позволяет полностью отделить от выполнения действия от с сохранением его эффектов.Он предоставляет вам набор очень интересных способов работы вашего внешнего интерфейса — вы можете отложить внутреннюю синхронизацию на сколько угодно или сделать свое приложение «автономным», при котором пользователь может выполнять действия даже при отсутствии подключения к Интернету. Так работают многие мобильные приложения — например, я могу добавить свою задачу в приложение Wunderlist, и она будет синхронизироваться, если будет доступно подключение к Интернету, — но моя задача сохранена и я могу просмотреть ее в любое время, когда захочу.

Есть также скрытый эффект этого решения — если вы хотите быть последовательными в этом подходе, вы собираетесь уделять все больше и больше внимания интерфейсу, делая его богаче. Есть много вещей, которые вы можете сделать, даже не консультируясь с серверной частью — и большинство программистов на Rails забывают об этом. При таком подходе перемещение логики из серверной части в интерфейсную становится естественным.

Пример:

В этом простом примере вам мало что нужно сделать, чтобы реализовать этот подход:

  класс Диспетчер
  конструктор: (@eventBus, @commands, @readModel, @flashMessages, @uuidGenerator) ->
    @ eventBus.on ('addTask', (taskText) ->
      uuid = @uuidGenerator.nextUUID ()
      @ readModel.addTask (uuid, taskText)
      @ commands.addTask (uuid, taskText)
        .fail (=>
          @ readModel.removeTask (uuid)
          @ flashMessages.error («Не удалось добавить задачу.»)
        )
    )
  

Как видите, в этом подходе есть небольшие изменения:

  • Появилась новая зависимость uuidGenerator . Поскольку мы добавляем задачу как можно быстрее, мы не можем ждать, пока идентификатор будет сгенерирован на бэкэнде — теперь интерфейс назначает первичные ключи нашим объектам.
  • Поскольку, когда что-то пошло не так, нам нужно компенсировать наше действие, в нашу модель чтения добавлен новый метод под названием removeTask . Это не проблема, когда есть еще функция удаления задач — но когда вы добавляете такой метод только для компенсации действия, я бы посчитал это запахом кода.

Самое интересное, что вы можете взять вызов @commands и переместить его на совершенно другой уровень. Вы можете добавить его в очередь команд «для синхронизации» или сделать что-то более сложное, но поскольку пользователь получает немедленную обратную связь, вы можете сделать это, когда захотите.

Плюсов:

  • Сделает ваш интерфейс максимально отзывчивым — ваши клиенты будут довольны этим решением. Благодаря этому ваши пользователи будут больше похожи на настольные компьютеры при работе с вашим интерфейсом.
  • Это делает связь с серверной частью более гибкой. — вы можете принять решение немедленно связаться с серверной частью или отложить ее на сколько угодно.
  • Заставить ваше приложение работать в автономном режиме легко. — поскольку мы немедленно принимаем меры, все, что вам нужно, — это отключить внешние службы при работе в автономном режиме и добавить их в какую-либо очередь, чтобы сделать это общение, когда вы подключитесь к сети. опять таки.
  • Это делает ваш интерфейсный код богаче — если ваша цель — переместить свою логику на интерфейс, принятие этого решения заставляет вас переместить всю необходимую логику и данные во внешний интерфейс при реализации взаимодействия с пользователем.
  • Проще сделать ваши команды «чистыми» — если вы реорганизуете свой бэкэнд для архитектуры CQRS, существует требование, чтобы ваши команды вообще не возвращали никаких выходных данных. С обновлением внешнего интерфейса и устранением необходимости консультироваться с каждым эффектом действия с серверной частью (создание UUID на интерфейсе является одним из основных шагов в этом направлении) вы можете легко реорганизовать свои запросы POST / PUT / PATCH / DELETE, чтобы возвращать только заголовок HTTP и вообще никаких данных.
  • Вы можете уменьшить накладные расходы на свой внутренний код — поскольку вы не делаете запрос немедленно, вы можете реализовать какую-то пакетную обработку или предоставить другой способ уменьшить количество запросов, сделанных пользователем к вашей службе. Таким образом вы можете увеличить пропускную способность вашего бэкэнда, что может быть полезно, если вы испытываете проблемы с производительностью.

Минусы:

  • Может быть трудно вычислить эффект действия на интерфейсе — есть некоторые типы действий, которые может быть трудно выполнить без консультации с сервером — например, аутентификация. Везде, где данные, необходимые для вычисления результата, являются конфиденциальными, гораздо проще реализовать код, который сначала консультируется с серверной частью .
  • Внедрение сложнее — вам нужно реализовать компенсацию действий пользователя, что может быть сложно. Существует также нетривиальная проблема последовательного выполнения множества действий — что делать, если что-то в середине такой «транзакции» не удается? Также могут быть ситуации, когда реализация компенсации без правильных шаблонов может сделать ваш код менее удобным в обслуживании.
  • Сложнее добиться согласованности данных таким способом — при первом подходе у вас нет возможности иметь «дополнительные» данные во внешнем интерфейсе, которые не синхронизируются с вашим сервером — у вас может быть меньше данных, чем на бэкэнде. В этом подходе это сложнее — у вас могут быть данные, которых нет на сервере, но они существуют на вашем интерфейсе. Ваша задача — сделать ваш код в конечном итоге согласованным — и при таком подходе сделать это труднее .
  • Вам необходимо изменить свой бэкэнд — решения, необходимые для хорошей реализации этого подхода, например, генерация UUID должна идти вразрез с соглашениями Rails — вам нужно будет написать некоторый бэкэнд-код для его поддержки.

Советы:

  • Вы можете получить большую выгоду от отслеживания с возвратом, которое обеспечивают неизменяемые структуры данных. Поскольку при таком подходе каждая мутация возвращает новую коллекцию, если вы сделаете свое состояние неизменным, будет легче отслеживать «историю» своего состояния и, соответственно, выполнять откат, если что-то не удается. Существует библиотека ImmutableJS, которая помогает вам реализовать такой шаблон.
  • Чтобы избежать неприятного запаха кода при создании методов только для компенсации сбоев, вы можете реорганизовать свои команды в шаблон Command.Вы можете создать его экземпляр с данными, которые ему нужны, и предоставить метод undo , который вы вызываете, чтобы компенсировать эффект такой команды.

    Вот небольшой пример такого подхода:

      Команды класса
      конструктор: (@readModel) ->
    
      addTask: (uuid, taskText) ->
        новый AddTaskCommand (@readModel, uuid, taskText)
    
    класс AddTaskCommand
      конструктор: (@readModel, @uuid, @taskText) ->
    
      звонок: ->
        # поместите сюда тело вашего метода addTask.
    
      отменить: ->
        # логика компенсации
    
     # в нашем диспетчере:
      @eventBus.на ('addTask', (taskText) ->
        uuid = @ uuidGenerator.nextUUID ()
        @ readModel.addTask (uuid, taskText)
        command = @ commands.addTask (uuid, taskText)
        command.call (). fail (command.undo)
      )
      

    Таким образом вы «усиливаете» команду знаниями об «отмене» самой себя. Это может быть полезно, если логика, которую вам нужно реализовать, действительна только для компенсации события — таким образом ваш другой код может предоставить интерфейс, который можно использовать только для выполнения реальных бизнес-действий, а не для их отмены.

  • В сложных интерфейсах это хороший шаг для построения состояния объекта домена на основе событий домена.Этот метод называется «источником событий» и хорошо согласуется с идеей «реактивного программирования». Я просто хочу сообщить, что это возможно — RxJS — это библиотека, которая может вам в этом помочь.

Заключение

Решения, которые вы можете принять для обработки эффектов действий пользователя, могут иметь серьезные последствия для вашего общего дизайна кода . Знание этих последствий — первый шаг к тому, чтобы сделать ваш интерфейс более удобным в обслуживании и более удобным для пользователей. К сожалению, серебряной пули нет. Если вы планируете сделать свой интерфейс богаче и хотите максимально отделить его от серверной части, было бы здорово попробовать использовать подход «сначала обновление» — он имеет множество последствий, которые «подталкивают» нас к этой цели. . Но все зависит от вашего домена и функций. Я надеюсь, что этот пост поможет вам принять эти решения более осознанно.

Есть ли у вас интересный опыт в этой области? Или у вас есть вопрос? Не забудьте оставить комментарий — я буду более чем счастлив обсудить с вами!

Сделайте обновления пользовательского интерфейса мгновенными, не дожидаясь бэкэнда с помощью React | Автор: Tomasz Fiechowski

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

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

Следуя структуре проекта из репозитория, реализация будет осуществляться в src / batching / usePhotos.js . Давайте взглянем на урезанную «начальную» версию usePhotos , чтобы помочь нам понять скелет и структуру компонента, который мы будем внедрять:

Мы используем useState , чтобы сохранить список фотографий, и pendingUpdates объект.

Почему возражают? Мы будем использовать идентификаторы с фотографиями в качестве ключей для более быстрого и удобного доступа к элементам, ожидающим обновления. Этот объект будет иметь следующую форму:

 {[id: string]: {[id: string}, / * модификации фотографий * /}} 

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

Добавление списка ожидающих обновлений — updatePhotos

Давайте сначала реализуем updatePhotos :

В качестве параметра, itemsToUpdate , мы передаем массив объектов следующей формы: {id: string, / * фото модификации * /} , например: {id: 1, like: true} , если мы хотим отметить фотографию с id: 1 как понравившуюся.

С помощью getItemsToResetAndUpdate мы выполняем требование 5 по пропуску ненужных обновлений. Если фотография понравилась или не понравилась за короткий промежуток времени, эта функция обнаружит это и удалит это обновление из объекта pendingUpdates .

Здесь мы используем функцию update из immutability-helper . Вкратце, он удалит все ключи, указанные в $ unset из pendingUpdates , и объединит их с объектом toUpdate .Таким образом мы удаляем все ненужные обновления и добавляем действительные.

Наконец, мы вызываем performUpdates.callback — это то, как следует использовать значение из useDebouncedCallback .

Давайте перейдем к performUpdate . Мы вернемся к getItemsToResetAndUpdate и другим служебным функциям позже.

Выполнение обновления — выполнить обновление

Это функция, в которой выполняется большая часть необходимой логики. Это реализовано следующим образом:

После проверки наличия ожидающих обновлений, которые нужно вернуть раньше, мы очищаем pendingUpdates , чтобы мы могли принять следующий пакет обновлений, не мешая текущему.

Затем обновления применяются к фотографиям , чтобы отразить изменения, ожидающие обновления . Затем мы вызываем API ( onUpdate ) и удаляем флаг заблокирован с фотографий в случае успеха или возвращаем фотографии в исходное состояние в случае каких-либо исключений.

Утилиты

Сейчас мы рассмотрим служебные функции. Начнем с упомянутого getItemsToResetAndUpdate . Мы также рассмотрим isUpdateNeeded здесь.

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

Затем мы преобразуем их в соответствующий формат: toReset будет использоваться с $ unset , поэтому это должен быть массив идентификаторов фотографий, а toUpdate — это просто объект той же формы, что и pendingUpdates чтобы слить это.

Функция isUpdateNeeded проверяет, нужно ли обновлять фотографию после применения itemUpdate .По сути, он проверяет, отличаются ли поля из itemUpdate от соответствующих полей на исходной фотографии. Если они совпадают, обновление не требуется.

Теперь давайте реализуем все остальные утилиты, используемые в performUpdate . Это довольно простые функции манипулирования состоянием:

  • removePhotosLockedFlag принимает pendingUpdates в качестве аргумента и удаляет флаг заблокирован с фотографии, если у него есть соответствующий pendingUpdate .
  • revertPhotosToOriginalState принимает исходный список фотографий в качестве аргумента и меняет местами текущие фотографии с их исходными версиями. Также на всякий случай снимается флаг заблокирован .
  • applyUpdatesToPhotos применяет изменения из pendingUpdates к списку фотографий .
  • clearPendingUpdates просто очищает список ожидающих обновлений. Его также можно было бы назвать setPendingUpdates ({}) , но я хотел извлечь его в функцию с более значимым именем.

Что делать, когда потоки зависают в socketRead () в ожидании ответа от серверной части

Кузов

Распространенная причина зависания потоков в WebSphere Application Server — это когда поток отправляет запрос на внутренний сервер, такой как база данных, и ожидает ответа неожиданно долгое время. Когда это произойдет, вы увидите socketRead () в верхней части трассировки стека потока. Например:

[9/8/15 19: 19: 27: 702 EDT] 00000062 ThreadMonitor W WSVR0605W: Тема «WebContainer: 2301» (00001785) была активна 1425013 миллисекунд и может зависнуть.Всего на сервере 21 поток (-а), которые могут зависнуть.
в java.net.SocketInputStream.socketRead0 (собственный метод)
в java.net.SocketInputStream.read (SocketInputStream.java:140)
в com.ibm.db2.jcc.t4.zb (z.java:199)
на com.ibm.db2.jcc.t4.zc (z.java:289)
на com.ibm.db2.jcc.t4.zc (z.java:402)
на com.ibm.db2.jcc.t4 .zv (z.java:1170)
в com.ibm.db2.jcc.t4.cb.b (cb.java:40)
в com.ibm.db2.jcc.t4.qa (q.java:32 )
по адресу com.ibm.db2.jcc.t4.sb.i (sb.java:135)
на com.ibm.db2.jcc.am.yn.gb (yn.java:2066)
на com.ibm.db2.jcc.am.zn.pc (zn. java: 3446)
на com.ibm.db2.jcc.am.zn.b (zn.java:4236)
на com.ibm.db2.jcc.am.zn.fc (zn.java:2670)
на com.ibm.db2.jcc.am.zn.execute (zn.java:2654)
в com.ibm.ws.rsadapter.jdbc.WSJdbcPreparedStatement.execute (WSJdbcPreparedStatement.java:618)
в com.mycompany.myapp. MyClass.executeDatabaseQuery (MyClass.java:123)

В этом примере приложение выполняет оператор SQL (запрос или обновление) для базы данных DB2 и ожидает ответа от сервера базы данных .По умолчанию поток будет продолжать ждать бесконечно долго, пока не получит ответ. Это означает, что поток может зависать в этом состоянии на очень долгое время, если оператор SQL очень сложен и требует много времени для выполнения, если база данных работает очень медленно, если сервер базы данных перестает отвечать или если есть проблема в сетевом соединении между сервером приложений и базой данных.

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

Во-первых, для оператора SQL можно установить тайм-аут запроса. По истечении количества секунд, указанного в тайм-ауте, запрос будет прерван, и будет создано исключение, чтобы поток больше не зависал в ожидании ответа. Разработчик приложения может вызвать setQueryTimeout () для каждого java.sql.Statement в приложении. Однако более простым решением для администратора WebSphere является установка настраиваемого свойства источника данных webSphereDefaultQueryTimeout.Этот тайм-аут будет применяться ко всем операторам SQL, созданным для соединений, полученных из источника данных. Второе свойство, syncQueryTimeoutWithTransactionTimeout, также можно установить в качестве настраиваемого свойства источника данных. С этим набором WebSphere вычислит время, оставшееся до истечения времени ожидания транзакции (если выполняется в рамках глобальной транзакции), и автоматически установит это значение для тайм-аута запроса.

Также можно включить трассировку WebSphere Application Server для идентификации конкретных операторов SQL, выполнение которых занимает много времени.Это может быть полезно, если некоторые, но не все операторы выполняются медленно. Вы можете увидеть эту информацию в полной трассировке пула соединений WebSphere (* = info: WAS.j2c = all: RRA = all), но ее также можно увидеть, если включена следующая облегченная трассировка:

* = info: com.ibm .ws.rsadapter.jdbc.WSJdbcPreparedStatement = all: com.ibm.ws.rsadapter.jdbc.WSJdbcCallableStatement = all: com.ibm.ws.rsadapter.jdbc.WSJdbcConnection = все

для трассировки,

в трассировке строки executeQuery Entry и executeQuery Exit.Вы также можете увидеть «executeUpdate» или «выполнить» вместо «executeQuery». Если вы не уверены, вызывает ли приложение executeQuery, exceuteUpdate или execute, вы можете просто выполнить поиск по запросам «> execute» и «

[9/8/15 21: 29: 40: 693 EDT] 0000001d WSJdbcPrepare> executeQuery Entry
com.ibm.ws.rsadapter.jdbc.WSJdbcPreparedStatement@42084208

[09.08.15 21:15 : 40: 364 EDT] 0000001d WSJdbcPrepare com.ibm.ws.rsadapter.jdbc.WSJdbcResultSet@56245624

В этом примере вы можете видеть, что запрос занял 4 минуты. Если вы вообще не получаете ответа от базы данных, вы можете увидеть только «Entry» без «Exit». Вы можете посмотреть на несколько строк над «Entry», чтобы найти выполняемый оператор SQL. Например:

[9/8/15 21: 29: 40: 570 EDT] 0000001d WSJdbcConnect> prepareStatement Entry
com.ibm.ws.rsadapter.jdbc.WSJdbcConnection@104b104b
выберите * from table_name
TYPE FORWARD ONLY (1003)
CONCUR READ ONLY (1007)

Здесь вы видите, что «select * from table_name» — это SQL-оператор бежал медленно. После того, как вы узнаете инструкцию SQL, следующим шагом будет обращение к администратору баз данных, чтобы определить, почему конкретная инструкция SQL работает медленнее, чем ожидалось.

Вы также можете увидеть зависание потоков в socketRead () при подключении к другим типам внутренних серверов помимо баз данных. Это обычное явление, когда приложения отправляют HTTP-запросы на удаленные серверы или отправляют запросы веб-служб и ждут ответа. Эти случаи могут быть решены аналогичным образом, либо путем установки тайм-аута, чтобы запрос не мог ждать дольше, чем период тайм-аута для получения ответа, либо путем работы с администраторами внутреннего сервера, чтобы определить, почему это может быть медленным. реагировать.

Для веб-служб JAX-WS, которые используют механизм веб-служб JAX-WS WebSphere Application Server, вы можете установить тайм-аут чтения, чтобы запросы истекали по тайм-ауту, если от веб-службы нет ответа. По умолчанию этот тайм-аут установлен на 300 секунд (5 минут). Это можно изменить, установив настраиваемое свойство JVM «тайм-аут». Хотя имя этого свойства является общим, его конкретная цель — установить тайм-аут чтения для запросов веб-служб JAX-WS. Вы также можете установить свойство «readTimeout» в наборе транспортной политики HTTP для клиента веб-службы или установить «тайм-аут» в организации.apache.axis2.context.MessageContext в коде приложения.

Вы также можете включить трассировку веб-служб со следующей спецификацией трассировки:

* = info: com.ibm.ws.webservices. = All: org.apache. * = All: com.ibm.ws.websvcs. * = все: com.ibm.ws.metadata. * = все

В трассировке можно найти следующие сообщения:

WSWS3571I: исходящий HTTP-запрос SOAP

WSWS3570I: входящий HTTP-ответ SOAP

Эти сообщения сообщают вам, когда был отправлен запрос веб-служб (исходящий HTTP-запрос SOAP) и когда был получен ответ от веб-службы (входящий HTTP-ответ SOAP).Если вы вообще не получили никакого ответа, вы увидите только сообщение WSWS3571I без соответствующего сообщения WSWS3570I. Оба сообщения будут включать полное сообщение SOAP, которое часто помогает определить, почему для получения ответа могло потребоваться много времени.

Для других реализаций веб-служб и других HTTP-клиентов существуют другие тайм-ауты, которые могут быть установлены на тайм-аут запроса, обычно называемый таймаутом чтения или тайм-аутом сокета.

Наконец, можно включить трассировку TCP / IP с помощью такого инструмента, как Wireshark, чтобы видеть пакеты, отправленные по сети между клиентом и сервером.Это может сказать вам, где проблема: на стороне клиента, на стороне сервера или в сети.

Не забудьте также прочитать блог Дуга Спата под названием «Аналогия для потоков в socketRead».

[{«Business Unit»: {«code»: «BU053», «label»: «Cloud & Data Platform»}, «Product»: {«code»: «», «label»: «»}, «Компонент «:» «,» Платформа «: [{» code «:» «,» label «:» «}],» Version «:» «», «Edition»: «», «Line of Business»: {«code «:»»,»этикетка»:»»}}]

Управление внутренними запросами и уведомлениями внешнего интерфейса в бессерверных веб-приложениях

Веб-приложения и мобильные приложения обычно взаимодействуют с серверной службой, часто через API.Многие интерфейсные приложения передают запросы на обработку, ждут результата, а затем отображают его пользователю. Этот синхронный подход — только один из способов обработки сообщений, но у современных приложений есть альтернативы, обеспечивающие более удобное взаимодействие с пользователем.

Есть три распространенных способа создания запросов и управления ими из вашего внешнего интерфейса. В этом сообщении блога объясняются преимущества и варианты использования каждого подхода. Этот пост ссылается на пример приложения Ask Around Me , которое позволяет пользователям задавать вопросы и отвечать на них в их географической зоне в режиме реального времени.Чтобы узнать больше, обратитесь к части 1 серии приложений для блога.

Синхронная модель

Синхронный вызов — это наиболее распространенный шаблон запроса API, когда вызывающий объект делает запрос к API, а затем ожидает ответа:

Этот тип запроса легко реализовать и понять, поскольку он отражает функциональную схему «вызов-ответ», с которой знакомы многие разработчики. Инициатор запроса блокируется до завершения вызова, поэтому он хорошо подходит для простых запросов с коротким временем выполнения.Примеры использования включают: получение содержимого корзины покупок, поиск значения в базе данных или отправку адреса электронной почты из веб-формы.

Однако, когда ваш API взаимодействует с другими службами или внешними рабочими процессами, синхронная модель может иметь ограничения. В следующем примере электронной коммерции любая низкая производительность нижестоящих сервисов приводит к задержке всей производительности в оба конца. Кроме того, любые перебои в работе одной из этих служб могут привести к явному отказу всей службы.

Для сервисов с длительными рабочими процессами может достигаться 29-секундный тайм-аут интеграции API Gateway. В следующем примере совместного использования поездок служба, отвечающая за поиск доступных водителей, может иметь сильно различающееся время отклика. Время ожидания этого запроса истекло. Это также создает неудобства для пользователя, поскольку в течение значительного периода времени с ним не поступает обратная связь.

У синхронных запросов есть и другие ограничения.Вы не можете получить более одного ответа на запрос и не можете подписаться на будущие изменения данных. В этом примере запрос API может информировать вызывающих абонентов о драйверах только в конце длительного запроса процесса.

Асинхронная модель

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

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

В этом примере вызывающий абонент отправляет заказ и почти сразу получает ответ от API Gateway.После интеграции сервисов API Gateway сохраняет запрос непосредственно в надежном хранилище, таком как Amazon SQS или DynamoDB, до того, как произойдет какая-либо обработка. Это приводит к относительно постоянному времени ответа вызывающего абонента, независимо от времени обработки нисходящей услуги.

Последующие службы получают сообщения из очереди SQS или таблицы DynamoDB для обработки. Если происходит сбой в нисходящем направлении, сообщения сохраняются в очереди и могут быть повторены позже. С точки зрения пользователя, запрос был успешно отправлен.

Приложение «Спроси меня» обрабатывает публикацию вопросов и ответов асинхронно. API передает пользовательские данные в лямбда-функцию, которая сохраняет сообщение в очереди SQS. SQS немедленно отвечает, указывая, что сообщение было успешно сохранено, завершая ответ API. Затем другая функция Lambda берет сообщения из очереди SQS и обрабатывает их независимо.

Как синхронные, так и асинхронные запросы полезны для различных функций в веб-приложениях, поэтому может быть полезно сравнить их функции и поведение:

Синхронные запросы Асинхронные запросы
Вызывающий ожидает до конца обработки ответа. Вызывающий быстро получает подтверждение, пока обработка продолжается.
Ожидание может повлечь за собой расходы. Минимизирует стоимость ожидания.
Медленность или сбои в нисходящем направлении влияют на общий запрос. Очередь отделяет прием запроса от обработки запроса.
Передает полезную нагрузку между шагами. Чаще передает идентификаторы транзакций.
Ошибка влияет на весь запрос. Ошибка влияет только на сегмент запроса.
Простота внедрения. Средняя сложность в реализации

Обработка значений ответа и состояния для асинхронных запросов

С асинхронными процессами вы не можете передать возвращаемое значение обратно вызывающей стороне так же, как вы можете для синхронных процессов.Помимо первоначального подтверждения того, что запрос был получен, нет обратного пути для предоставления дополнительной информации. Веб-разработчики и разработчики мобильных приложений могут отслеживать состояние бортовых запросов несколькими способами:

  • Опрос : первоначальный запрос возвращает идентификатор отслеживания. Вы создаете вторую конечную точку API для внешнего интерфейса, чтобы проверить статус запроса, ссылаясь на идентификатор отслеживания. Используйте DynamoDB или другое хранилище данных для отслеживания состояния запроса.
  • WebSocket : это двунаправленное соединение между клиентским интерфейсом и серверной службой. Он позволяет отправлять дополнительную информацию после завершения первоначального запроса. Ваши серверные службы могут продолжать отправлять данные обратно клиенту, используя соединение WebSocket.

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

Использование AWS IoT Core для обмена сообщениями в реальном времени

И в синхронной, и в асинхронной моделях предполагается, что вызывающая сторона делает запрос и интересуется только конечным результатом этого запроса. Это не позволяет получать частичную информацию, такую ​​как процент выполнения задачи, или получать постоянные уведомления об изменении данных.

Современные веб-приложения обычно используют шаблон публикации-подписки для получения уведомлений об изменении данных. От получения предупреждений при поступлении нового сообщения электронной почты до предоставления аналитических данных на панели управления — этот метод позволяет получать гораздо более богатые потоки событий из серверных систем.

В Ask Around Me приложение использует этот шаблон при прослушивании новых вопросов из локальной области пользователя. Интерфейс подписывается на значение геохеша местоположения пользователя через AWS SDK.Затем он ожидает сообщений, опубликованных серверной частью в этой теме.

SDK автоматически управляет подключением WebSocket, а также решает многие распространенные проблемы с подключением в веб-приложениях. Сообщения классифицируются с помощью тем, которые представляют собой строки, определяющие каналы сообщений.

Сервис AWS IoT Core управляет широковещательной рассылкой между внутренними издателями и внешними подписчиками. Это включает функцию разветвления, которая происходит, когда несколько подписчиков слушают одну и ту же тему.Вы можете транслировать сообщения на тысячи внешних устройств, используя этот механизм. Для интеграции веб-приложений это предпочтительный способ реализовать публикацию-подписку, чем использование Amazon SNS.

Класс IotData в AWS SDK возвращает клиента, использующего протокол MQTT. После того, как интерфейсное приложение устанавливает соединение, оно возвращает сообщения, ошибки и статус соединения через обратные вызовы:

  mqttClient.on ('подключиться', function () {
          приставка.журнал ('mqttClient connected')
        })

        mqttClient.on ('ошибка', функция (ошибка) {
          console.log ('ошибка mqttClient:', ошибка)
        })

        mqttClient.on ('сообщение', функция (тема, полезная нагрузка) {
          const msg = JSON.parse (payload.toString ())
          console.log ('Сообщение Интернета вещей:', тема, сообщение)
        })
  

Дополнительные сведения о том, как реализовать подключение MQTT WebSocket для вашего приложения, см. В примере кода приложения Ask Around Me .

Объединение нескольких подходов для вашего внешнего приложения

Многие внешние приложения могут комбинировать эти модели в зависимости от типа запроса. Приложение Ask Around Me использует несколько подходов к управлению состоянием вопросов пользователей:

  1. Когда приложение запускается, оно получает начальный набор вопросов от синхронной конечной точки API. Это возвращает список доступных на данный момент вопросов.
  2. Одновременно интерфейс подписывается на тему геохеширования через AWS IoT Core. Любые новые вопросы для этого местоположения геохеша отправляются из службы внутренней обработки во внешний интерфейс через эту тему. Это позволяет веб-интерфейсу получать новые вопросы без последующих вызовов API.
  3. Когда публикуется новый вопрос, он сохраняется в соответствующей очереди SQS и подтверждается. Вопрос обрабатывается асинхронно серверным процессом, который отправляет обновления в тему.

Комбинирование таких подходов к синхронному, асинхронному обмену сообщениями и обмену сообщениями в реальном времени дает несколько преимуществ. Самое главное, чтобы пользовательский опыт оставался неизменным. Пользователь получает немедленную обратную связь при размещении новых вопросов и ответов, в то время как более длительные процессы управляются асинхронно.

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

Заключение

Веб-приложения обычно используют синхронные API-интерфейсы при взаимодействии с серверными службами. Для более длительных процессов асинхронные рабочие процессы могут улучшить взаимодействие с пользователем и помочь в управлении масштабированием.Используя надежные хранилища сообщений, такие как SQS или DynamoDB, вы можете отделить получение запроса и ответ от обработки запроса.

В этом посте я покажу, как современные веб-приложения используют обмен сообщениями в реальном времени через WebSockets для улучшения взаимодействия с пользователем. Это обеспечивает транспортный механизм для передачи обновлений состояния от серверной части к клиентскому интерфейсу. Сервис AWS IoT Core может разветвлять сообщения по темам, транслируя сообщения большому количеству подписчиков внешнего интерфейса.

Чтобы увидеть эти три метода в примере внешнего интерфейса, прочитайте больше о примере приложения Ask Around Me .

PostgreSQL: Документация: 11: 28.2. Сборщик статистики

LWLock ShmemIndexLock Ожидание поиска или выделения места в общей памяти.
OidGenLock Ожидание выделения или назначения OID.
XidGenLock Ожидание выделения или присвоения идентификатора транзакции.
ProcArrayLock Ожидание получения снимка или очистка идентификатора транзакции в конце транзакции.
SInvalReadLock Ожидание получения или удаления сообщений из общей очереди недействительности.
SInvalWriteLock Ожидание добавления сообщения в общую очередь недействительности.
WALBufMappingLock Ожидание замены страницы в буферах WAL.
WALWriteLock Ожидание записи буферов WAL на диск.
ControlFileLock Ожидание чтения или обновления управляющего файла или создания нового файла WAL.
КПП Замок Ожидание выполнения контрольной точки.
CLogControlLock Ожидание чтения или обновления статуса транзакции.
SubtransControlLock Ожидание чтения или обновления информации о суб-транзакции.
MultiXactGenLock Ожидание чтения или обновления общего состояния мультиплексирования.
MultiXactOffsetControlLock Ожидание чтения или обновления сопоставлений мультиплексных смещений.
MultiXactMemberControlLock Ожидание чтения или обновления сопоставлений элементов мультиплексирования.
RelCacheInitLock Ожидание чтения или записи файла инициализации кэша отношений.
Контрольная точкаCommLock Ожидание обработки запросов fsync.
TwoPhaseStateLock Ожидание чтения или обновления состояния подготовленных транзакций.
Табличное пространствоCreateLock Ожидание создания или удаления табличного пространства.
BtreeVacuumLock Ожидание чтения или обновления информации, связанной с вакуумом, для индекса B-дерева.
AddinShmemInitLock Ожидание управления выделением пространства в общей памяти.
Блокировка автоматического вакуумирования Рабочий или программа запуска автоочистки ожидают обновления или чтения текущего состояния рабочих процессов автоочистки.
Блокировка автоматического вакуумирования Ожидание, чтобы убедиться, что стол, выбранный для вакуумирования, все еще нуждается в очистке.
SyncScanLock Ожидание получения начального местоположения сканирования на столе для синхронизированных сканирований.
RelationMappingLock Ожидание обновления файла сопоставления отношений, используемого для хранения сопоставления каталога с файловым узлом.
AsyncCtlLock Ожидание чтения или обновления состояния общего уведомления.
AsyncQueueLock Ожидание чтения или обновления уведомлений.
SerializableXactHashLock Ожидание получения или сохранения информации о сериализуемых транзакциях.
SerializableFinishedListLock Ожидание доступа к списку завершенных сериализуемых транзакций.
SerializablePredicateLockListLock Ожидание выполнения операции со списком блокировок, удерживаемых сериализуемыми транзакциями.
OldSerXidLock Ожидание чтения или записи конфликтующих сериализуемых транзакций.
SyncRepLock Ожидание чтения или обновления информации о синхронных репликах.
BackgroundWorkerLock Ожидание чтения или обновления состояния фонового рабочего.
DynamicSharedMemoryControlLock Ожидание чтения или обновления состояния динамической общей памяти.
AutoFileLock Ожидание обновления файла postgresql.auto.conf .
ReplicationSlotAllocationLock Ожидание выделения или освобождения слота репликации.
ReplicationSlotControlLock Ожидание чтения или обновления состояния слота репликации.
CommitTsControlLock Ожидание чтения или обновления отметок времени фиксации транзакции.
CommitTsLock Ожидание чтения или обновления последнего значения, установленного для метки времени транзакции.
ReplicationOriginLock Ожидание установки, удаления или использования источника репликации.
MultiXactTruncationLock Ожидание чтения или усечения мультиплексной информации.
OldSnapshotTimeMapLock Ожидание чтения или обновления старой управляющей информации снимка.
BackendRandomLock Ожидание генерации случайного числа.
LogicalRepWorkerLock Ожидание завершения действия над работником логической репликации.
CLogTruncationLock Ожидание выполнения txid_status или обновления самого старого доступного идентификатора транзакции.
WrapLimitsVacuumLock Ожидание обновления лимитов на идентификатор транзакции и потребление мультиплексирования.
NotifyQueueTailLock Ожидание обновления лимита на хранилище сообщений уведомлений.
засор Ожидание ввода-вывода в буфере засорения (состояния транзакции).
commit_timestamp Ожидание ввода-вывода в буфере отметки времени фиксации.
субтранс Ожидание ввода-вывода буфера подтранзакции.
multixact_offset Ожидание ввода-вывода в буфере мультиплексного смещения.
многослойный элемент Ожидание ввода-вывода в буфере multixact_member.
асинхронно Ожидание ввода-вывода в асинхронном (уведомляющем) буфере.
oldserxid Ожидание ввода-вывода в буфере oldserxid.
wal_insert Ожидание вставки WAL в буфер памяти.
buffer_content Ожидание чтения или записи страницы данных в памяти.
buffer_io Ожидание ввода-вывода на странице данных.
Replication_origin Ожидание чтения или обновления хода репликации.
replication_slot_io Ожидание ввода-вывода в слоте репликации.
проц Ожидание чтения или обновления информации о блокировке быстрого доступа.
buffer_mapping Ожидание связывания блока данных с буфером в пуле буферов.
lock_manager Ожидание добавления или проверки блокировок для серверных ВМ или ожидание присоединения к группе блокировки или выхода из нее (используется параллельным запросом).
predicate_lock_manager Ожидание добавления или проверки информации о блокировке предиката.
parallel_query_dsa Ожидание блокировки распределения динамической общей памяти параллельного запроса.
тбм Ожидание блокировки общего итератора TBM.
параллельный_приложение Ожидание выбора следующего подплана во время выполнения плана параллельного добавления.
parallel_hash_join Ожидание выделения или обмена фрагментом памяти или обновления счетчиков во время выполнения плана параллельного хеширования.
Замок отношение Ожидание блокировки отношения.
продлить Ожидание расширения отношения.
замороженный Ожидание обновления pg_database . datfrozenxid и pg_database . datminmxid .
стр. Ожидание блокировки страницы отношения.
кортеж Ожидание получения блокировки кортежа.
transactionid Ожидание завершения транзакции.
virtualxid Ожидание получения блокировки виртуального xid.
спекулятивный токен Ожидание получения спекулятивной блокировки вставки.
объект Ожидание получения блокировки для объекта базы данных, не связанного с отношениями.
блокировка пользователя Ожидание получения пользовательской блокировки.
консультативный Ожидание получения консультативной блокировки пользователя.
Буферный штифт Буферный штифт Ожидание получения буфера.
Активность Архиватор Главный Ожидание в основном цикле процесса архиватора.
AutoVacuumMain Ожидание в основном цикле процесса автоочистки.
BgWriterHibernate Ожидание в фоновом процессе записи, гибернация.
BgWriterMain Ожидание в основном цикле фонового рабочего процесса фонового процесса записи.
Контрольный указатель Основной Ожидание в основном цикле процесса контрольной точки.
LogicalApplyMain Ожидание в основном цикле процесса логического применения.
LogicalLauncherMain Ожидание в основном цикле процесса логической программы запуска.
PgStatMain Ожидание в основном цикле процесса сборщика статистики.
RecoveryWalAll Ожидание WAL от потока при восстановлении.
RecoveryWalStream Ожидание, когда данные WAL недоступны из каких-либо источников (локальных, архивных или потоковых), перед повторной попыткой получить данные WAL при восстановлении.
SysLoggerMain Ожидание в основном цикле процесса системного журнала.
WalReceiverMain Ожидание в основном цикле процесса приемника WAL.
WalSenderMain Ожидание в основном цикле процесса отправителя WAL.
WalWriterMain Ожидание в основном цикле процесса записи WAL.
Клиент ClientRead Ожидание чтения данных от клиента.
ClientWrite Ожидание записи данных клиенту.
LibPQWalReceiverConnect Получатель WAL ожидает установления соединения с удаленным сервером.
LibPQWalReceiver Получить Ожидание в приемнике WAL получения данных от удаленного сервера.
SSLOpenServer Ожидание SSL при попытке подключения.
WalReceiverWaitStart Ожидание процесса запуска для отправки начальных данных для потоковой репликации.
WalSenderWaitForWAL Ожидание сброса WAL в процессе отправителя WAL.
WalSenderWriteData Ожидание каких-либо действий при обработке ответов от получателя WAL в процессе отправителя WAL.
Добавочный номер Добавочный номер Ожидание добавочного номера.
МПК BgWorkerShutdown Ожидание завершения работы фонового рабочего.
BgWorkerStartup Ожидание запуска фонового рабочего.
BtreePage Ожидание появления номера страницы, необходимого для продолжения параллельного сканирования B-дерева.
ClogGroupUpdate Ожидание, пока лидер группы обновит статус транзакции в конце транзакции.
ExecuteGather Ожидание активности дочернего процесса при выполнении узла Gather .
Хеш / Пакет / Распределение Ожидание, пока выбранный участник параллельного хеширования выделит хеш-таблицу.
Хеш / Пакет / Избрание Выбор участника параллельного хеширования для выделения хеш-таблицы.
Хеш / партия / загрузка Ожидание завершения загрузки хеш-таблицы другими участниками параллельного хеширования.
Хеш / сборка / выделение Ожидание, пока выбранный участник параллельного хеширования выделит начальную хеш-таблицу.
Хеш / сборка / выбор Выбор участника параллельного хеширования для выделения начальной хеш-таблицы.
Хеш / Сборка / Хеширование Внутренний Ожидание завершения хеширования внутреннего отношения другими участниками параллельного хеширования.
Хеш / сборка / хеширование Внешний Ожидание завершения разделения внешнего отношения другими участниками параллельного хеширования.
Хеш / GrowBatches / Распределение Ожидание, пока выбранный участник параллельного хеширования выделит дополнительные пакеты.
Хэш / GrowBatches / Решающий Выбор участника параллельного хеширования для принятия решения о будущем увеличении пакета.
Хеш / GrowBatches / Выбор Выбор участника параллельного хеширования для выделения дополнительных пакетов.
Хеш / GrowBatches / Завершение Ожидание, пока избранный участник параллельного хеширования примет решение о будущем увеличении пакета.
Hash / GrowBatches / Repartitioning Ожидание завершения перераспределения других участников параллельного хеширования.
Хеш / GrowBuckets / Выделение Ожидание, пока выбранный участник параллельного хеширования завершит выделение дополнительных сегментов.
Хэш / GrowBuckets / Выбор Выбор участника параллельного хеширования для выделения дополнительных сегментов.
Хеш / GrowBuckets / Повторная вставка Ожидание, пока другие участники параллельного хеширования завершат вставку кортежей в новые сегменты.
LogicalSyncData Ожидание отправки удаленным сервером логической репликации данных для начальной синхронизации таблицы.
LogicalSyncStateChange Ожидание изменения состояния удаленного сервера логической репликации.
MessageQueueInternal Ожидание присоединения другого процесса к общей очереди сообщений.
СообщениеQueuePutMessage Ожидание записи протокольного сообщения в общую очередь сообщений.
MessageQueueReceive Ожидание получения байтов из общей очереди сообщений.
СообщениеQueueSend Ожидание отправки байтов в общую очередь сообщений.
ParallelBitmapScan Ожидание инициализации параллельного сканирования растрового изображения.
ParallelCreateIndexScan Ожидание, пока параллельные рабочие CREATE INDEX завершат сканирование кучи.
Параллельная отделка Ожидание завершения вычислений параллельными исполнителями.
ProcArrayGroupUpdate Ожидание, пока лидер группы очистит идентификатор транзакции в конце транзакции.
ReplicationOriginDrop Ожидание перехода источника репликации в неактивное состояние для удаления.
ReplicationSlotDrop Ожидание, когда слот репликации станет неактивным для удаления.
SafeSnapshot Ожидание моментального снимка транзакции READ ONLY DEFERRABLE .
SyncRep Ожидание подтверждения от удаленного сервера во время синхронной репликации.
Тайм-аут BaseBackupThrottle Ожидание во время базового резервного копирования при регулировании активности.
PgSleep Ожидание в процессе, который вызвал pg_sleep .
RecoveryApplyDelay Ожидание применения WAL при восстановлении из-за задержки.
IO BufFileRead Ожидание чтения из буферизованного файла.
BufFileWrite Ожидание записи в буферный файл.
ControlFileRead Ожидание чтения из управляющего файла.
ControlFileSync Ожидание передачи контрольного файла в долговременное хранилище.
ControlFileSyncUpdate Ожидание обновления контрольного файла для доступа к надежному хранилищу.
ControlFileWrite Ожидание записи в управляющий файл.
ControlFileWriteUpdate Ожидание записи для обновления управляющего файла.
CopyFileRead Ожидание чтения во время операции копирования файла.
CopyFileWrite Ожидание записи во время операции копирования файла.
DataFileExtend Ожидание расширения файла данных отношения.
DataFileFlush Ожидание передачи файла данных отношения в долговременное хранилище.
DataFileImmediateSync Ожидание немедленной синхронизации файла данных отношения с долговременным хранилищем.
DataFilePrefetch Ожидание асинхронной предварительной выборки из файла данных отношения.
DataFileRead Ожидание чтения из файла данных отношения.
DataFileSync Ожидание изменений в файле данных отношения для достижения долговременного хранилища.
DataFileTruncate Ожидание усечения файла данных отношения.
DataFileWrite Ожидание записи в файл данных отношения.
DSMFillZeroWrite Ожидание записи нулевых байтов в файл поддержки динамической общей памяти.
LockFileAddToDataDirRead Ожидание чтения при добавлении строки в файл блокировки каталога данных.
LockFileAddToDataDirSync Ожидание передачи данных в долговременное хранилище при добавлении строки в файл блокировки каталога данных.
LockFileAddToDataDirWrite Ожидание записи при добавлении строки в файл блокировки каталога данных.
LockFileCreateRead Ожидание чтения при создании файла блокировки каталога данных.
LockFileCreateSync Ожидание передачи данных в долговременное хранилище при создании файла блокировки каталога данных.
LockFileCreateWrite Ожидание записи при создании файла блокировки каталога данных.
LockFileReCheckDataDirRead Ожидание чтения во время перепроверки файла блокировки каталога данных.
LogicalRewriteCheckpointSync Ожидание отображения сопоставлений логической перезаписи для достижения долговременного хранилища во время контрольной точки.
LogicalRewriteMappingSync Ожидание передачи данных сопоставления в долговременное хранилище во время логической перезаписи.
Логическая Перезапись Отображение Записать Ожидание записи данных отображения во время логической перезаписи.
LogicalRewriteSync Ожидание отображения сопоставлений логической перезаписи для достижения надежного хранилища.
LogicalRewriteTruncate Ожидание усечения данных отображения во время логической перезаписи.
Логическая перезапись Запись Ожидание записи отображений логической перезаписи.
RelationMapRead Ожидание чтения файла карты отношений.
RelationMapSync Ожидание, пока файл карты отношений достигнет надежного хранилища.
RelationMapWrite Ожидание записи в файл карты отношений.
ReorderBufferRead Ожидание чтения во время управления буфером изменения порядка.
ReorderBufferWrite Ожидание записи во время управления буфером изменения порядка.
ReorderLogicalMappingRead Ожидание чтения логического отображения во время управления буфером переупорядочения.
ReplicationSlotRead Ожидание чтения из файла управления слотом репликации.
ReplicationSlotRestoreSync Ожидание, пока файл управления слотом репликации достигнет долговременного хранилища, при его восстановлении в памяти.
ReplicationSlotSync Ожидание передачи файла управления слотом репликации в долговременное хранилище.
ReplicationSlotWrite Ожидание записи в файл управления слотом репликации.
SLRUFlushSync Ожидание поступления данных SLRU в долговременное хранилище во время завершения работы контрольной точки или базы данных.
SLRUR Цена Ожидание чтения страницы SLRU.
SLRUSync Ожидание передачи данных SLRU в долговременное хранилище после записи страницы.
SLRU Запись Ожидание записи страницы SLRU.
SnapbuildRead Ожидание чтения сериализованного снимка исторического каталога.
SnapbuildSync Ожидание передачи сериализованного исторического моментального снимка каталога в долговременное хранилище.
SnapbuildWrite Ожидание записи сериализованного снимка исторического каталога.
ХронологияHistoryFileSync Ожидание получения файла хронологии шкалы времени, полученного посредством потоковой репликации, для достижения надежного хранилища.
Хронология История Файл Запись Ожидание записи файла хронологии шкалы времени, полученного посредством потоковой репликации.
ХронологияИсторияЧитать Ожидание чтения файла истории временной шкалы.
TimelineHistorySync Ожидание, пока вновь созданный файл истории шкалы времени попадет в надежное хранилище.
Хронология История Запись Ожидание записи вновь созданного файла истории шкалы времени.
Двухфазное чтение Ожидание чтения файла двухфазного состояния.
TwophaseFileSync Ожидание передачи файла двухфазного состояния в долговременное хранилище.
Двухфазная запись файла Ожидание записи файла двухфазного состояния.
WALBootstrapSync Ожидание, пока WAL достигнет долговременного хранилища во время начальной загрузки.
WALBootstrap Запись Ожидание записи страницы WAL во время начальной загрузки.
WALCopyRead Ожидание чтения при создании нового сегмента WAL путем копирования существующего.
WALCopySync Ожидание нового сегмента WAL, созданного путем копирования существующего, для достижения долговременного хранилища.
WALCopyWrite Ожидание записи при создании нового сегмента WAL путем копирования существующего.
WALInitSync Ожидание поступления нового инициализированного файла WAL в долговременное хранилище.
WALInitWrite Ожидание записи при инициализации нового файла WAL.
WALRead Ожидание чтения из файла WAL.
WALSenderTimelineHistory Читать Ожидание чтения из файла истории шкалы времени во время выполнения команды шкалы времени walsender.
WALSyncMethodAssign Ожидание передачи данных в долговременное хранилище при назначении метода синхронизации WAL.
WAL Запись Ожидание записи в файл WAL.

Преодоление узких мест между разработчиками дизайна, внешнего интерфейса и внутреннего интерфейса

(перемещено сюда)

Во время моей работы в качестве внешнего разработчика в Solid State Group я видел несколько сценариев, которые блокируют работу для определенных областей команды разработчиков. Будь то дизайн, реализация разметки / стиля, технический интерфейс или бэкэнд, в тот или иной момент они достигают точки, когда они полагаются на кого-то, чтобы выполнить некоторую работу, прежде чем они смогут приступить к своей задаче.

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

Некоторые сценарии

Чтобы установить сцену, вот несколько примеров того, о чем я говорю:

  • Интерфейсный разработчик хочет начать создание функции, но API еще не готов.
  • Бэкэнд-разработчик хочет начать работу над функцией, но не знает, какие данные нужны фронтенду.
  • Внутренний разработчик вносит некоторые изменения (например, удаляет некоторые поля из DTO), но не уверен, нарушат ли эти изменения интерфейс.
  • Технический разработчик внешнего интерфейса (который обычно не занимается разметкой / CSS) разрабатывает функцию, но не знает, какие разметки / стили доступны для использования.
  • Разработчик внешнего интерфейса на основе дизайна (который занимается только разметкой / CSS) готов копировать проекты, но техническая реализация внешнего интерфейса или данные не готовы.
  • Дизайнер корректирует разметку, но не знает, как перейти на нужную страницу.
  • Разработчик проверяет конкретную проблему или функцию сценария, но должен пройти через утомительное состояние приложения (подключение и т. Д.), Чтобы добраться до нужного места.
  • Разработчику, впервые участвующему в проекте, требуется адаптация от другого разработчика, чтобы понять кодовую базу.

Вот несколько решений …

Разработка с помощью pact-js

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

Pact сам по себе является инструментом тестирования контрактов. Потребитель данных (в данном случае интерфейс) определяет, что он хочет от поставщика данных, и публикует это брокеру перед выполнением какой-либо работы. Разработчики поставщика (в данном случае API) ссылаются на это и используют его для разработки.

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

В результате мы часто обнаруживали, что можем разработать большой кусок приложения только тогда, чтобы переключиться с localhost на API разработки, чтобы начать интеграционное тестирование. В это время и фронтенд-разработчики, и бэкэнд-разработчики не ждут друг от друга, чтобы завершить работу, прежде чем они узнают, где они находятся.

До Пакта

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

После Пакта!

  • Файлы Pact используются в качестве единственного источника истины, могут быть написаны тесты, чтобы гарантировать, что изменения в полезной нагрузке API не повлияют на интерфейс.
  • Интерфейсный разработчик может использовать Pact JSON в качестве фиктивного сервера, это действительно полезно для разработки, когда вы не можете получить доступ к API или когда конечные точки не существуют / еще не были изменены.Это намного лучше, чем работать со статической переменной JSON, потому что, когда API готов, вы можете просто переключить конечную точку с localhost.

Разработка на основе компонентов и страница руководства по стилю

Недавно в наших веб-проектах и ​​мобильных проектах мы создали страницу, содержащую все компоненты, представленные в приложении. В частности, я обнаружил, что это действительно хорошо работает с React и React Native.

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

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

До страниц руководства по стилю

  • Технические разработчики должны помнить, какие классы и компоненты CSS соответствуют определенным проектам.
  • Нетехнические разработчики увязли бы, пытаясь найти, где реализовать разметку.
  • Чтобы внести изменения, разработчикам придется перемещаться по соответствующей странице, что часто включает утомительный рабочий процесс, такой как регистрация / подключение пользователей.

После страниц руководства по стилю!

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

Добавить конечные точки разрыва

Возможность эффективно тестировать рабочие процессы и очищать данные экономит много времени и избавляет от необходимости полагаться на внутренних разработчиков для очистки баз данных.

Конечные точки до разборки

  • Среды разработки будут заполнены тестовыми данными мусора.
  • Тестировщикам или разработчикам, возможно, придется полагаться на очистку данных, прежде чем они смогут продолжить тестирование.

После разрыва конечных точек!

  • Рабочие процессы можно тестировать снова и снова и очищать без каких-либо последствий.

Разработайте свое приложение так, чтобы вы могли моделировать сложные сценарии

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

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

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

Флаги перед симуляцией

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

Флаги после моделирования

  • Демонстрация / воспроизводимость сценариев занимает намного меньше времени.
  • Ошибки и функции в малоизвестных частях вашего приложения стало намного проще разрабатывать.

Стандартизация структуры и настройки проекта

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

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

Если вам интересно, вот простой пример того, на чем основан наш интерфейсный проект.

До Базовые схемы

  • Нам придется потратить намного больше времени на первоначальную настройку проекта и инфраструктуру.
  • Работа над новыми проектами иногда была очень болезненной.
  • Новым разработчикам требовалась дополнительная поддержка существующей команды для каждого проекта.

После макетов базы!

  • Разработчики могут начать разработку проекта намного быстрее, и они знают, с чего начать, основываясь на предыдущих проектах.
  • Предыдущие ошибки в проектах уже учтены на основе предыдущих итераций.
  • Разработчикам стало проще переключаться на работу в проектах и ​​отказываться от них, при этом не слишком полагаясь на нескольких избранных.

Надеюсь, люди сочли некоторые из этих советов полезными! Сообщите мне, какие методы вы используете, чтобы лучше работать вместе.

Контекст Spark остановлен во время ожидания серверной части

Когда Spark отправляет задание на выполнение для пряжи, возникают следующие проблемы:

  ОШИБКА TransportClient: 233 - Не удалось отправить RPC 7036352720568735489 на /172.16.96.152:34091: java.nio.channels.ClosedChannelException
java.nio.channels.ClosedChannelException
в io.netty.channel.AbstractChannel $ AbstractUnsafe.write (...) (Неизвестный источник)
2019-05-23 23:56:02 ОШИБКА YarnSchedulerBackend $ YarnSchedulerEndpoint: 91 - Отправка RequestExecutors (0,0, Map (), Set ()) в AM завершилась неудачно
java.io.IOException: не удалось отправить RPC 7036352720568735489 на /172.16.96.152:34091: java.nio.channels.ClosedChannelException
в org.apache.spark.network.client.TransportClient.lambda $ sendRpc $ 2 (TransportClient.java:237)
в io.netty.util.concurrent.DefaultPromise.notifyListener0 (DefaultPromise.java: 507)
в io.netty.util.concurrent.DefaultPromise.notifyListenersNow (DefaultPromise.java:481)
в io.netty.util.concurrent.DefaultPromise.access $ 000 (DefaultPromise.java:34)
в io.netty.util.concurrent.DefaultPromise $ 1.run (DefaultPromise.java:431)
в io.netty.util.concurrent.AbstractEventExecutor.safeExecute (AbstractEventExecutor.java:163)
в io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks (SingleThreadEventExecutor.java:403)
в io.netty.channel.nio.NioEventLoop.run (NioEventLoop.java: 463)
в io.netty.util.concurrent.SingleThreadEventExecutor $ 5.run (SingleThreadEventExecutor.java:858)
в io.netty.util.concurrent.DefaultThreadFactory $ DefaultRunnableDecorator.run (DefaultThreadFactory.java:138)
в java.lang.Thread.run (Thread.java:748)
Вызвано: java.nio.channels.ClosedChannelException
в io.netty.channel.AbstractChannel $ AbstractUnsafe.write (...) (Неизвестный источник)
2019-05-23 23:56:03 ОШИБКА SparkContext: 91 - Ошибка инициализации SparkContext.
java.lang.IllegalStateException: контекст Spark остановлен во время ожидания серверной части
в орг.apache.spark.scheduler.TaskSchedulerImpl.waitBackendReady (TaskSchedulerImpl.scala: 669)
в org.apache.spark.scheduler.TaskSchedulerImpl.postStartHook (TaskSchedulerImpl.scala: 177)
в org.apache.spark.SparkContext.  (SparkContext.scala: 559)
в org.apache.spark.SparkContext $ .getOrCreate (SparkContext.scala: 2493)
в org.apache.spark.sql.SparkSession $ Builder $$ anonfun $ 7.apply (SparkSession.scala: 933)
в org.apache.spark.sql.SparkSession $ Builder $$ anonfun $ 7.apply (SparkSession.scala: 924)
в scala.Option.getOrElse (Option.scala: 121)
в org.apache.spark.sql.SparkSession $ Builder.getOrCreate (SparkSession.scala: 924)
в org.apache.spark.examples.SparkPi $ .main (SparkPi.scala: 31)
в org.apache.spark.examples.SparkPi.main (SparkPi.scala)
в sun.reflect.NativeMethodAccessorImpl.invoke0 (собственный метод)
в sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
в sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
в java.lang.reflect.Method.invoke (Method.java:498)
в org.apache.spark.deploy.JavaMainApplication.start (SparkApplication.scala: 52)
в org.apache.spark.deploy.SparkSubmit $ .org $ apache $ spark $ deploy $ SparkSubmit $$ runMain (SparkSubmit.scala: 894)
в org.apache.spark.deploy.SparkSubmit $ .doRunMain $ 1 (SparkSubmit.scala: 198)
в org.apache.spark.deploy.SparkSubmit $ .submit (SparkSubmit.scala: 228)
в org.apache.spark.deploy.SparkSubmit $ .main (SparkSubmit.scala: 137)
в org.apache.spark.deploy.SparkSubmit.main (SparkSubmit.scala)
2019-05-23 23:56:03 ИНФОРМАЦИЯ SparkContext: 54 - SparkContext уже остановлен.Исключение в потоке "main" java.lang.IllegalStateException: контекст Spark остановлен во время ожидания серверной части
в org.apache.spark.scheduler.TaskSchedulerImpl.waitBackendReady (TaskSchedulerImpl.scala: 669)
в org.apache.spark.scheduler.TaskSchedulerImpl.postStartHook (TaskSchedulerImpl.scala: 177)
в org.apache.spark.SparkContext.  (SparkContext.scala: 559)
в org.apache.spark.SparkContext $ .getOrCreate (SparkContext.scala: 2493)
в org.apache.spark.sql.SparkSession $ Builder $$ anonfun $ 7.apply (SparkSession.scala: 933)
в org.apache.spark.sql.SparkSession $ Builder $$ anonfun $ 7.apply (SparkSession.scala: 924)
в scala.Option.getOrElse (Option.scala: 121)
в org.apache.spark.sql.SparkSession $ Builder.getOrCreate (SparkSession.scala: 924)
в org.apache.spark.examples.SparkPi $ .main (SparkPi.scala: 31)
в org.apache.spark.examples.SparkPi.main (SparkPi.scala)
в sun.reflect.NativeMethodAccessorImpl.invoke0 (собственный метод)
в sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62)
на солнце. отражение.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43)
в java.lang.reflect.Method.invoke (Method.java:498)
в org.apache.spark.deploy.JavaMainApplication.start (SparkApplication.scala: 52)
в org.apache.spark.deploy.SparkSubmit $ .org $ apache $ spark $ deploy $ SparkSubmit $$ runMain (SparkSubmit.scala: 894)
в org.apache.spark.deploy.SparkSubmit $ .doRunMain $ 1 (SparkSubmit.scala: 198)
в org.apache.spark.deploy.SparkSubmit $ .submit (SparkSubmit.scala: 228)
в org.apache.spark.deploy.SparkSubmit $.main (SparkSubmit.scala: 137)
в org.apache.spark.deploy.SparkSubmit.main (SparkSubmit.scala)
2019-05-23 23:56:03 INFO SchedulerExtensionServices: 54 - Остановка SchedulerExtensionServices
(serviceOption = Нет,
 services = Список (),
 началось = ложь)
2019-05-23 23:56:03 ERROR Utils: 91 - Неперехваченное исключение в мониторе состояния приложения thread Yarn
org.apache.spark.SparkException: исключение, созданное в awaitResult:
в org.apache.spark.util.ThreadUtils $ .awaitResult (ThreadUtils.scala: 205)
на org.apache.spark.rpc.RpcTimeout.awaitResult (RpcTimeout.scala: 75)
в org.apache.spark.scheduler.cluster.CoarseGrainedSchedulerBackend.requestTotalExecutors (CoarseGrainedSchedulerBackend.scala: 567)
в org.apache.spark.scheduler.cluster.YarnSchedulerBackend.stop (YarnSchedulerBackend.scala: 95)
в org.apache.spark.scheduler.cluster.YarnClientSchedulerBackend.stop (YarnClientSchedulerBackend.scala: 155)
в org.apache.spark.scheduler.TaskSchedulerImpl.stop (TaskSchedulerImpl.scala: 508)
в org.apache.spark.scheduler.DAGScheduler.стоп (DAGScheduler.scala: 1755)
в org.apache.spark.SparkContext $$ anonfun $ stop $ 8. примените $ mcV $ sp (SparkContext.scala: 1931)
в org.apache.spark.util.Utils $ .tryLogNonFatalError (Utils.scala: 1360)
в org.apache.spark.SparkContext.stop (SparkContext.scala: 1930)
в org.apache.spark.scheduler.cluster.YarnClientSchedulerBackend $ MonitorThread.run (YarnClientSchedulerBackend.scala: 112)
Вызвано: java.io.IOException: не удалось отправить RPC 7036352720568735489 на /172.16.96.152:34091: java.nio.channels.ClosedChannelException
в орг.apache.spark.network.client.TransportClient.lambda $ sendRpc $ 2 (TransportClient.java:237)
в io.netty.util.concurrent.DefaultPromise.notifyListener0 (DefaultPromise.java:507)
в io.netty.util.concurrent.DefaultPromise.notifyListenersNow (DefaultPromise.java:481)
в io.netty.util.concurrent.DefaultPromise.access $ 000 (DefaultPromise.java:34)
в io.netty.util.concurrent.DefaultPromise $ 1.run (DefaultPromise.java:431)
в io.netty.util.concurrent.AbstractEventExecutor.safeExecute (AbstractEventExecutor.java:163)
в io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks (SingleThreadEventExecutor.java:403)
в io.netty.channel.nio.NioEventLoop.run (NioEventLoop.java:463)
в io.netty.util.concurrent.SingleThreadEventExecutor $ 5.run (SingleThreadEventExecutor.java:858)
в io.netty.util.concurrent.DefaultThreadFactory $ DefaultRunnableDecorator.run (DefaultThreadFactory.java:138)
в java.lang.Thread.run (Thread.java:748)
Вызвано: java.nio.channels.ClosedChannelException
на io.netty.channel.AbstractChannel $ AbstractUnsafe.написать (...) (Неизвестный источник)
2019-05-23 23:56:03 ИНФОРМАЦИЯ DiskBlockManager: 54 - Вызывается ловушка завершения работы
2019-05-23 23:56:03 ИНФОРМАЦИЯ MapOutputTrackerMasterEndpoint: 54 - MapOutputTrackerMasterEndpoint остановлен!
2019-05-23 23:56:03 INFO ShutdownHookManager: 54 - Вызывается ловушка отключения
2019-05-23 23:56:03 ИНФОРМАЦИЯ MemoryStore: 54 - MemoryStore очищен
2019-05-23 23:56:03 INFO ShutdownHookManager: 54 - Удаление каталога / tmp / spark-4193ed00-d3eb-4dfa-8969-7038029679e7
2019-05-23 23:56:03 ИНФОРМАЦИЯ BlockManager: 54 - BlockManager остановлен
2019-05-23 23:56:03 INFO ShutdownHookManager: 54 - Удаление каталога / tmp / spark-a518bdb0-c1c7-42b3-98ea-5c234a8da608
2019-05-23 23:56:03 INFO ShutdownHookManager: 54 - Удаление каталога / tmp / spark-4193ed00-d3eb-4dfa-8969-7038029679e7 / userFiles-205232dc-74cd-487c-adb6-51a318cab014
  

Решение: Изменить узел пряжи.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *