受信メールをRailsのコントーラに受け渡すスクリプト
- Railsアプリでメールを受信して処理
- ActionMailer::Base.receive(STDIN.read) とかやる
- 受信のたびに起動とか重いよ!
- SMTPサーバとRailsアプリのサーバが違うと面倒
ってことで、コントローラで処理させるための簡単なスクリプトを書いた。
使うことに以下のようなメリットがある。
- 起動負荷の軽減、高速化
- SMTPサーバとアプリサーバが違っても問題なし
- 受信メール処理用のサーバを複数指定してロードバランシング
改良すべき点
- 処理サーバへのアクセスに失敗した際、キューにためておき、後で試す。
※注意 当然無保証です。自己責任でどうぞ。
#!/usr/bin/ruby # mail-proxy.rb # /etc/aliases # user: "| /path-to-script/mail-proxy.rb" require 'uri' require 'net/http' require 'net/https' module MailProxy class Proxy def initialize(servers) @servers = [] servers.each do |server| @servers << MailProxy::Server.new(server) end end def proxy(str, options = {}) start(str, options) do |server, mail| server.post(mail) end end private def select_server server = @servers[rand(@servers.size).to_i] server = select_server unless server.alive? return server end def start(mail, options, &block) options = { :trial => 30 }.merge(options) success_flag = false options[:trial].to_i.times do server = select_server if block.call(server, mail) success_flag = true break end sleep(1) end success_flag end end class Server attr_reader :uri def initialize(url) @uri = URI.parse(url) end def alive? Net::HTTP.start(uri.host, uri.port){ |http| ; } true rescue false end def post(data) Net::HTTP.start(uri.host, uri.port) do |http| response = http.post( uri.path, data ) return [200, 403].include?(response.code.to_i) end rescue false end end end servers = [ 'http://192.168.0.100:3000/receive/signup', 'http://192.168.0.100:3001/receive/signup', 'http://192.168.0.101:3000/receive/signup', 'http://192.168.0.101:3001/receive/signup'] proxy = MailProxy::Proxy.new(servers) proxy.proxy($stdin.read)
コントローラはこんな感じで。
class ReceiveController < ApplicationController skip_before_filter :verify_authenticity_token class IllegalRequestError < RuntimeError; end # 携帯登録要求メール def signup raise IllegalRequestError.new('許可されていないリクエストです。') if !request.post? || request.raw_post.blank? UserNotify.receive(request.raw_post) render :text => 'メールを受け付けました。' rescue TMail::SyntaxError, IllegalRequestError => e render :status => 403,:text => e.message end end