Русский
Русский
English
Статистика
Реклама

Semaphore

Как перестать DDoS-ить чужой API и начать жить

25.04.2021 12:10:20 | Автор: admin

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

Вводные

Для начала немного вводных. Есть наше приложение иесть некий внешний сервис. Например, какое-то банковское ПО, API для отслеживания почтовых отправлений, что угодно. При этом наше приложение непросто использует его, там куча очень важной для нас информации. Прибыль компании напрямую зависит отобъема выгруженных оттуда данных. Мыпонимаем, один сервер это слишком мало изаводим себе пару десятков машин. Чтобы приложение масштабировалось лучше, делаем так: разбиваем весь объем намаленькие задачи иотправляем ихвочередь. Каждый сервер извлекает ихоттуда поодной. Втаком сообщении указан, например, IDпользователя. Затем приложение скачивает данные для него исохраняет ихвбазе. Большая ибыстрая машина обработает много задач, маленькая имедленная поменьше.

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

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

Один семафор на машину

Делим лимит запросов начисло доступных серверов (1000/20) иполучаем по50конкурентных обращений намашину.

Простой семафор в .NET
private const int RequestsLimit = 50;private static readonly SemaphoreSlim Throttler =   new SemaphoreSlim(RequestsLimit);async Task<HttpResponseMessage> InvokeServiceAsync(HttpClient client){try{await Throttler.WaitAsync().ConfigureAwait(false);return await client.GetAsync("todos/1").ConfigureAwait(false);}finally{Throttler.Release();}}

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

Попробуем проанализировать то, что получилось.

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

Подведем ему некий итог:

Плюсы:

  1. Простой код

  2. Ресурсы машины используются эффективно

Минусы:

  1. Не полностью утилизируется канал во внешний сервис

Один семафор на всех

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

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

ВRedis нет готового семафора, номожно построить его насортированных множествах.

Писать код для него будем наLua. Интерпретатор Lua встроен вRedis, такие скрипты выполняются одним махом, атомарно. Мымоглибы без этого обойтись, нотак код получится ещё сложнее, придется учитывать конкуренцию между серверами заотдельные команды.

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

Скрипт для Redis
--[[  KEYS[1] - Имя семафора ARGV[1] - Время жизни блокировки ARGV[2] - Идентификатор блокировки, чтобы её можно было возвратить ARGV[3] - Доступный объем семафора ]]--   -- Будем использовать команды с недетерминированным результатом,  -- Redis-у важно знать заранее redis.replicate_commands()local unix_time = redis.call('TIME')[1]   -- Удаляем блокировки с истёкшим TTL redis.call('ZREMRANGEBYSCORE', KEYS[1], '-inf', unix_time - ARGV[1])   -- Получаем число элементов в множестве local count = redis.call('zcard', KEYS[1])   if count < tonumber(ARGV[3]) then-- добавляем блокировку в множество, если есть место  -- время будет являться ключем сортировки (для последующий чистки записей) redis.call('ZADD', KEYS[1], unix_time, ARGV[2])       -- Возвращаем число взятых блокировок (например, для логирования)    return count end   return nil

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

Подробнее о вариантах реализации блокировок с Redis и семафоров в частности можно посмотреть здесь.

Иногда внешний сервис ограничивает число обращений другим образом, например, разрешает делать неболее 1000 запросов вминуту. Вэтом случае вRedis можно завести счётчик сфиксированным временем жизни.

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

Код для приложения
public sealed class RedisSemaphore{private static readonly string AcquireScript = "...";private static readonly int TimeToLiveInSeconds = 300;private readonly Func<ConnectionMultiplexer> _redisFactory;public RedisSemaphore(Func<ConnectionMultiplexer> redisFactory){_redisFactory = redisFactory;}public async Task<LockHandler> AcquireAsync(string name, int limit){var handler = new LockHandler(this, name);do{var redisDb = _redisFactory().GetDatabase();var rawResult = await redisDb.ScriptEvaluateAsync(AcquireScript, new RedisKey[] { name },new RedisValue[] { TimeToLiveInSeconds, handler.Id, limit }).ConfigureAwait(false);var acquired = !rawResult.IsNull;if (acquired)break;await Task.Delay(10).ConfigureAwait(false);} while (true);return handler;}public async Task ReleaseAsync(LockHandler handler, string name){var redis = _redisFactory().GetDatabase();await redis.SortedSetRemoveAsync(name, handler.Id).ConfigureAwait(false);}}public sealed class LockHandler : IAsyncDisposable{private readonly RedisSemaphore _semaphore;private readonly string _name;public LockHandler(RedisSemaphore semaphore, string name){_semaphore = semaphore;_name = name;Id = Guid.NewGuid().ToString();}public string Id { get; }public async ValueTask DisposeAsync(){await _semaphore.ReleaseAsync(this, _name).ConfigureAwait(false);}}

Посмотрим, что получилось.

Плюсы:

  1. Просто конфигурировать лимит

  2. Канал используется эффективно

  3. Легко наблюдать за утилизацией канала

