Почтальон сохраняет почту несколько раз
Я включил самоцвет почтальона в мой проект rails. Он успешно получает письма из Gmail. В моем приложении есть модельное Сообщение для моих писем. Письма правильно сохранены как модель сообщения.
Проблема в том, что электронные письма иногда сохраняются несколько раз, и я не могу распознать шаблон. Некоторые электронные письма сохраняются один раз, некоторые два раза, а некоторые сохраняются три раза.
Но я не могу найти ошибку в моем коде.
Вот мой скрипт mailman_server:
Сценарий / mailman_server
#!/usr/bin/env ruby
# encoding: UTF-8
require "rubygems"
require "bundler/setup"
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment'))
require 'mailman'
Mailman.config.ignore_stdin = true
#Mailman.config.logger = Logger.new File.expand_path("../../log/mailman_#{Rails.env}.log", __FILE__)
if Rails.env == 'test'
Mailman.config.maildir = File.expand_path("../../tmp/test_maildir", __FILE__)
else
Mailman.config.logger = Logger.new File.expand_path("../../log/mailman_#{Rails.env}.log", __FILE__)
Mailman.config.poll_interval = 15
Mailman.config.imap = {
server: 'imap.gmail.com',
port: 993, # usually 995, 993 for gmail
ssl: true,
username: 'my@email.com',
password: 'my_password'
}
end
Mailman::Application.run do
default do
begin
Message.receive_message(message)
rescue Exception => e
Mailman.logger.error "Exception occurred while receiving message:\n#{message}"
Mailman.logger.error [e, *e.backtrace].join("\n")
end
end
end
Письмо обрабатывается в моем классе сообщений:
def self.receive_message(message)
if message.from.first == "my@email.com"
Message.save_bcc_mail(message)
else
Message.save_incoming_mail(message)
end
end
def self.save_incoming_mail(message)
part_to_use = message.html_part || message.text_part || message
if Kontakt.where(:email => message.from.first).empty?
encoding = part_to_use.content_type_parameters['charset']
Message.create topic: message.subject, message: part_to_use.body.decoded.force_encoding(encoding).encode('UTF-8'), communication_partner: message.from.first, inbound: true, time: message.date
else
encoding = part_to_use.content_type_parameters['charset']
Message.create topic: message.subject, message: part_to_use.body.decoded.force_encoding(encoding).encode('UTF-8'), communication_partner: message.from.first, inbound: true, time: message.date, messageable_type: 'Company', messageable_id: Kontakt.where(:email => message.from.first).first.year.id
end
end
def self.save_bcc_mail(message)
part_to_use = message.html_part || message.text_part || message
if Kontakt.where(:email => message.to.first).empty?
encoding = part_to_use.content_type_parameters['charset']
Message.create topic: message.subject, message: part_to_use.body.decoded.force_encoding(encoding).encode('UTF-8'), communication_partner: message.to.first, inbound: false, time: message.date
else
encoding = part_to_use.content_type_parameters['charset']
Message.create topic: message.subject, message: part_to_use.body.decoded.force_encoding(encoding).encode('UTF-8'), communication_partner: message.to.first, inbound: false, time: message.date, messageable_type: 'Company', messageable_id: Kontakt.where(:email => message.to.first).first.year.id
end
end
Я демонизировал mailman_server с помощью этого скрипта:
Сценарий / mailman_daemon
#!/usr/bin/env ruby
require 'rubygems'
require "bundler/setup"
require 'daemons'
Daemons.run('script/mailman_server')
Я использую Capistrano.
Это те части, которые отвечают за остановку, запуск и перезапуск моего mailman_server:
Сценарий / deploy.rb
set :rails_env, "production" #added for delayed job
after "deploy:stop", "delayed_job:stop"
after "deploy:start", "delayed_job:start"
after "deploy:restart", "delayed_job:restart"
after "deploy:stop", "mailman:stop"
after "deploy:start", "mailman:start"
after "deploy:restart", "mailman:restart"
namespace :deploy do
desc "mailman script ausfuehrbar machen"
task :mailman_executable, :roles => :app do
run "chmod +x #{current_path}/script/mailman_server"
end
desc "mailman daemon ausfuehrbar machen"
task :mailman_daemon_executable, :roles => :app do
run "chmod +x #{current_path}/script/mailman_daemon"
end
end
namespace :mailman do
desc "Mailman::Start"
task :start, :roles => [:app] do
run "cd #{current_path};RAILS_ENV=#{fetch(:rails_env)} bundle exec script/mailman_daemon start"
end
desc "Mailman::Stop"
task :stop, :roles => [:app] do
run "cd #{current_path};RAILS_ENV=#{fetch(:rails_env)} bundle exec script/mailman_daemon stop"
end
desc "Mailman::Restart"
task :restart, :roles => [:app] do
mailman.stop
mailman.start
end
end
Может ли быть так, что несколько экземпляров почтового сервера запускаются во время моего развертывания почти одновременно, а затем каждый экземпляр опрашивает почти одновременно? Пулы второго и третьего экземпляров перед первым экземпляром помечают письмо как прочитанное, а также опрашивают и обрабатывают письмо?
Обновление 30.01.
Я установил интервал опроса до 60 секунд. но это ничего не меняет.
Я проверил папку, в которой хранится pid-файл почтальона. есть только один файл pid почтальона. Таким образом, определенно работает только один почтовый сервер. Я проверил лог-файл и вижу, что сообщения выбираются несколько раз:
Mailman v0.7.0 started
IMAP receiver enabled (my@email.com).
Polling enabled. Checking every 60 seconds.
Got new message from 'my.other@email.com' with subject 'Test nr 0'.
Got new message from 'my.other@email.com' with subject 'Test nr 1'.
Got new message from 'my.other@email.com' with subject 'test nr 2'.
Got new message from 'my.other@email.com' with subject 'test nr 2'.
Got new message from 'my.other@email.com' with subject 'test nr 3'.
Got new message from 'my.other@email.com' with subject 'test nr 4'.
Got new message from 'my.other@email.com' with subject 'test nr 4'.
Got new message from 'my.other@email.com' with subject 'test nr 4'.
Так что мне кажется, что проблема определенно в коде моего почтового сервера.
Обновление 31.1.
Мне кажется, это как-то связано с моей производственной машиной. когда я тестирую это в разработке с точно такой же конфигурацией (изменил мою локальную базу данных с sqlite на mysql этим утром, чтобы протестировать ее), так как на рабочей машине я не получаю дубликаты. Вероятно, все в порядке с моим кодом, но есть проблема с рабочей машиной. Спросите моего хостера, могут ли они найти решение для этого. Чтобы исправить это, я пойду с предложением Арьеяна.
Решение: я нашел проблему. Я развернул на машине, где каталог tmp является общим для всех выпусков. Я забыл определить путь, по которому должен быть сохранен pid-файл mailman_daemon. Поэтому он был сохранен в каталоге сценариев вместо каталога /tmp/pids. Из-за этого старый mailman_daemon не может быть остановлен после нового развертывания. Это привело к армии рабочих mailman_daemon s, которые опрашивали мой почтовый аккаунт... После уничтожения всех этих процессов все прошло хорошо! Нет больше дубликатов!
2 ответа
Я нашел проблему. Я развернул на машине, где каталог tmp является общим для всех выпусков. Я забыл определить путь, по которому должен быть сохранен pid-файл mailman_daemon. Поэтому он был сохранен в каталоге сценариев вместо каталога /tmp/pids. Из-за этого старый mailman_daemon не может быть остановлен после нового развертывания. Это привело к армии рабочих mailman_daemon s, которые опрашивали мой почтовый аккаунт... После уничтожения всех этих процессов все прошло хорошо! Нет больше дубликатов!
Это может быть проблемой параллелизма / синхронизации. Например, новые письма импортируются до того, как те, которые в настоящее время обрабатываются, будут сохранены.
Изменить: только что заметил, что у вас есть Mailman.config.poll_interval
установить на 15. Это означает, что он будет проверять наличие новых сообщений каждые 15 секунд. Попробуйте увеличить это значение до значения по умолчанию 60 секунд. Независимо от этого параметра, было бы неплохо добавить код дедупликации, о котором я упоминал ниже.
Мой совет будет также хранить message_id
с каждого письма, так что вы можете легко найти дубликаты.
Вместо:
Message.create(...)
делать:
# This makes sure you have the latest pulled version.
message = Message.find_or_create(message_id: message.message_id)
message.update_attributes(...)
# This makes sure you only import it once, then ignore further duplicates.
if !Message.where(message_id: message.message_id).exists?
Message.create(...)
end
Для получения дополнительной информации о message_id
: http://rdoc.info/github/mikel/mail/Mail/Message
Помните, что электронная почта и imap не должны быть согласованными хранилищами данных, как вы ожидаете от Postgres или Mysql. Надеюсь, это поможет вам разобраться с дубликатами писем.