Библиотека сайта rus-linux.net
GNU Mailman
Глава 10 из книги "Архитектура приложений с открытым исходным кодом", том 2.Оригинал: GNU Mailman
Автор: Barry Warsaw
Перевод: А.Панин
10.3. Обработчики
Сообщения перемещаются в рамках системы с помощью наборов независимых процессов, называемых обработчиками (runners). Изначально введенные в качестве инструмента для предсказуемой обработки всех файлов сообщений из очереди, найденных в определенной директории, на данный момент обработчики существуют в виде нескольких независимых постоянно функционирующих процессов, которые выполняют специфическую задачу и находятся под управлением ведущего процесса; более подробное описание будет приведено ниже. В том случае, если обработчик управляет файлами в директории, он называется обработчиком очереди (queue runner).
Система Mailman принципиально разрабатывается как однопоточное приложение даже при наличии возможностей параллельного выполнения процессов. Например, система Mailman может принимать сообщения от почтового сервера одновременно с отправкой сообщений принимающим сторонам, обработкой нагрузок или архивацией сообщения. Параллелизм в рамках системы Mailman достигается путем использования множества процессов в виде этих самых обработчиков. Например, существует обработчик очереди входящих сообщений (incoming queue runner), задачей которого является прием (или отклонение) сообщений от используемого сервера электронной почты. Также существует обработчик очереди исходящих сообщений (outgoing queue runner), задачей которого является взаимодействие с используемым SMTP-сервером для отправки сообщений конечным адресатам. Наряду с описанными существуют обработчики очереди архивирования (archiver queue runner), очереди уведомлений (bounce processing queue runner), обработчик очереди пересылки сообщений серверу NNTP (queue runner for forwarding messages), обработчик создания каталога (runner for composing digests) и некоторые другие обработчики. Обработчики, не управляющие очередями включают обработчик с реализацией локального протокола пересылки почты (Local Mail Transfer Protocol) и обработчик с реализацией административного HTTP-сервера.
Каждый обработчик очереди ответственен за отдельную директорию, т.е., за свою очередь. Хотя обычная система Mailman может превосходно работать, выделяя по одному процессу на очередь, мы можем применить сложный алгоритм для параллельного исполнения задач в рамках отдельной директории очереди, не используя при этом каких-либо типов взаимодействий и блокировок. Секретом успеха является способ именования файлов в директории очереди.
Как упоминалось ранее, каждое сообщение, которое перемещается в системе также сопровождается словарем для метаданных, который накапливает данные состояния и позволяет независимым компонентам системы Mailman взаимодействовать друг с другом. Библиотека pickle
языка Python позволяет производить прямое и обратное преобразование множества объектов в один файл, поэтому мы можем преобразовать в один и тот же файл и дерево объектов сообщения и словарь метаданных.
В рамках системы Mailman существует основной класс с именем Switchboard
, который предоставляет интерфейс для выполнения операций помещения в очередь (т.е. записи) и извлечения из очереди (т.е. чтения) дерева объектов сообщения и словаря метаданных в отношении файлов из директории определенной очереди. Каждая директория очереди имеет как минимум один соответствующий ей экземпляр класса Switchboard и каждый экземпляр обработчика очереди имеет по одному экземпляру класса Switchboard.
Все созданные с использованием библиотеки pickle файлы имеют расширение .pck
, хотя вы также можете обнаружить в очереди файлы с расширениями .bak
, .tmp
, и .psv
. Они используются для реализации двух священных принципов функционирования системы Mailman: ни один из файлов не должен быть потерян и ни одно из сообщений не должно быть доставлено более чем один раз. Но обычно система функционирует в нормальном режиме, поэтому эти файлы могут быть обнаружены очень редко.
Как было показано, для особо загруженных сайтов система Mailman предоставляет возможность запуска более чем одного процесса обработчика для каждой из директорий очередей в полностью параллельном режиме без взаимодействия между ними или необходимости блокировок для обработки файлов. Этого удается добиться путем использования хэшей SHA1 для именования файлов, после чего разрешения отдельному обработчику очереди обрабатывать только определенный участок диапазона хэш-значений. Таким образом, если сайту необходимо использовать два обработчика для очереди уведомлений (bounces queue), один обработчик будет обрабатывать файлы из верхней половины диапазона хэш-значений, а другой будет обрабатывать файлы из нижней половины диапазона хэш-значений. Хэши рассчитываются с использованием данных дерева объектов сообщения из файла, имени списка рассылки, для которого предназначено сообщение и метки времени. Хэши SHA1 эффективно распределены и, следовательно, в среднестатистической директории с двумя обработчиками очередей у каждого процесса будет примерно одинаковый объем работы. А так как диапазон хэш-значений может быть статически разделен, эти процессы могут работать в одной директории очереди без необходимости вмешательства в работу друг друга и взаимодействия.
Существует интересное ограничение данного алгоритма. Так как алгоритм разделения ставит в соответствие каждому обработчику одни или несколько битов хэша, количество обработчиков для каждой директории очереди должно быть кратным 2. Это значит, что могут использоваться 1, 2, 4 или 8 процессов обработчиков для каждой очереди, но, например, не 5. На практике это ограничение никогда не приводило к проблемам, так как только нескольким сайтам может потребоваться более 4 обработчиков для работы в условиях их нагрузки.
Существует другой побочный эффект использования этого алгоритма, который приводил к проблемам в период раннего проектирования этой системы. Несмотря на непредсказуемость процесса доставки сообщений электронной почты в общем случае, для пользователя является наиболее удобной обработка файлов очереди в последовательности FIFO, таким образом, чтобы направляемые в список рассылки ответы отсылались в относительно хронологическом порядке. Игнорирование этого правила может привести в замешательство участников списка рассылки. Но использование хэшей SHA1 в качестве имен файлов препятствует использованию меток времени, при этом следует избегать вызова функции stat()
в отношении файлов очереди по причинам, связанным с производительностью, а также распаковки содержимого сообщения (т.е. чтения метки времени из метаданных).
Решение в рамках системы Mailman заключалось в расширении алгоритма именования файлов для включения в состав имени префикса с меткой времени в форме числа, отражающего количество секунд, прошедших с начала эпохи (т.е. <метка времени>+<хэш sha1>.pck
). Каждый обход очереди обработчик начинает с вызова метода os.listdir()
, который возвращает список всех файлов в директории очереди. После этого для каждого файла производится разбор имени файла и игнорируются все имена файлов, хэши SHA1 которых не соответствуют диапазону хэшей обработчика. После этого обработчик сортирует оставшиеся файлы на основе данных из части имен, содержащей метку времени. Утверждение о том, что при использовании множества обработчиков очередей, каждый из которых обрабатывает определенный диапазон хэш-значений, могут возникнуть проблемы с распределением файлов между параллельно функционирующими обработчиками, является истинным, но на практике распределение на основе меток времени достаточно для удовлетворения ожиданий конечных пользователей в плане последовательной доставки сообщений.
На практике этот метод работал очень хорошо как минимум в течение 10 лет с проводимыми время от времени исправлениями незначительных ошибок или доработками для использования в частных случаях и в условиях возникновения ошибок. Это одна из наиболее стабильных частей системы Mailman, которая была портирована практически без изменений из Mailman 2 в Mailman 3.
Далее: Ведущий обработчик