Минусы:

  1. Дополнительный элемент инфраструктуры

  2. Ещё одна точка отказа

  3. Накладные расходы на обращение к Redis-у

  4. Нетривиальный код

Если выиспользуете Redis впроекте, топочемубы несделать нанём иблокировки. Тащитьже его ксебе только ради этого уже нетак весело. Что-то подобное можно реализовать, используя базу данных, носкорее всего это добавит немало хлопот инакладных расходов. Второй минус лично мне не кажется очень существенным. Что ни говори, а отказывает Redis обычно не так уж часто, особенно, если это SaaS.

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

Думаю, можно реализовать троттлинг исходящих запросов инауровне инфраструктуры, номне кажется удобным иметь вкоде контроль над состоянием блокировки. Кроме того, настроить это в чужих облаках наверняка будет непросто. Авам приходилось когда-нибудь ограничивать число исходящих запросов? Как делаете этовы?

Подробнее..

Перевод Как протестировать блокноты Jupyter с помощью pytest и nbmake

20.05.2021 16:23:11 | Автор: admin

Файлы блокнотов Jupyter, в смысле количества одного из самых быстрорастущих типов файлов на Github, предоставляют простой интерфейс для итераций при решении визуальных задач, будь то анализ наборов данных или написание документов с большим объёмом кода. Однако популярность блокнотов Jupyter сопровождается проблемами: в репозитории накапливается большое количество файлов ipynb, многие из которых находятся в нерабочем состоянии. В результате другим людям трудно повторно запустить или даже понять ваши блокноты. В этом руководстве рассказывается, как для автоматизации сквозного (end-to-end) тестирования блокнотов можно воспользоваться плагином pytest nbmake.

К старту флагманского курса о Data Science области, в которой блокноты Jupyter незаменимы делимся переводом статьи из блога CI Semaphore о том, как при помощи Semaphore проверить, что ваши блокноты Jupyter находятся в рабочем состоянии и для этого больше не запускать блокноты вручную.


Предварительные требования

Это руководство предполагает владение основными навыками тестирования проектов на Python, о них рассказывается в статьеНепрерывная интеграция и развёртывание на Python с нуля. Прежде чем продолжить, пожалуйста, убедитесь, что вы знакомы с основами и на вашем компьютере установлен набор инструментов Python 3, таких как pip + virtualenv.

Демонстрационное приложение

Обычно проекты Python содержат папки с файлами блокнотов с расширением .ipynb, это может быть:

  • код доказательства концепции;

  • документация с примерами применения API;

  • длинный научный туториал.

В этой статье мы разберём, как автоматизировать простые сквозные тесты некоторых блокнотов, содержащих упражнения на Python. Благодарим pytudes за предоставленные для нашего примера материалы; воспользуемся этим примером проекта, не стесняйтесь делать форк и клонировать его с GitHub:

fig:fig:

Внутри репозитория вы найдёте содержащую блокноты папку, она называется ipynb. Установите зависимости из requirements.txt, а при необходимости сначала создайте виртуальную среду.

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

Локальное тестирование блокнота

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

Давайте начнём автоматизировать этот процесс в вашей среде разработки; первым шагом в автоматизации станет nbmake. Nbmake это пакет python и плагин к pytest. Он разработан автором этого руководства и используется известными научными организациями, такими как Dask, Quansight и Kitware. Вы можете установить nbmake с помощью pip:

pip install nbmake==0.5

Перед первым тестированием блокнотов давайте проверим, всё ли правильно настроено, указав pytest просто собрать (но не запускать) все тест-кейсы для блокнотов.

 pytest --collect-only --nbmake "./ipynb" ================================ test session starts =================================platform darwin -- Python 3.8.2, pytest-6.2.4, py-1.10.0, pluggy-0.13.1rootdir: /Users/a/git/alex-treebeard/semaphore-demo-python-jupyter-notebooksplugins: nbmake-0.5collected 3 items           ============================= 3 tests collected in 0.01s =============================

Мы видим, что pytest собрал несколько элементов.

Примечание: если вы получаете сообщение unrecognized arguments: --nbmake, оно означает, что плагин nbmake не установлен. Такое может произойти, если ваш CLI вызывает двоичный файл pytest за пределами текущей виртуальной среды. Проверьте, где находится ваш двоичный файл pytest, с помощью команды which pytest.

Теперь, когда мы проверили, что nbmake и pytest видят ваши блокноты и работают вместе, давайте выполним настоящие тесты:

 pytest --nbmake "./ipynb"================================ test session starts =================================platform darwin -- Python 3.8.2, pytest-6.2.4, py-1.10.0, pluggy-0.13.1rootdir: /Users/a/git/alex-treebeard/semaphore-demo-python-jupyter-notebooksplugins: nbmake-0.5collected 3 items                                                                    ipynb/Boggle.ipynb .                                                           [ 33%]ipynb/Cheryl-and-Eve.ipynb .                                                   [ 66%]ipynb/Differentiation.ipynb .                                                  [100%]================================= 3 passed in 37.65s =================================

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

