Привет, Хабр! Вы наверное тоже с любопытством наблюдаете за «эпопеей Американского Дикого Запада по распределению земельных участков — доскачи первым и воткни флаг чтобы застолбить» или возможно даже участвуете в ней. Точнее в ее современном варианте — успей первым подать заявление на госуслугах для получения денежных средств на детей или получи пропуск на выход из дома. Смотря на все это, хочется поделиться опытом нашей команды по тестированию и участию в подготовке регионального портала услуг к предоставлению услуги «Запись в первый класс». Она тоже очень похожа на хабраэффект и, думаю, была близка к тому, что пару дней назад проиcходило с федеральным порталом gosuslugi.ru, но на региональном масштабе.
Мы прошли этот челлендж в г. Хабаровске в январе этого года и недавно участвовали в подготовке подобной услуги по выдаче охотничьих разрешений в другом регионе. Ниже — немного опыта, который даст вам возможность посмотреть на вопрос подготовки работы региональных порталов госуслуг к пиковым периодам с другой стороны.
А для начала — фото черного автобуса, трое суток круглосуточно дежурившего у школы, в котором автор этих строк подтверждал свою очередь среди родителей три года назад. У нас хотя бы родители соорганизовались. Дежурить зимой в Хабаровске при -30 градусов — то ещё удовольствие.
В процессе подготовки к пиковому проведению записи в первый класс работали несколько команд, т.к. в неё вовлечены, так или иначе: оператор ЦОД, эксплуатант и разработчик информационной системы регионального портала, эксплуатант и разработчик интегрированной информационной системы образования, техническая поддержка пользователей регионального портала. Мы в iondv выполняем последнюю задачу, независимо отслеживая работоспособность портала и поддерживая пользователей.
Наша роль в подготовке – организация тестирования и рекомендаций по конфигурациям кеширования в nginx, ну и мы готовили инструкцию для пользователей с рекомендованным «поведением».
Проблема в услугах, подобных «записи в 1-й класс» в интегральном показателе нагрузки (или вероятности запросов) стремящемся к «дельта функции» (?-функция, функция Дирака) – хорошо видна на графиках в виде пики. В этот момент происходит кратный рост обращений в краткий период времени.
Наш опыт говорит, что основная задача подготовки вовсе не в том, чтобы увеличить ресурсы. Задача — максимально уменьшить потенциальное кол-во запросов в секунду, растянуть их на какой-то период и подготовить систему к оставшейся нагрузке. При этом надо найти и ускорить узкие места — это даст самый большой эффект в соответствии с принципами теории ограниченных систем (принципы Голдратта). И иначе именно самое узкое место откажет. Вся система должна работать от него: принцип «барабан-буфер-веревка».
Физически невозможно в 10-ти минутных песочных часах просыпать весь объем за 1 минуту – очевидно, что они разрушатся. Аналогично и для предоставления услуги. Никого не удивляет — когда перед МФЦ очередь и скандалы на получение услуги, но всех удивляет — почему портал лег.
Есть разные модели поведения обработки нагрузки из теории массового обслуживания:
Целесообразность где-то посередине. Ведь для услуги с ограниченным ресурсом, предоставляемой регионом или государством, важна не только скорость, но и, в первую очередь, сохранение социальной справедливости – т.е. равные условия у всех. В тоже время – если пользователь не получил то, что ему нужно, он инициирует новый запрос. В этом случае запросы растут лавиной, формируя модель атаки «эффект собачьей стаи» (dog-pile effect, cache stampede, hit miss storm) – пользователь запрос уже отменил и инициировал новый, а предыдущий ещё в очереди стоит на обработку.
Этот процесс усиливает то, что в подаче участвуют целыми семьями — папа и мама заполняют заявления одновременно, и часто подают заявления несколько раз для надежности. А кроме того, часто ещё и в нескольких вкладках и нескольких браузерах. Поэтому ожидаемую пиковую нагрузку обычно имеет смысл умножать в 2-3 раза, от количества тех, кто фактически подает заявления в подобных услугах.
Мы сделали расчет ожидаемого количества заявителей на основе комбинации данных об общем количестве поданных заявлений в прошлому году и поминутным данным по другим регионам. Обычно пик заявлений приходится на 5-10 минуты, в том числе потому что первые три-пять минут порталы почти не отвечают, а позже пользователи заполняют форму от 1 до 5 минут (не удивляйтесь многие даже в таких «нервных» условиях заполняют с телефона).
Примерная модель расчета для условных 1000 заявлений в час такая:
По факту первая подача произошла через одну минуту и 45 секунд, а пик заявлений шёл с 4 минуты.
Для уменьшения нагрузки на ЕСИА и на систему от генерирования сессий авторизации в инструкции предложили пользователям авторизоваться заранее и удлинили время жизни сессии. По факту 50% были авторизованы за 1 час, а ~90% за полчаса. Мы сталкивались ранее с тем, что пользователи начинали заходить на портал за 10 минут до начала услуги — и авторизация начинала работать нестабильно. Сложно сказать почему. Возможно причина в том, что когда в Москве проводятся технические работы ночью — у нас в Хабаровске как раза начало рабочего дня.
Убрать «дельта функцию» при перезагрузке формы в 00:00 часов невозможно. Весь смысл этой процедуры в том, что услуга появляется в заданное время. Но можно постараться уменьшить кол-во запросов браузера на всех ожидаемых маршрутах пользователей и тем самым оставить нагрузку на систему только от необходимых — форма, динамические справочники и отправка заявления.
Сами настройки nginx достаточно стандартны. Здесь важнее подобрать ограничения, которые может выдержать система. Их и подобрать — т.е. начинать ставить в очередь запросы, когда сервер ожидаемо подойдет к границе своих возможностей.
Ну и самое важное — мы принудительно проставили кеширование (proxy_cache) и увеличили время жизни данных «expires» в nginx для всех путей статики и где возможно динамических страниц, в которых нет сессий. Это кстати частая ошибка при кешировании — записывать в кеш данные (иногда даже статику), в которых сохранена чужая сессия, выход обычно удалять эти куки из заголовков, если сервер не может разделять типы данных.
В браузере для пользователя это выглядит как обновление страниц из файлов загруженных с диска или памяти. Но даже когда у пользователя они получаются с сервера — они берутся из кеша nginx. Сами справочники конечно же в самой системе закешированы.
Это уменьшило кол-во потенциальных запросов с 89-запросов до 14 и объем с 2,1 Мб (для 1000 обновивших страницу пользователей это потенциальный пик 4-8Гбит/с) до 38Кб (мы все помним про webpack, но для entrprise платформ это не всегда легко сделать). По результатам прохождения – надо ещё было кешировать не только в системе, но и в nginx часть справочников с формы и динамических классификаторов не используемых в пиковый момент и проставлять для них принудительно время жизни. А при росте нагрузки вообще имеет смысл выставлять на главную полностью статичную страницу с маршрутизацией пользователей на нужную услугу или делать отдельный ресурс для услуги.
Для уменьшения нагрузки на отправку, были отключены черновики и автоматическое заполнение данных на ребёнка. Скорость ввода данных у всех пользователей разная, что исключает появление полностью готовой для отправки формы и позволяет избежать дельта функции на отправку заявлений — всю 1000 в одну минуту. При этом социальная справедливость сохраняется, хотя, конечно, появляются жалобы.
Оптимизацию самой системы описывать не буду — при нагрузочном тестировании выявлялись узкие места — в основном в запросах к СУБД и оптимизировались индексы и сами запросы.
Наверное самой главной оптимизацией является упрощение формы. Что сильнее всего влияет на скорость при реализации в форме?
Ну и перейдем собственно к тестам.
Тестирование делали через puppeteer – путем эмуляции действий пользователя в браузере Crominium. Янедекс.танк и JMeter отбиваются защитой от атак, т. к. генерируют множество однотипных запросов. Кроме того эти тесты слабо совпадают с профилем реальных запросов при изменении поведения системы под нагрузкой. Кроме того сервера кешируют запросы, а часть процессов в них воспроизвести сложно (например авторизацию). Кстати, с одного из семинаров devDV у нас есть выступление с презентацией по вопросу использования puppeteer для тестирования, в т.ч. нагрузочного, ссылка на видео.
Для начала мы составили профиль поведения пользователя и процедуру разбили на ключевые этапы:
На каждый из этапов мы делали отдельный тест.
В прошлом году на этапе авторизации в ЕСИА были сложности, но протестировать его полномасштабно затруднительно. Система внешняя, срабатывает защита от атак и баны на авторизации. Тем не менее можно сформировать профиль тестов для проверки именно узких мест тестируемой системы — обычно это количество одновременно авторизованных сессий и плановые значения авторизаций в минуту, которые можно регулировать рекомендациями.
В тесте важна обертка для организации нескольких потоков, мы используем 'puppeteer-cluster'. Но обычно более сложным является обработка исключений и изменения поведения портала под нагрузкой — часто выявляются элементы верстки, которые всплывают по два раза. Или элементы не проявляются, если какие-то данные загрузились не так, как ожидалось. Это все те ошибки, которые увидят и пользователи и перезагрузят страницу — а значит создадут дополнительную нагрузку. Здесь два пути — реализовывать в тесте обработку исключений. Или дорабатывать портал.
Сам тест простой. Ниже фрагмент от клика на кнопку «Вход» на портале услуг до ввода данных в ЕСИА.
Проверка обновления формы заявления в ожидании пользователями «открытия записи». Тест на перезагрузку по сути одношаговый, но важно проверять типы возвращаемых ошибок – сетевая это проблема, ошибка nginx, ошибка сервера и соответствует ли форма критериям. А сложность в том, чтобы сгенерировать максимальный объем запросов за наименьшее кол-во времени и не попасть под ограничения защиты (впрочем на время тестов её можно изменить, с другой стороны, это тоже проверка настроек сетевой и серверной инфраструктуры и WAF).
Такие тесты на puppeteer требует достаточно много ресурсов для работы. Де факто получилось, что нужно не менее 2-х ядер против 1-го ядра фронтенд подсистемы и очень широкий канал. Но при аренде их в облаке — это вполне доступно. Мы пользовались Яндекс.облаком.
В тесте сначала реализуется авторизация в ЕСИА для каждого потока отдельно. После этого запускается на каждый поток отдельный браузер и в рамках одной инстанции проводится заданное кол-во обновлений. После этого инстанция перезапускается. Сама проверка может включать типовой путь, например главная страница, форма услуги. Но чаще достаточно только полного обновления услуги и проверки необходимого справочника, что услугу можно подать — все как в инструкции для пользователей.
Фрагмент теста по открытию главной и обновлению страницы.
Для нагрузки отправкой заявлений реализовывался весь цикл проверки – с перезагрузкой формы и проверкой ввода всех данных.
Фрагмент.
Кстати, прохождение теста можно ускорить, если вводить все данные не из из puppeteer конструкцией await page.type, а перенести эту логику в сам браузер. Но тогда возрастает сложность отлавливания ошибок. Например так
Во время тестов мы обеспечивали несколько тысяч авторизаций ЕСИА и около 16 тыс. отправленных заявлений. Как проводилось восстановление продуктивной информационной системы образования после такого кол-ва заявлений — даже не спрашивайте. Это совсем другая история.
Главный видимый результат этого процесса оказался в том, что местным СМИ в дни записи в первый класс теперь было скучно. Услуга ушла из медийной области.
Параллельно мы сделали сводную панель мониторинга, для проверки работоспособности формы на основе Grafana: количества заявлений, количества звонков, данные яндекс.метрики и т.д. Но эту тему оставим для следующего раза.
Ну и я хотел бы поздравить всех, кто связан с темой улучшения качества предоставления государственных и муниципальных услуг в электронном виде. Эта бесконечная работа по подготовкам не прошли зря — ведь за апрель и май количества поданных заявлений увеличились в разы.
Мы прошли этот челлендж в г. Хабаровске в январе этого года и недавно участвовали в подготовке подобной услуги по выдаче охотничьих разрешений в другом регионе. Ниже — немного опыта, который даст вам возможность посмотреть на вопрос подготовки работы региональных порталов госуслуг к пиковым периодам с другой стороны.
А для начала — фото черного автобуса, трое суток круглосуточно дежурившего у школы, в котором автор этих строк подтверждал свою очередь среди родителей три года назад. У нас хотя бы родители соорганизовались. Дежурить зимой в Хабаровске при -30 градусов — то ещё удовольствие.
В процессе подготовки к пиковому проведению записи в первый класс работали несколько команд, т.к. в неё вовлечены, так или иначе: оператор ЦОД, эксплуатант и разработчик информационной системы регионального портала, эксплуатант и разработчик интегрированной информационной системы образования, техническая поддержка пользователей регионального портала. Мы в iondv выполняем последнюю задачу, независимо отслеживая работоспособность портала и поддерживая пользователей.
Наша роль в подготовке – организация тестирования и рекомендаций по конфигурациям кеширования в nginx, ну и мы готовили инструкцию для пользователей с рекомендованным «поведением».
Для тех кто не знает о проблеме записи в 1-й класс
Во многих больших городах существует несколько муниципальных или региональных школ, в которые сложно попасть. Причины разные. Некоторые школы стабильно выпускают детей с высокими результатами по ЕГЭ, другие используют особые программы образования, третьи дают какие-то высоко ценимые родителями навыки (например английский язык или упор на математику, а иногда — особо известный хороший учитель начальных классов), четвертые находятся в районах, где кол-во домов приписанных к школе велико, школа сама небольшая, а другая ближайшая находится за большой магистралью или в получасе ходьбы ребёнком. При том, чтобы ребёнок был зачислен в эту школу, он должен быть зарегистрирован в домах, которые отнесены к школе – это не останавливает родителей, они готовых покупать возможность прописки или даже квартиры. Поэтому ажиотаж подогревается ещё и уже произведенными затратами времени и денег.
Во многих регионах запись в 1-й класс организуют в электронной форме параллельно с записью в очной, но смещённой по времени. Например в Хабаровске в этом году электронная запись открылась в 0:00 минут, а прием в в школах открылся в 10:00 26 января. Очевидно, что подаваться очно – это уже изначально оказаться в конце очереди. В прошлом году запись стартовала одновременно в 10:00 утра, но это не решало проблему очередей — родители всё равно дежурили, опасаясь что портал ляжет или что-то сломается.
Обычно запись в традиционной форме осуществлялась в виде круглосуточных дежурств и ведением очередей. По ссылке можно посмотреть опыт г. Хабаровска в 2017 г. с фотографиями и отзывами родителей. Иногда такая ситуация даже провоцировала драки родителей в очередях, бывало родители организовывали ЧОП, чтобы отсечь параллельные очереди. А некоторые директора вызывали ОМОН для наведения порядка перед школой.
Во многих регионах запись в 1-й класс организуют в электронной форме параллельно с записью в очной, но смещённой по времени. Например в Хабаровске в этом году электронная запись открылась в 0:00 минут, а прием в в школах открылся в 10:00 26 января. Очевидно, что подаваться очно – это уже изначально оказаться в конце очереди. В прошлом году запись стартовала одновременно в 10:00 утра, но это не решало проблему очередей — родители всё равно дежурили, опасаясь что портал ляжет или что-то сломается.
Обычно запись в традиционной форме осуществлялась в виде круглосуточных дежурств и ведением очередей. По ссылке можно посмотреть опыт г. Хабаровска в 2017 г. с фотографиями и отзывами родителей. Иногда такая ситуация даже провоцировала драки родителей в очередях, бывало родители организовывали ЧОП, чтобы отсечь параллельные очереди. А некоторые директора вызывали ОМОН для наведения порядка перед школой.
Услуга на востребованный ресурс как техническая задача
Проблема в услугах, подобных «записи в 1-й класс» в интегральном показателе нагрузки (или вероятности запросов) стремящемся к «дельта функции» (?-функция, функция Дирака) – хорошо видна на графиках в виде пики. В этот момент происходит кратный рост обращений в краткий период времени.
Наш опыт говорит, что основная задача подготовки вовсе не в том, чтобы увеличить ресурсы. Задача — максимально уменьшить потенциальное кол-во запросов в секунду, растянуть их на какой-то период и подготовить систему к оставшейся нагрузке. При этом надо найти и ускорить узкие места — это даст самый большой эффект в соответствии с принципами теории ограниченных систем (принципы Голдратта). И иначе именно самое узкое место откажет. Вся система должна работать от него: принцип «барабан-буфер-веревка».
Физически невозможно в 10-ти минутных песочных часах просыпать весь объем за 1 минуту – очевидно, что они разрушатся. Аналогично и для предоставления услуги. Никого не удивляет — когда перед МФЦ очередь и скандалы на получение услуги, но всех удивляет — почему портал лег.
Есть разные модели поведения обработки нагрузки из теории массового обслуживания:
- можно ставить пользователей в ожидание, т.е. наращивать очередь;
- можно просто отказывать в обслуживании тем, кто пришёл позже, например, пока не будут обработаны предыдущие;
- можно пытаться бесконечно наращивать производительность.
Целесообразность где-то посередине. Ведь для услуги с ограниченным ресурсом, предоставляемой регионом или государством, важна не только скорость, но и, в первую очередь, сохранение социальной справедливости – т.е. равные условия у всех. В тоже время – если пользователь не получил то, что ему нужно, он инициирует новый запрос. В этом случае запросы растут лавиной, формируя модель атаки «эффект собачьей стаи» (dog-pile effect, cache stampede, hit miss storm) – пользователь запрос уже отменил и инициировал новый, а предыдущий ещё в очереди стоит на обработку.
Этот процесс усиливает то, что в подаче участвуют целыми семьями — папа и мама заполняют заявления одновременно, и часто подают заявления несколько раз для надежности. А кроме того, часто ещё и в нескольких вкладках и нескольких браузерах. Поэтому ожидаемую пиковую нагрузку обычно имеет смысл умножать в 2-3 раза, от количества тех, кто фактически подает заявления в подобных услугах.
Отступление про справедливость
Иногда, осмысляя очередной «опыт», я вспоминаю участие в марафонах и разыгрывание ограниченного количества мест в лотерею. Что будет справедливей? Сейчас «справедливость» больше работает на тех, кто разбирается в ИТ. Мы имеем преимущество. Банально если есть время — можно просто написать скрипт для автозаполнения таких полей, да навык копипастле более отработан. Имея это преимущество мы считаем такую ситуацию «более справедливой». Но это не так для других категорий заявителей. И это цифровое неравенство не убрать проведением интеренет в деревни.
Организация оказания услуги
Мы сделали расчет ожидаемого количества заявителей на основе комбинации данных об общем количестве поданных заявлений в прошлому году и поминутным данным по другим регионам. Обычно пик заявлений приходится на 5-10 минуты, в том числе потому что первые три-пять минут порталы почти не отвечают, а позже пользователи заполняют форму от 1 до 5 минут (не удивляйтесь многие даже в таких «нервных» условиях заполняют с телефона).
Примерная модель расчета для условных 1000 заявлений в час такая:
- пик с 5 по 10 минуту с момента начала и будет подано 80% заявлений по правилу Паретто
- условно планируем 160 заявлений в минуту или 3 заявления в секунду.
По факту первая подача произошла через одну минуту и 45 секунд, а пик заявлений шёл с 4 минуты.
Для уменьшения нагрузки на ЕСИА и на систему от генерирования сессий авторизации в инструкции предложили пользователям авторизоваться заранее и удлинили время жизни сессии. По факту 50% были авторизованы за 1 час, а ~90% за полчаса. Мы сталкивались ранее с тем, что пользователи начинали заходить на портал за 10 минут до начала услуги — и авторизация начинала работать нестабильно. Сложно сказать почему. Возможно причина в том, что когда в Москве проводятся технические работы ночью — у нас в Хабаровске как раза начало рабочего дня.
Отступление про инструкцию и организационные мероприятия
На главной странице были размещены баннеры с прямой ссылкой на форму и инструкцию.
В самой инструкции были прописаны все прямые ссылки на форму, чтобы избежать использования «ресурсоемких ресурсов» поиска и каталогов с описаниями. Т.е. организационно маршрутизировали пользователей прямо на форму. Заодно разъясняли все спорные и сложные вопросы, чтобы уменьшить кол-во обращений в тех.поддержку.
Здесь кстати напрашивается реализация функциональности, как это все можно реализовать в самой форме, чтобы было удобно и можно было прояснить вопросы сразу, не обращаясь к поддержке. Многие пользователи далеки от ИТ и ожидаемые ИТ-разработчиками методы взаимодействия с формой часто просто не понимают. На это накладываются и нормативные требования, названия атрибутов и значения справочников очень формализованы, а изменить их просто так невозможно.
В самой инструкции были прописаны все прямые ссылки на форму, чтобы избежать использования «ресурсоемких ресурсов» поиска и каталогов с описаниями. Т.е. организационно маршрутизировали пользователей прямо на форму. Заодно разъясняли все спорные и сложные вопросы, чтобы уменьшить кол-во обращений в тех.поддержку.
Здесь кстати напрашивается реализация функциональности, как это все можно реализовать в самой форме, чтобы было удобно и можно было прояснить вопросы сразу, не обращаясь к поддержке. Многие пользователи далеки от ИТ и ожидаемые ИТ-разработчиками методы взаимодействия с формой часто просто не понимают. На это накладываются и нормативные требования, названия атрибутов и значения справочников очень формализованы, а изменить их просто так невозможно.
Убрать «дельта функцию» при перезагрузке формы в 00:00 часов невозможно. Весь смысл этой процедуры в том, что услуга появляется в заданное время. Но можно постараться уменьшить кол-во запросов браузера на всех ожидаемых маршрутах пользователей и тем самым оставить нагрузку на систему только от необходимых — форма, динамические справочники и отправка заявления.
Сами настройки nginx достаточно стандартны. Здесь важнее подобрать ограничения, которые может выдержать система. Их и подобрать — т.е. начинать ставить в очередь запросы, когда сервер ожидаемо подойдет к границе своих возможностей.
Ну и самое важное — мы принудительно проставили кеширование (proxy_cache) и увеличили время жизни данных «expires» в nginx для всех путей статики и где возможно динамических страниц, в которых нет сессий. Это кстати частая ошибка при кешировании — записывать в кеш данные (иногда даже статику), в которых сохранена чужая сессия, выход обычно удалять эти куки из заголовков, если сервер не может разделять типы данных.
В браузере для пользователя это выглядит как обновление страниц из файлов загруженных с диска или памяти. Но даже когда у пользователя они получаются с сервера — они берутся из кеша nginx. Сами справочники конечно же в самой системе закешированы.
Это уменьшило кол-во потенциальных запросов с 89-запросов до 14 и объем с 2,1 Мб (для 1000 обновивших страницу пользователей это потенциальный пик 4-8Гбит/с) до 38Кб (мы все помним про webpack, но для entrprise платформ это не всегда легко сделать). По результатам прохождения – надо ещё было кешировать не только в системе, но и в nginx часть справочников с формы и динамических классификаторов не используемых в пиковый момент и проставлять для них принудительно время жизни. А при росте нагрузки вообще имеет смысл выставлять на главную полностью статичную страницу с маршрутизацией пользователей на нужную услугу или делать отдельный ресурс для услуги.
Для уменьшения нагрузки на отправку, были отключены черновики и автоматическое заполнение данных на ребёнка. Скорость ввода данных у всех пользователей разная, что исключает появление полностью готовой для отправки формы и позволяет избежать дельта функции на отправку заявлений — всю 1000 в одну минуту. При этом социальная справедливость сохраняется, хотя, конечно, появляются жалобы.
Оптимизацию самой системы описывать не буду — при нагрузочном тестировании выявлялись узкие места — в основном в запросах к СУБД и оптимизировались индексы и сами запросы.
Наверное самой главной оптимизацией является упрощение формы. Что сильнее всего влияет на скорость при реализации в форме?
- загрузка файлов — при загруженном канале, это существенно увеличивает нагрузку на него и систему, особенно при загрузке сканов большого размера. Математика тут простая — типичная фотография на смартфоне сейчас занимает 5-10Мбайт (привет владельцам новых iPhone у которых низкое разрешение на камерах просто не поддерживается) и для 5-ти документов дает использование одним пользователем до 375 Мбит/с канала (1 байт считаем примерно равным в трафике 10 битам, хотя при кодирование файлов
application/x-www-form-urlencoded
– это 20 бит), а 100 пользователей в минуту это дает 625 Мбит/с. В регионах где ширина арендованных каналов к ЦОДам редко превышает 100Мбит/с это может стать неожиданным сюрпризом — так как начнется отказ в обслуживании по таймаутам. Пользователи будут нервничать, перегружать и это приведет к «эффекту собачьей стаи». Обычно первый вопрос — зачем нам нужны эти файлы? Если оригиналы все равно приносят, то часто их можно опустить. А если это копии, то какая юридическая значимость в них? - сложные справочники. Нагрузку повышает обычно использование адресного справочника ФИАС или КЛАДР. Проблемы здесь в силу размера — ФИАС занимает до 40Гбайт в БД и поиск по нему занимает время. Десятые доли секунды, но умноженные на 1000 одновременных запросов — нагружают любую систему. Без специальной подготовки, возможно в виде отдельного веб-сервиса и на отдельном ресурсе, сложно выдержать нагрузку — поэтому часто для адреса используют обычное текстовое поле.
Ну и перейдем собственно к тестам.
Нагрузочное тестирование при подготовке
Тестирование делали через puppeteer – путем эмуляции действий пользователя в браузере Crominium. Янедекс.танк и JMeter отбиваются защитой от атак, т. к. генерируют множество однотипных запросов. Кроме того эти тесты слабо совпадают с профилем реальных запросов при изменении поведения системы под нагрузкой. Кроме того сервера кешируют запросы, а часть процессов в них воспроизвести сложно (например авторизацию). Кстати, с одного из семинаров devDV у нас есть выступление с презентацией по вопросу использования puppeteer для тестирования, в т.ч. нагрузочного, ссылка на видео.
Для начала мы составили профиль поведения пользователя и процедуру разбили на ключевые этапы:
- массовая авторизация в ЕСИА
- единовременное обновление формы услуги,
- массовая подача
На каждый из этапов мы делали отдельный тест.
В прошлом году на этапе авторизации в ЕСИА были сложности, но протестировать его полномасштабно затруднительно. Система внешняя, срабатывает защита от атак и баны на авторизации. Тем не менее можно сформировать профиль тестов для проверки именно узких мест тестируемой системы — обычно это количество одновременно авторизованных сессий и плановые значения авторизаций в минуту, которые можно регулировать рекомендациями.
В тесте важна обертка для организации нескольких потоков, мы используем 'puppeteer-cluster'. Но обычно более сложным является обработка исключений и изменения поведения портала под нагрузкой — часто выявляются элементы верстки, которые всплывают по два раза. Или элементы не проявляются, если какие-то данные загрузились не так, как ожидалось. Это все те ошибки, которые увидят и пользователи и перезагрузят страницу — а значит создадут дополнительную нагрузку. Здесь два пути — реализовывать в тесте обработку исключений. Или дорабатывать портал.
Сам тест простой. Ниже фрагмент от клика на кнопку «Вход» на портале услуг до ввода данных в ЕСИА.
await page.waitForSelector(AUTH_AVAIL,{timeout:OPT_ELEM_WAIT_TIME});
const needAuth = await page.$(ELEM_AUTH_IN);
if (!needAuth) throw (new Error(`Нет элемента входа`));
await page.waitForSelector(AUTH_BUT, OPT_ELEMENT_VISIBLE);
await page.click(AUTH_BUT);
await waitNewUrl(page, 'https://esia.gosuslugi.ru/idp/rlogin?cc=bp', OPT_PAGE_WAIT_TIME);
await page.waitForSelector('#mobileOrEmail', OPT_ELEMENT_VISIBLE);
let text = await elemGetText(page, '#authnFrm > div.login-slils-box > div > div.detected > div.left > div.this-user');
if (text)
text = text.replace(/ -\(\)/g, '');
if (text && text.indexOf(user) === -1) {
await page.click('div.click-to-another > a');
await page.waitForSelector('#authnFrm > div.login-slils-box > div >' +
' div.detected > div.left > div.this-user', OPT_ELEMENT_INVISIBLE);
}
await page.waitForSelector('#password', OPT_ELEMENT_VISIBLE);
await page.type('#mobileOrEmail', user);
await page.type('#password', pwd);
await page.click('#loginByPwdButton');
Проверка обновления формы заявления в ожидании пользователями «открытия записи». Тест на перезагрузку по сути одношаговый, но важно проверять типы возвращаемых ошибок – сетевая это проблема, ошибка nginx, ошибка сервера и соответствует ли форма критериям. А сложность в том, чтобы сгенерировать максимальный объем запросов за наименьшее кол-во времени и не попасть под ограничения защиты (впрочем на время тестов её можно изменить, с другой стороны, это тоже проверка настроек сетевой и серверной инфраструктуры и WAF).
Такие тесты на puppeteer требует достаточно много ресурсов для работы. Де факто получилось, что нужно не менее 2-х ядер против 1-го ядра фронтенд подсистемы и очень широкий канал. Но при аренде их в облаке — это вполне доступно. Мы пользовались Яндекс.облаком.
В тесте сначала реализуется авторизация в ЕСИА для каждого потока отдельно. После этого запускается на каждый поток отдельный браузер и в рамках одной инстанции проводится заданное кол-во обновлений. После этого инстанция перезапускается. Сама проверка может включать типовой путь, например главная страница, форма услуги. Но чаще достаточно только полного обновления услуги и проверки необходимого справочника, что услугу можно подать — все как в инструкции для пользователей.
Фрагмент теста по открытию главной и обновлению страницы.
try {
await page.setViewport(PUP_OPT);
await page.goto(BASE_URL);
await page.setCookie(...cookies[worker.id]);
await page.goto(`${BASE_URL}/nd/lk/form/dnv.htm`);
rdyRefresh++;
} catch (err) {
console.error(`# Ошибка в открытия портала или формы ${data}: ${err.message}`);
getErr++;
await page.screenshot({path: filename});
}
for (let i = 0; i < AMOUNT_REFRESH - 1; i++) {
const filenameIter = path.join(BASE_DIR, PIC_DIR, `${data}-${i}.png`);
try {
await page.reload({waitUntil: ["networkidle0", "domcontentloaded"]});
rdyRefresh++;
} catch (err) {
if (!err.message.includes('Navigation failed because browser')) {
console.error(`# Ошибка в обновлении страницы ${data}-${i}: ${err.message}`);
getErr++;
await page.screenshot({path: filenameIter});
}
}
}
Для нагрузки отправкой заявлений реализовывался весь цикл проверки – с перезагрузкой формы и проверкой ввода всех данных.
Фрагмент.
for (let i = 0; i < AMOUNT_RESEND; i++) {
const filename = path.join(BASE_DIR, PIC_DIR, `${data}-${i}.png`);
try {
await page.goto('https://uslugi27.ru/nd/lk/form/dnv.htm');
} catch (err) {
console.error(`# Ошибка в в открытиие страницы 1го класса ${data}-${i}: ${err.message}`);
await page.screenshot({path: filename});
getErr++;
continue;
}
try {
const FORM_PREF = '#createForm > div:nth-child(4) > ';
await clickDelayed(page,`${FORM_PREF}fieldset.petgroup.ungroupped-attrs > div > div:nth-child(4) > div.col-md-9.attr-data`);
// <…>
await page.type(`${FORM_PREF}fieldset:nth-child(2) > div > div:nth-child(1) > div.col-md-9.attr-data > input`, 'ТестФамилия');
// <…>
} catch (err) {
console.error(`# Ошибка в заполнении данных формы ${data}-${i}: ${err.message}`);
await page.screenshot({path: filename});
continue;
}
try {
await page.click('#createForm > div.col_100.controls > button.btn.btn-primary.pull-right.next');
await clickDelayed(page,`#createForm > div:nth-child(5) > fieldset > div > div:nth-child(1) > div > div`);
await page.click('#createForm > div:nth-child(5) > fieldset > div > div:nth-child(2) > div > div');
await page.click('#createForm > div.col_100.controls > button.btn.btn-success.pull-right.submit');
} catch (err) {
console.error(`# Ошибка в отправке формы ${data}-${i}: ${err.message}`);
await page.screenshot({path: filename});
sendErr++;
continue;
}
Кстати, прохождение теста можно ускорить, если вводить все данные не из из puppeteer конструкцией await page.type, а перенести эту логику в сам браузер. Но тогда возрастает сложность отлавливания ошибок. Например так
document.querySelector('#createForm > div:nth-child(4) > fieldset.petgroup.ungroupped-attrs > div > div:nth-child(4) > div.col-md-9.attr-data').click();
document.querySelector('#createForm > div:nth-child(4) > fieldset:nth-child(2) > div > div:nth-child(1) > div.col-md-9.attr-data > input').value = 'ТестФамилия';
Во время тестов мы обеспечивали несколько тысяч авторизаций ЕСИА и около 16 тыс. отправленных заявлений. Как проводилось восстановление продуктивной информационной системы образования после такого кол-ва заявлений — даже не спрашивайте. Это совсем другая история.
Главный видимый результат этого процесса оказался в том, что местным СМИ в дни записи в первый класс теперь было скучно. Услуга ушла из медийной области.
Параллельно мы сделали сводную панель мониторинга, для проверки работоспособности формы на основе Grafana: количества заявлений, количества звонков, данные яндекс.метрики и т.д. Но эту тему оставим для следующего раза.
Ну и я хотел бы поздравить всех, кто связан с темой улучшения качества предоставления государственных и муниципальных услуг в электронном виде. Эта бесконечная работа по подготовкам не прошли зря — ведь за апрель и май количества поданных заявлений увеличились в разы.
Jef239
Так и не понял, как вы растянули подачу на большее время.
И ещё. Почему ЕСИА, которое должно быть готово к синхронной подаче заявлений в Москве и Питере, оказалось не готово к нагрузке в Хабаровске. У вас больший уровень использования госуслуг?
akumidv Автор
По ЕСИА я не могу сказать почему и даже не могу утверждать, что это было так. Это было в 2019 г. В субботу утром — моя гипотеза, что технические работы проводились. Это ночь по Москве — с этим во многих российских сервисах приходится сталкиваться. Кроме того, на мой взгляд — там разные группы серверов для разных макрорегионов. Но тех.ответ — все работает штатно. Кроме того, вылазают и сетевые проблемы периодически, какие-то операторы весть трафик кешируют, что-то не выдерживает. И ещё часто сбоят не сами системы, а системы защиты этих систем. В этом году такой проблемы не было — но услуга подаваль в пятницу вечером по Москве (у нас ночью).
По растягиванию — всё просто. Форма очень простая получилась и фактически полностью заполненная сразу, особенно если черновик предварительно заполнить. Поэтому черновик убрали. И убрали автоподстановку информации о детях и документах в этой услуге. Вбиваение этих данных требует времени. У обычного пользователя на это уходит от минуты (копировать/вставить и перечитать), у того кто вбивает на компьютере руками до 2-4х минут, а с телефонов и того больше.
Но более правильный вариант — форму вообще не показывать до начала подачи, иначе можно сгенерировать скрипты и заполнить всё за 1 секунду или вообще post запрос подготовить. А т.к. спрос есть — это может породить рынок.
Jef239
А почему бы не создать очередь отправки в браузерах? То есть в момент отправки запроса фиксируется ид, а сама отправка в СУБД идет позднее (и отображается кручением часов).
Ну и на самый главный вопрос нет ответа — если Хабаровск создает значимую нагрузку, то что же в Москве? Или как сейчас — синхронная нагрузка по всей России?
akumidv Автор
Можно по всякому решать. Единственно в услуге запись в первый класс очень критично время подачи — по нему формируется очередь — это юридически значимо — а передать id без минимум ФИО родителя/ребенка — это уже не то, вдруг потом данные не дойдут.
Тем более проблемы на этапе подачи не было — нагрузка растянутая на 3-5 минут уже вполне решаемая на этих объемах. И её протестировать хорошо вполне возможно. Основаня проблема в обновлении в 0:00 минут — этот пик, дельта фукцию, не обойти никак. И её кстати сложно сэмулировать нагрузкой — можно только экстраполяции делать на динамике с разной нагрузкой и выдвигать гипотезы — что вперед будет "выбрано" — канал, производительность коммутационного оборудования, производительность защиты или ошибки в настройках алогоритмов, ресурсы сервера. Кстати канал и коммутаторы не всегда в ЦОДе — ведь есть ещё операторы связи в городах, районах и у некоторых они не с таким уж большим резервом — а трафик из ютуба очень отличается от этой ?-функции.
Про ЕСИА ничего вам не могу допонительно сказать. Не думаю, что Хабаровск создает значимую нагрузку, даже с остальными регионами ДВ — меньше 7ми миллионов на 3 часовых пояса. Но факторы складываются по разному, а внешняя система — есть внешняя система.
Jef239
Мне кажется, что время подачи критично
не для всехдля малого числа школ. Соответственно можно подачу в эти школы делать сразу, а в иные — с задержкой на случайное число секунд.akumidv Автор
Так именно родители этого малого числа школ и создают эту нагрузку — их примерно до 10% от общего кол-ва школьников. В остальные школы проще прийти с документами до конца мая без шума и спешки.
Jef239
Ну так простая задержка для оставшихся 90% уменьшит трафик в 10 раз.
akumidv Автор
Тут какая штука — пока весь состав атрибутов заявления не передан в интегрированную информационную систему образования, заявление не считается поданным и соответственно права "первоорчедености" не возникает.
Сама подача ведь тоже верифицируется на стороне сервера — там есть логика проверки на фронте, а есть и серверая логика верификации значений справочников хотя бы.
Опять же — если то, что вы подали раньше всех не соответствует логике и требования адм.регламента записи — это заявление просто отклонят. Ну например адрес вы не укажите или другой какой-то, не соответствующий школе. Поэтому полное заполнение заявления — это обязательное условие. В идеале (и в практике для ряда услуг) — весь состав данных подписывается эл.подписью для юридической значимости. Но т.к. это сложно организовать для граждан — используется простая эл.подпись, но де факто юридическая значимость обеспечивается статусом информационной системы, логгированием всех запросов и подразумеваемой возможностью инициировать проверку прокуратуры и изъятия всех этих данных для ручной сверки. Происходит редко именно в силу посреднической функции, т.е. доверительной, которую выполняют региональные порталы между гражданами(браузерами) и ведомствами(инф.системами).
Браузер в этой схеме является несертифицированным армом — т.е. логике на его стороне фактически нет никакого доверия — т.к. действительно можно и в скрипты вмешиваться и состав данных подправить прямо в форме, даже в заблокированных для редактирования полей и просто POST запрос сгенерировать. Т.е. важно — идентификтор пользователя в виде сессии авторизации и состав данных по полям. И когда валидация сессии и соответствия состава полей и каких-то формальных требований к содержанию пройденно — именно это и является заявлением и фактом его подачи.
Досылка даных — это уже другой процесс. Такой например есть в госэкспертизе. Там вы подаете заявление, но реальные данные выгружаете (сканы документов проектых) уже после регистрации факта подачи.
Хотя де юре можно сделать простую кнопку — "встать очередь" — так видят услугу многие пользователи и к этому логика развития эл.услуг идет. Только нужно адм.регламенты написать и закрепить их. Тогда нужно передать только сессию пользователя и его "желание получить услугу". А данные дослать или запросить в других ведомствах. Но тогда мы опять же получим ?-функцию в отправке.
И получим кучу других проблем — а действительно он выразил это желание или случайно нажал? Это как в договоре — банку недостаточно вашей подписи, они ещё и ФИО просит написать — чтобы гарантировать, что это вы. А нотариус сколько вопросов задает, чтобы понять дееспособность и осознанность намерения. Ввод доп.данных служит в том числе и этой цели. Ну и растягивает подачу на какое-то время, меняя модель случайно успел нажать, систему случано раньше получила из-за случайных сетевого состояния — на какой-то соревнуемый навык — быстрее печатал, знал как скрипт написать автозаполнения — это вместо быстрого коня на диком западе ))) Иначе можно и просто лотерею ввести. Что будет справедливее?
Jef239
Боюсь, что вы не поняли, что я предлагаю. Есть смысл приоритетно отправлять запросы по школам, где конкуренция есть. Так, чтобы они точно не были отброшены из-за слишком большого трафика. Запросы по остальным школам могут подождать пару минут перед отправкой. А выделить приоритетные школы можно на клиенте.
Что касается социальной справедливости — она достигается совсем иными мерами. Надо проводить обследование и раз в год менять количество классов и границы закрепленных участков. Главный принцип — первоклассник не должен переходить на пути в школу дороги с оживлённым движением.
Там есть один секрет — в детском саду и началке очень важна личность учителя (воспитателя). И почти не важно само учреждение. Если у ребёнка с учителем нет контакта — он не будет учиться. Если контакт есть — при самом неумелом учителе ребенок выучится.
Сейчас это вообще не регламентировано. После записи в школу надо идти к завучу и просить зачисления в нужный класс. Ну или менять класс уже после понимания, что учитель ребенку не подходит. А это уже плохо для ребенка.