2014-01-29 45 views
2

我已經把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: '[email protected]', 
    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 == "[email protected]" 
     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。

我已經設置輪詢intervall 60秒。但這並沒有改變。

我檢查了mailman pid文件的存儲位置。只有一個郵遞員pid文件。所以肯定只有一個郵遞員服務器在運行。我查了日誌文件,然後可以看到,該消息是取多次:

Mailman v0.7.0 started 
IMAP receiver enabled ([email protected]). 
Polling enabled. Checking every 60 seconds. 
Got new message from '[email protected]' with subject 'Test nr 0'. 
Got new message from '[email protected]' with subject 'Test nr 1'. 
Got new message from '[email protected]' with subject 'test nr 2'. 
Got new message from '[email protected]' with subject 'test nr 2'. 
Got new message from '[email protected]' with subject 'test nr 3'. 
Got new message from '[email protected]' with subject 'test nr 4'. 
Got new message from '[email protected]' with subject 'test nr 4'. 
Got new message from '[email protected]' with subject 'test nr 4'. 

所以這似乎對我來說,這個問題肯定是我的郵差服務器代碼。

更新31.1。

對我來說,這與我的生產機器有關。當我使用完全相同的配置在開發中測試這個(在今天早上將我的本地數據庫從sqlite改爲mysql來測試它),就像在生產機器上一樣,我不會重複。可能是我的代碼一切正常,但生產機器存在問題。請問我的主機是否可以看到解決方案。爲了解決這個問題,我會和Ariejan的建議一起去解決。

解決方案: 我發現了這個問題。我部署到一臺機器,其中tmp目錄是所有版本之間共享的目錄。我忘了定義保存mailman_daemon的pid文件的路徑。所以它被保存在腳本目錄中而不是/ tmp/pids目錄中。因此,新的部署後無法停止舊的mailman_daemon。這導致了一羣工作mailman_daemons投票我的mailaccount ......殺死所有這些進程後,一切順利!沒有更多的重複!

+1

嗨railsnewbie,似乎是一個棘手的問題。我冒昧地贊助你在這裏的編碼問題 - http://www.codersclan.net/ticket/211 – Dror

回答

0

我發現了這個問題。我部署到一臺機器,其中tmp目錄是所有版本之間共享的目錄。我忘了定義保存mailman_daemon的pid文件的路徑。所以它被保存在腳本目錄中而不是/ tmp/pids目錄中。因此,新的部署後無法停止舊的mailman_daemon。這導致了一羣工作mailman_daemons投票我的mailaccount ......殺死所有這些進程後,一切順利!沒有更多的重複!

2

這可能是一些併發/時間問題。例如。新的郵件在當前處理的郵件被保存之前導入。


編輯:剛纔注意到你有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_idhttp://rdoc.info/github/mikel/mail/Mail/Message#message_id-instance_method

請記住,電子郵件和IMAP並不意味着是一致的數據存儲喜歡你」 d期待Postgres或Mysql。希望這可以幫助你理清重複的郵件。

+0

我已經把intervall設置爲60,但這並沒有改變。我會根據您的建議來存儲message_id,並在保存新消息之前檢查它。 – coderuby

+0

我會讓這個問題一直持續到明天。不能相信這是郵遞員服務器的正常行爲。我現在用一些新的信息更新我的問題。當沒有可以回答爲什麼明天郵件會被多次導入的問題的郵件時,我會接受你的回答。謝謝你的努力! – coderuby

+0

我根據您的建議更新了我的代碼。但我仍然重複郵件。我查看了你的代碼並理解了這一部分:if!Message.where(message_id:message.message_id).exists? Message.create(...)結束。但是第一部分大部分都不一樣?首先,它在數據庫中找到具有相同message_id的Message對象,當它不在那裏時,它實例化一個新對象。在這兩種情況下,它都會更新對象並將其保存到數據庫中。如何確保我擁有最新的拉動版本?它只檢查數據庫,不是嗎? – coderuby