ruby-on-rails - Mailman多次保存邮件

标签 ruby-on-rails capistrano daemon mailman-gem

我已将 mailman gem 集成到我的 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: '<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="f4998db49199959d98da979b99" rel="noreferrer noopener nofollow">[email protected]</a>',
    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

电子邮件在我的 Message 类中处理:

  def self.receive_message(message)
    if message.from.first == "<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="385541785d55595154165b5755" rel="noreferrer noopener nofollow">[email protected]</a>"
      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

是否可能是在我的部署过程中几乎同时启动了 mailman 服务器的多个实例,然后每个实例几乎同时进行轮询?第一个实例之前的第二个和第三个实例池将电子邮件标记为已读并轮询并处理电子邮件?

更新 30.01。

我已将轮询间隔设置为 60 秒。但这没有任何改变。

我检查了存储mailman pid文件的文件夹。只有一个mailman pid 文件。所以肯定只有一台邮件服务器在运行。我检查了日志文件,可以看到消息被多次获取:

Mailman v0.7.0 started
IMAP receiver enabled (<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="1a77635a7f777b737634797577" rel="noreferrer noopener nofollow">[email protected]</a>).
Polling enabled. Checking every 60 seconds.
Got new message from '<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="a2cfdb8ccdd6cac7d0e2c7cfc3cbce8cc1cdcf" rel="noreferrer noopener nofollow">[email protected]</a>' with subject 'Test nr 0'.
Got new message from '<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="bed3c790d1cad6dbccfedbd3dfd7d290ddd1d3" rel="noreferrer noopener nofollow">[email protected]</a>' with subject 'Test nr 1'.
Got new message from '<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="85e8fcabeaf1ede0f7c5e0e8e4ece9abe6eae8" rel="noreferrer noopener nofollow">[email protected]</a>' with subject 'test nr 2'.
Got new message from '<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="87eafea9e8f3efe2f5c7e2eae6eeeba9e4e8ea" rel="noreferrer noopener nofollow">[email protected]</a>' with subject 'test nr 2'.
Got new message from '<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="f69b8fd899829e9384b6939b979f9ad895999b" rel="noreferrer noopener nofollow">[email protected]</a>' with subject 'test nr 3'.
Got new message from '<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="9cf1e5b2f3e8f4f9eedcf9f1fdf5f0b2fff3f1" rel="noreferrer noopener nofollow">[email protected]</a>' with subject 'test nr 4'.
Got new message from '<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="076a7e2968736f627547626a666e6b2964686a" rel="noreferrer noopener nofollow">[email protected]</a>' with subject 'test nr 4'.
Got new message from '<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="375a4e1958435f524577525a565e5b1954585a" rel="noreferrer noopener nofollow">[email protected]</a>' with subject 'test nr 4'.

所以在我看来,问题肯定出在我的邮件服务器代码中。

更新 31.1。

在我看来,这与我的生产机器有关。当我在开发中使用与生产机器完全相同的配置(今天早上将本地数据库从 sqlite 更改为 mysql 以进行测试)来测试它时,我没有得到重复项。我的代码可能一切正常,但生产机器有问题。会询问我的房东是否可以找到解决方案。为了解决这个问题,我将采纳 Ariejan 的建议。

解决方案: 我发现了问题。我部署到一台机器,其中 tmp 目录是所有版本之间共享的目录。我忘记定义 mailman_daemon 的 pid 文件的保存路径。所以它被保存在脚本目录中而不是/tmp/pids目录中。因此,旧的 mailman_daemon 在新部署后无法停止。这导致了一大群正在工作的 mailman_daemons 正在轮询我的邮件帐户......在杀死所有这些进程后,一切都很顺利!不再有重复!

最佳答案

这可能是一些并发/计时问题。例如。在保存当前处理的邮件之前导入新邮件。


编辑:刚刚注意到您将 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#message_id-instance_method

请记住,电子邮件和 imap 并不像您期望的 Postgres 或 Mysql 那样是一致的数据存储。希望这可以帮助您解决重复邮件。

关于ruby-on-rails - Mailman多次保存邮件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21442720/

相关文章:

javascript - 如何告诉谷歌地图 API 加载脚本在 React 组件中寻找回调?

用于读取电子邮件和创建 JIRA 票证的 Java 守护进程?

python-3.x - python - 守护 bottlepy 脚本

c - 将 STDERR 从服务重定向到系统日志

ruby-on-rails - 使用 mina 部署子目录

开源项目和不同环境中的 Capistrano

ruby-on-rails - 如何使用 esbuild 作为 JavaScript bundler 自动加载 Rails 7 中的所有 js Controller 文件

ruby-on-rails - 将 MongoDB 中的 _id 类型更改为整数是否不好?

ruby-on-rails - 一般 Active Record 加入或包含

git - capistrano ssh 代理转发 - 权限被拒绝(公钥)