Игнорирование ожидаемых ошибок в блокноте

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

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

  • откройте файл .ipynb в простом текстовом редакторе, например Sublime;

  • в метаданных блокнота найдите поле kernelspec;

  • добавьте объект execution в качестве родственного элемента kernelspec.

Должно получиться что-то вроде этого:

{  "cells": [ ... ],  "metadata": {    "kernelspec": { ... },    "execution": {      "allow_errors": true,      "timeout": 300    }  }}

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

Ускорение тестов с помощью pytest-xdist

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

pip install pytest-xdist

Запустите команду ниже с количеством рабочих процессов, равным auto:

pytest --nbmake -n=auto "./ipynb"

Запись выполненных блокнотов обратно в репозиторий

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

  • отладить неработающие блокноты путём просмотра выходных данных;

  • создать коммит в вашем репозитории с блокнотами в воспроизводимом состоянии;

  • встроить выполненные блокноты в сайт с документацией при помощи nbsphinx или jupyter book.

Мы можем направить nbmake на сохранение выполненных блокнотов на диск с помощью флага overwrite:

pytest --nbmake --overwrite "./ipynb"

Исключение блокнотов из тестов

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

pytest --nbmake docs --overwrite --ignore=docs/landing-page.ipynb

Примечание: это не сработает, если вы выбираете все блокноты с помощью шаблона поиска, например (*ipynb), вручную переопределяющего флаги игнорирования pytest.

Автоматизация тестирования блокнотов на Semaphore CI

С помощью вышеописанных методов можно создать автоматизированный процесс тестирования с использованием непрерывной интеграции (CI) Semaphore. Ключ здесь прагматизм: нужно найти способ протестировать большую часть ваших блокнотов и проигнорировать сложные блокноты; это хороший шаг к улучшению качества.

Начните с создания содержащего ваши блокноты проекта для репозитория. Выберите Choose repository:

Затем подключите Semaphore к репозиторию с вашими блокнотами:

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

Создайте простой рабочий процесс в один блок; ниже о процессе в деталях:

  1. Названием блока будет Test.

  2. Названием задачи Test Notebooks.

  3. В поле Commands введите команды ниже:

checkoutcache restorepip install -r requirements.txtpip install nbmake pytest-xdistcache storepytest --nbmake -n=auto ./ipynb

Чтобы лучше понять происходящее, давайте разберёмся, что делает каждая команда:

  1. checkout клонирует репозиторий с GitHub;

  2. cache restore загружает зависимости Python из кэша Semaphore. Это ускоряет процесс установки;

  3. pip устанавливает зависимости и инструменты;

  4. cache store сохраняет загруженные зависимости обратно в кэш;

  5. pytest запускает тесты nbmake.

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

Конвейер должен заработать сразу же:

Устранение некоторых распространённых ошибок

Добавление отсутствующего ядра Jupyter в среду CI

Если вы используете имя ядра, отличное от имени по умолчанию python3, то при выполнении ноутбуков в свежей среде CI увидите сообщение об ошибке: Error - No such kernel: 'mycustomkernel'. Чтобы установить пользовательское ядро, воспользуйтесь командой ipykernel:

python -m ipykernel install --user --name mycustomkernel

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

Добавление недостающих секретов в среду CI

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

Добавление недостающих зависимостей в среду CI

Стек Data Science на Python часто требует установки собственных библиотек. Если вы работаете с CONDA, вполне вероятно, что они будут присутствовать в вашем нормальном процессе установки. Если же вы используете стандартные библиотеки Python, их присутствие немного менее вероятно.

Если вы пытаетесь установить необходимые вам библиотеки, посмотрите на статью о создании образа docker в CI. С ним тестирование упрощается, и по сравнению со стандартными средами Semaphore этот подход стабильнее во времени.

Пожалуйста, помните совет о прагматизме; 90 % полезности часто можно достичь за 10 % времени. Следование совету может привести к компромиссам, таким как игнорирование блокнотов, на которых запускаются требующие GPU модели ML.

Заключение

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

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

  • для удаления громоздких выходных данных блокнота перед коммитом запустите pre-commit и nbstripout;

  • для компиляции ваших блокнотов в красивый сайт с документацией используйте jupyter book;

  • для просмотра блокнотов в пул-реквестах используйте ReviewNB.

Действительно, процессы в разработке ПО и в научных исследованих всё дальше уходят с локальных компьютеров на арендуемые мощности самого разного рода, в том числе потому, что требуют всё больших ресурсов. То же касается и растущих объёмов данных, работа с которыми преобразилась в несколько смежных, но очень разных областей; среди них центральное место занимает Data Science. Если вам интересна эта сфера, вы можете присмотреться к нашему курсу Data Science, итог которого это 16 проектов, то есть 2-3 года постоянного самостоятельного изучения науки о данных.

Узнайте, как прокачаться и в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru