-
Задача стоит следующая: Приходит большое число писем (две-три сотни тысяч в сутки), эти письма надо обработать следующим образом: Для каждого письма проверить его валидность (неким внешним процессом по данным из заголовка письма), если письмо невалидное, то удалить с почтового сервера, если письмо валидное, то передать его на обработку еще одному внешнему процессу, дождаться оповещения об успешном начале обработки и удалить с почтового сервера. Письмо должны быть гарантировано обработаны и не должны теряться в случае ошибок, сбоев питания, и прочих обстоятельств на обрабатывающей стороне. Как сделано сейчас: Используется протокол POP3, программа подключается к почтовому серверу, забирает порцию писем (скажем, 100) не удаляя их с сервера, для каждого письма из порции получает UIDL, отключается от сервера. Дальше письма этой порции обрабатываются, когда наступает момент удаления письма, программа подключается к почтовому серверу, перебирает письма, сравнивая UIDL-ы, найдя нужный, удаляет письмо с сервера). После обработки порции писем, программа пытается получить следующую порцию, если ее нет, то засыпает на какое-то время, и дальше все сначала. Надежность этого решения устраивает, не устраивает скорость при большом количестве писем в ящике.
Собственно вопрос в чем - есть ли какие-то готовые проверенные решения по обработке приличных объемов почты, возможно, использование другого протокола, и т.д. Мне необходимо обеспечить а) скорость б) надежность. Ну все это в режиме 24/7
Буду рад пинкам в нужном направлении.
-
>Игорь Шевченко © (22.03.16 11:12) [0]
на самом почтовом сервере есть какие инструменты по валидации?
есть возможность работать с базой данных почтового сервера?
-
Почему нельзя сделать так, чтобы программа, которая забирает порцию писем с сервера надежно сохраняла их у себя до обработки и сразу удаляла с сервера?
-
virex(home) © (22.03.16 11:28) [1]
Нет. Интересует решение для клиентской части. Работа с базой данных почтового сервера - это IMAP имеется в виду или что ?
-
DayGaykin © (22.03.16 11:32) [2]
Это одно из решений, которые делать не хочется, но возможно, придется, если не найдется менее затратных. Сохраняло где ? В базе данных ? Сложно и затратно. Файловой системы нету.
-
>Для каждого письма проверить его валидность Идет ли речь про отсеивание/частичное отсеивание спама?
-
>Игорь Шевченко © (22.03.16 11:33) [3]
вдруг бд хранится в sql-подобной бд клиент бы запускал запросы/процедуры к бд по валидации и удалению ненужных писем, почти мгновенно тут конечно нужна серверная часть доступа к бд, можно нацепить шифрование
-
Empleado © (22.03.16 11:53) [5]
> Идет ли речь про отсеивание/частичное отсеивание спама?
Не совсем. Спам отсеивает почтовый сервер. Здесь именно валидность писем с прикладной точки зрения. В среднем валидных писем 95%
-
> Надежность этого решения устраивает, не устраивает скорость
Если процент "спама" весьма высок, то не лучше ли на первом этапе (валидации) загружать не всё письмо, а только его заголовок (в MS OUTLOOK есть такая возможность). Обработать заголовки, отметить плохие для удаления, а хорошие для загрузки. На втором этапе уже удалять "спам", загружать "проверенные" письма полностью и передавать их в обработку. В итоге, если спама много, загружать придётся намного меньше писем, что повысит скорость. Заголовки загружаются практически мгновенно.
-
Труп Васи Доброго © (22.03.16 12:04) [8]
> В среднем валидных писем 95%
-
Игорь, а не проще/быстрее сразу просить у сервера все UIDL, а потом в один проход удалять все старое и брать все новое?
-
Sha © (22.03.16 13:05) [10]
Привет, Саша! Не понял, можно подробнее :) Письма, они же поступают регулярно, а не так, что они лежат в ящике, ждут, пока их обработают, а потом до следующего дня. Я обозначил общий объем, а темпы поступления я, увы, обозначить не могу.
-
Первое, что приходит в голову. Клиент ведет 4 списка UIDL: - успешно обработанные - их надо удалить в ближайшем сеансе, - переданные на обработку - надо дождаться результата, - не прошедшие валидацию - их надо удалить в ближайшем сеансе, - новые, еще не считанные с сервера - их надо считать ближайшем сеансе. Клиент в непрерывном цикле делает следующее: - соединяется с сервером, начиная новый сеанс, - одним запросом получает все имеющиеся у него UIDL, - сравнивает их со своим списком и считывает, если надо, очередную порцию новых сообщений, - сравнивает их со своим списком и удаляет невалидное и обработанное, - закрывает соединение.
-
Я так понимаю надёжность имеющегося подхода устраивает. Не устраивает скорость. Проводился ли анализ на каком именно этапе происходит задержка в описанном сценарии? если проблема с POP3 - то может какой-то мэйл-сервер поддерживает какие-то плагины, для которых есть более шустрые протоколы.
-
Выше имелось в виду, что "сравнивает их" = "сравнивает полученные UIDL"
-
> KSergey
ну, скорость у POP3 совершенно нормальная, если прочитанные сообщения удалять сразу после прочтения, и не использовать сервер POP3 в качестве сервера БД
-
У нас Женька (jack128) писал небольшой сервис на шарпе по обработки такого кол-ва заявок (багрепорты от клиентов отсылаемых нам - объем примерно такой-же). Его вариант работает именно так как ты и говоришь 24/7 без сбоев и задержек + сортировка писем по каталогам багтрекера и отсев спама. Вроде бы у него даже все конфигурируется. Щас его позову, может что подскажет
-
Надо на ассемблере все переписать. Быстрее будет :)
-
KSergey © (22.03.16 13:51) [13]
Да, проводился анализ. Узкое место при удалении писем с сервера при наличии в ящике большего количества писем. В pop3 можно удалять письма по их номеру в сессии, а не по UIDL, поэтому при удалении приходится перебирать все письма и сравнивать UIDL
-
Sha © (22.03.16 13:49) [12]
Я примерно понял, что ты хочешь сказать, но хотелось бы для начала услышать, может, какие-то механизмы есть уже готовые. Я попробовал IMAP, но у меня сразу возникли проблемы со скоростью даже на этапе выборки писем. Читаю порцию, запоминаю UID-ы писем, потом удаляю по UID-у. Почему мне не хочется хранить все в памяти и обрабатывать помногу - если произойдет сбой, мне кажется, я могу потерять всю хранящуюся у меня информацию. И да, забыл добавить, повторно отправлять на обработку валидные письма нежелательно.
-
> при удалении приходится перебирать все письма и сравнивать UIDL
зачем перебирать, если можно сразу можно получить список всех пар No:UIDL
-
На счет объема Rouse загнул, на пару порядков поменьше объем будет. И надежности никакой не надо, потерялась сотня сообщений об ошибках, ну и бог с ними, не жалко. Поэтому я просто раз 15 минут подключаюсь считываю все письма на диск, удаляю с сервера и оправляю дальше на обработку, никаких заморочек нет.
-
> Игорь Шевченко © (22.03.16 14:36) [19] > Sha © (22.03.16 13:49) [12] > > Я примерно понял, что ты хочешь сказать, но хотелось бы > для начала услышать, может, какие-то механизмы есть уже > готовые.
Видимо речь про готовую команду UIDL протокола POP3
А нельзя опереться на тот факт, что номера (не UIDL, а порядковые) неудалённых писем от сессии к сессии не меняются? Впрочем, можно же вообще не закрывать POP3 сессию. Подключился - загрузил письма - обработал - если успешно - удалил. Вот тут можно переподключиться и увидеть новые письма. Правда, вероятон медленное удаление писем - фича почтового сервера. ВОзможно - конкретного почтового сервера. Например, для корректной работы разных POP3 сессий (которые теоретически могут быть), плюс приём новых писем.
-
> мне не хочется хранить все в памяти и обрабатывать помногу
размер порции может быть любой
> если произойдет сбой, мне кажется, я могу потерять всю хранящуюся у меня информацию.
во время обработки все данные лежат также на почтовом сервере
> повторно отправлять на обработку валидные письма нежелательно
если не потеряешь список UIDL, то все будет OK
-
jack128 © (22.03.16 14:45) [21]
Увы, не мой вариант
Sha © (22.03.16 14:40) [20]
> зачем перебирать, если можно сразу можно получить список > всех пар > No:UIDL
Не понял, можно подробнее ?
KSergey © (22.03.16 14:54) [22]
> А нельзя опереться на тот факт, что номера (не UIDL, а порядковые) > неудалённых писем от сессии к сессии не меняются?
Я ОЧЕНЬ не уверен, что они не меняются.
> Впрочем, можно же вообще не закрывать POP3 сессию. Подключился > - загрузил письма - обработал - если успешно - удалил. Вот > тут можно переподключиться и увидеть новые письма.
Тут есть один нюанс - все процессы (валидации и оповещения об успешной постановке на обработку) асинхронные. То есть, я отправляю запрос на валидацию и занимаюсь дальше своим делом. Точно также я отправляю запрос на обработку письма и тоже занимаюсь своим делом. В нужный момент мне приходят оповещения об удачной/неудачной валидации и удачной/неудачной постановке на обработку. В обработчиках этих оповещений я и занимаюсь взаимодействием с почтовым сервером на предмет удалить письмо.
-
> Не понял, можно подробнее ? если опустить порядковый номер, то результат (список) вернется для всех порядковых номеров, известных в текущей сессии см https://tools.ietf.org/html/rfc1939
-
Sha © (22.03.16 15:38) [25]
Спасибо, этого я не знал. К сожалению, используемые мной компоненты список не поддерживают. Indy, как я вижу, умеет. Но зтот список будет действительным для номеров в текущей транзакции сервера. После закрытия соединения, мне кажется, я могу список выбросить, нет ?
-
А если на постоянной основе подключится несколькими такими клиентами ( UIDL and UIDL_MASK ) и внешние процессы валидации и обработки подстроить под 'многоклиентность' ?
( я так понимаю на такой режим работы и заточены почтовики, а производительность обмена с внешними процессами увеличится )
-
о, коли они асинхронные, то ( как минимум для теста) так и нужно сделать
-
NoUser © (22.03.16 15:52) [27]
Не понимаю, можно подробнее ?
-
> В обработчиках этих оповещений я и занимаюсь взаимодействием > с почтовым сервером на предмет удалить письмо.
то есть новая сесия POP3 для каждого удаления?
-
> подробнее каждый клиент работает только со своим 'окончанием' UIDL или порядкового номера ( чёт/нечёт, если их два; b00/b01/b02/b03 если четыре )
-
> Но зтот список будет действительным для номеров в текущей транзакции сервера. > После закрытия соединения, мне кажется, я могу список выбросить, нет ?
Да, правильно его получать каждый раз в начале сессии.
> то есть новая сесcия POP3 для каждого удаления?
Удаление происходит по окончании сессии. До этого помеченные на удаление можно вернуть к жизни.
-
Sha © (22.03.16 16:47) [32]
> Да, правильно его получать каждый раз в начале сессии.
Я и так получаю этот список в начале сессии, при получении порции писем. Все письма складываются в некий внутренний список, с которым собственно выполняется работа. Когда мне надо письмо удалить (одно), я зная его UIDL, получаю от сервера письма, спрашиваю UIDL каждого, если совпадает, прошу удалить его и закрываю соединение. Ты предлагаешь при удалении получать полный список UIDL с сервера, найти там номер нужного и удалить, я верно понимаю ?
-
Нет.
Удаление вообще не наша цель. Оно - прибамбас к получению.
Наша цель - получение. Когда нам приходит мысль, что надо получить порцию данных (таймер или сигнал или просто бесконечный цикл), делаем следующее. 1. Открываем соединение и прочитываем список всех UIDL, имеющихся на сервере. 2. Определяем, что там появилось нового. 3. Считываем порцию данных нужного нам размера. 4. Отдаем на валидацию. 5. Глядя в ведущийся нами список подлежащих удалению, помечаем на удаление невалидное и обработанное. 6. Закрываем соединение, при этом произойдет физическое удаление.
По событию завершения валидации - либо помещаем UIDL в список невалидных, - либо передаем сообщение на обработку, UIDL помещаем в список обрабатываемых.
По событию завершения обработки 1. помещаем UIDL в список обработанных 2. если список обрабатываемых пуст, сигналим, что пора принимать.
-
Sha © (22.03.16 18:37) [34]
Саша, спасибо тебе за помощь, но есть нюансы: И валидация и обработка - это асинхронные процессы. Результат валидации может поступить неизвестно когда. Но по результату надо запустить на обработку. Постановка на обработку - это тоже асинхронный процесс, ее результат тоже может придти неизвестно когда. Ты предлагаешь постоянно держать открытый сеанс с почтовым сервером ?
-
> Игорь Шевченко © (22.03.16 23:07) [35] > Ты предлагаешь постоянно держать открытый сеанс с почтовым сервером ?
Нет, конечно.
Основная идея в том, чтобы все время поддерживать непустую очередь на обработку. При этом мы отслеживаем текущее состояние всех скачанных писем (готово к валидации/готово к обработке/готово к удалению).
По таймеру или сигналу об опустошении очереди на обработку выполняем такую единицу работы: 1 Открываем соединение. 2. Считываем оглавление, определяем в нем количество и ID ранее нами не прочитанных писем, скачиваем их (ставя на валидацию и запоминая ID) сколько надо (напр, 10 или 1000 штук) 3. Метим на сервере на удаление те, которые надо было удалить к началу сеанса (т.е. как бы выполняем отложенное удаление). 4. И быстренько закрываем сеанс.
Остальные обработчики событий очевидны. Важно не терять время на индивидуальные удаление писем в отдельных сеансах и на лишние чтения оглавление.
-
Я правильно понял из Игорь Шевченко © (22.03.16 15:05) [24] что письма удаляются по одному за раз?
-
Сергей Суровцев © (23.03.16 02:46) [37]
Да
Sha © (23.03.16 00:14) [36]
Я понял примерно. Ты предлагаешь организовать зеркало писем и заниматься его синхронизацией. Это все хорошо, но если будет ошибка, что станет с этими внутренними структурами ?
> Считываем оглавление, определяем в нем количество и ID ранее > нами не прочитанных писем
Я не до конца понимаю, как определить "ранее не прочитанные письма". Как только я открыл соединение, я считываю содержимое почтового ящика.
-
> Ты предлагаешь организовать зеркало писем
Не совсем. Предлагается вести и надежно хранить только список троек (No,Id,State), полностью описывающих текущее состояние.
> если будет ошибка, что станет с этими внутренними структурами ?
Ничего страшного не произойдет. Максимум, если все рухнет совсем - повторная обработка. А если дублировать тройки хотя бы в INI файле, то вообще никаких последствий.
> как определить "ранее не прочитанные письма"
Ну, мы же в начале каждого сеанса имеем список пар (No,Id) на сервере и такой же список пар для писем, которые скачали в прошлых сеансах. Более того, для всех скачанных писем мы знаем их состояние в техпроцессе (готово к валидации/готово к обработке/готово к удалению).
-
Sha © (23.03.16 11:03) [39]
Я бы все-таки предпочел более простое решение. Почему: Хранить что-то в файловой системе я не могу, потому что проблемы с репликацией. Хранить в базе данных такую информацию как-то пока непонятно, за какую глубину.
Чем мне мое решение кажется проще - подключился к серверу, забрал письма, проверил, обработал, нужное удалил. Все просто и ясно. Если сломается, то рискуем одним письмом максимум (сломались на удалении, письмо будет прочитано и обработано еще раз). Я почему в начале обратился к сообществу пнуть в плане протокола или еще чего - мне не очень нравится переделывать архитектуру, она учитывает много нюансов, которые не всегда возможно описать. Поэтому архитектуру я бы по максимуму хотел сохранить, а это значит, никаких дополнительных хранилищ и т.п.
-
> Хранить что-то в файловой системе я не могу, потому что проблемы с репликацией.
Непонятно. Почему список Id+State нельзя положить в ФС?
> Хранить в базе данных такую информацию как-то пока непонятно, за какую глубину.
Это не вопрос. Все Id, которых нет на почтовом сервере должны сразу удаляться из базы.
-
Sha © (23.03.16 11:29) [41]
Потому что в файловую систему для постоянного хранения нельзя положить ничего.
-
Еще вариант - по одному письму обрабатывать не отключаясь. Делаем LIST, затем RETR 1, обработали, DELE 1, затем RETR 2, ... . Обработали всё, отключаемся. Если сервер в случае разрыва соединения откатывает изменения (этого я не знаю), то раз во сколько-нибудь писем переподключаться.
Можно передачу данных сделать в отельном потоке, а в основном обрабатывать письма. Отдельный поток примерно так работает: 1. RETR 1 2. Отправили 1 на обработку основному потоку. 3. RETR 2 4. Ждем, когда основной поток обработает предыдущее письмо. Ждем, когда POP3 сервер вернет письмо 2. 5. Отправляем 2 на обработку основному потоку. 6. DELE 1 7. RETR 3 И так далее с 4ого пункта, пока письма не кончатся. Если обработка письма осуществляется дольше, чем выборка из pop3 сервера, то простоев не будет. Если обработка письма осуществляется намного больше, то этот алгоритм можно доработать до многопоточной обработки.
В случае сбоя есть риск обработать несколько писем несколько раз. Как я понял - это не критично.
-
DayGaykin © (23.03.16 13:57) [43]
Если после каждого письма ждать, то какой смысл в нескольких потоках?
Игорь же говорит как раз о том, что обработка асинхронная. Кроме всего прочего, прикладных обработчиков писем много, потоков выполнения тоже много.
-
DayGaykin © (23.03.16 13:57) [43]
> В случае сбоя есть риск обработать несколько писем несколько > раз. Как я понял - это не критично.
Критично. Я бы посоветовал почитать все мои посты. Я в одном не могу описать все нюансы.
> Можно передачу данных сделать в отельном потоке, а в основном > обрабатывать письма.
Еще раз советую почитать - обработкой и валидацией занимаются внешние процессы.
Процесс, скорость работы которого в ряде случае меня не устраивает, занимается по сути только сортировкой писем на хорошие и плохие. Хороших 95%. При этом он должен не терять письма в случае сбоев и повторных перезапусков и не отправлять письма на обработку повторно.
-
>Игорь Шевченко © (23.03.16 10:36) [38]
Если Да, то может сделать так же удаление блоком, как и забирается? Накопился блок в 100 писем на удаление, подключаемся и удаляем все сразу. Тут еще надо анализировать скорость работы по участкам системы. То есть взять, к примеру час или сутки, или другой промежуток, главное чтобы он отражал правильную усредненную картину и проверить сколько за это время 1) поступило 2) сколько ушло на обработку 3) сколько вернулось с обработки с пометкой "можно удалить". 4) сколько реально удалено
Если 3 < 1, копать надо не удаление, а обработку. Если 3 ~= 1, но > 4, то реально копать удаление. Если удаляется меньше, чем приходит, то проблема будет только усугубляться с каждой итерацией- список писем растет, удаляется в нем еще медленнее. В идеале этот список вообще не должен разрастаться больше 100 ( размера забираемого блока ).
-
Сергей Суровцев © (23.03.16 16:47) [46]
Я бы посоветовал прочитать еще раз мои посты. Я НЕ МОГУ УДАЛЯТЬ БЛОКОМ, ЕСЛИ ПРОИЗОЙДЕТ СБОЙ, ТО Я ЭТОТ БЛОК ПОТЕРЯЮ
-
>Игорь Шевченко © (23.03.16 17:52) [47]
Что именно ты потеряешь? Список из 50-10 писем, которые УЖЕ надо удалить, ибо они уже ранее успешно обработаны? То есть в худшем случае они пойдут на обработку повторно.
Какова вероятность сбоя? Теоретическая и реальная, ибо система уже ведь работает, статистика есть.
То есть соотношение будет, думаю, приметно таково, что на несколько десятков миллионов писем будет повторный посыл 50-100 на обработку, но пропущено не будет ни одно.
И еще раз про пункт 3) это в первую очередь нужно проверять, возможно собака зарыта там, а не в 4).
-
Сергей Суровцев © (23.03.16 18:27) [48]
"мне не очень нравится переделывать архитектуру, она учитывает много нюансов, которые не всегда возможно описать. Поэтому архитектуру я бы по максимуму хотел сохранить, а это значит, никаких дополнительных хранилищ и т.п. "
> Список из 50-10 писем, которые УЖЕ надо удалить, ибо они > уже ранее успешно обработаны? То есть в худшем случае они > пойдут на обработку повторно.
Как ты думаешь, если повторно будет запущено на обработку снятие с твоего счета денег, это будет какой случай ?
-
Внезапно, в качестве бреда (или мозгового штурма). :) Собственный промежуточный сервер (возможно, с расширенным набором команд), в котором реализовано отложенное удаление писем.
-
> Игорь Шевченко ©
Сервер почтовый не MS Exchange случайно?
-
DVM © (23.03.16 22:49) [51]
Нет. Но сервер может быть разный, в том числе и этот. Мне бы хотелось услышать решение (если есть), не зависящее от возможностей конкретного сервера. Впрочем, если есть какой-то сервер, который гарантировано будет быстро работать, это тоже интересно и может послужить канвой для рекомендаций. Но это уже не моя область.
-
> Игорь Шевченко © (23.03.16 23:02) [52]
Extended MAPI в связке с Exchange на мой взгляд - очень удобное решение для манипулирования большими количествами писем. Кроме всего прочего сервер сам уведомляет о новых письмах.
-
подобное сделал так программа все письма обрабатывает и удаляет, пересылая валидные на другой ящик. Другая программа с другого ящика парсит и данные складывет куда надо, письма с "хорошими данными" пересылает на третий. Что уже не обязательно, в общем-то, но для разбора полетов пригодится
-
А.. валидных 95%.. у меня наоборот )
|