Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Проект Graphite

Глава 7 из книги "Архитектура приложений с открытым исходным кодом", том 1.

Оригинал: Graphite, глава из книги "The Architecture of Open Source Applications" том 1.
Автор: Chris Davis
Перевод: Н.Ромоданов

7.7. Все это в режиме реального времени

Буферизация значений точек данных была замечательным способом оптимизировать ввод/вывод в carbon, но через не очень продолжительное время мои пользователи заметили довольно тревожный побочный эффект. Снова вернемся к примеру с 600000 метриками, обновляющимися каждую минуту, и, предположим, что наша память может справиться лишь с 60000 операциями записи в минуту. Это означает, что у нас возникает ситуация, когда в любой заданный момент времени данные будут находиться в очередях carbon приблизительно по 10 минут. Для пользователя это означает, что в графиках, которые они запросили из веб-приложения Graphite, будут отсутствовать данные за последние 10 минут. Это плохо!

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

7.8. Ядра, кэширование и катастрофические отказы

К настоящему моменту, вероятно, уже очевидно, что ключевой характеристикой производительности, от которой зависит собственная производительность Graphite, является задержка ввода/вывода. До сих пор мы предполагали, что в нашей системе стабильно низкая задержка ввода/вывода, составляющая в среднем около одной миллисекунды на запись, но это серьезное предположение, требующее несколько более глубокого анализа. Большинство жёстких дисков просто не настолько быстры; даже когда десятки дисков объединены в RAID-массив, есть очень большая вероятность, что задержка при случайном доступе будет больше одной миллисекунды. Тем не менее, если вы попробуете и проверите, насколько быстро даже старый ноутбук мог бы записать целый килобайт данных на диск, вы обнаружите, что системный вызов записи возвращает управление гораздо быстрее, чем 1 миллисекунда. Почему?

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

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

Что это всё означает для пакета Graphite? Мы просто подчеркнули зависимость carbon от стабильно низкой задержки ввода/вывода, и мы также знаем, что системный вызов записи возвращает управление быстро только потому, что данные просто копируются в буфер. Что происходит, когда ядру не хватает памяти для продолжения буферизации записи? Запись становится синхронной и, следовательно, ужасно медленной! Это приводит к резкому снижению скорости операций записи в carbon, что ведет в carbon к росту очередей, съедающих еще больше памяти, которой еще больше не хватает ядру. В конце концов, такая ситуация обычно приводит к тому, что в carbon заканчивается память или что сердитый сисадмин убивает процесс.

Чтобы избежать катастрофы подобного рода, я добавил к carbon несколько возможностей, в том числе конфигурируемые ограничения на количество значений точек данных, которые могут быть в очереди, и ограничения, определяющие насколько быстро в {whisper могут выполняться различные операции. Эти возможности могут защитить carbon от потери управления, но вместо этого могут привести к менее суровым последствиям, например, к пропаданию некоторых значений точек данных и к отказу от приема новых значений точек данных. Однако, правильные значения для этих настроек зависят от системы и для их настройки требуется значительное количество проб. Эти возможности полезны, но они не решают данную проблему принципиально. Для этого нам понадобится больше оборудования.


Далее: Кластеризация