在控制器操作之间共享Net :: IMAP连接

时间:2017-07-14 11:51:16

标签: ruby-on-rails ruby imap

我只有一个控制器和一些操作来处理与IMAP相关的不同功能。所以我的问题是我不想为每个动作创建一个单独的连接。例如,在一个动作中我可以做类似的事情(它不是真正的代码):

def index
 @imap = Net::IMAP.new(server, 993, true)
 @imap.login(user, password)
 @imap.select("INBOX")
end

再次在同一控制器内的另一个动作中,如果我需要做一些与IMAP相关的事情,那么我将不得不再次创建@imap变量。

我是第一次使用IMAP,因此根据我的理解new方法,每个操作都会创建另一个与服务器的连接,我听说谷歌有连接数限制(15)的IMAP连接数。 / p>

我无法序列化此连接对象或将其存储在任何其他服务(如Redis或Memcached)中或缓存它,那么如何创建此连接一次并将其用于所有其他操作,至少在同一控制器内执行操作(如果可能)?如果不可能,那么任何其他解决方案来处理这个问题?

当然,我可以从邮箱中缓存我需要的数据,但由于还有一些其他不需要数据的操作,因此无法提供帮助,因此需要执行以下操作:邮箱就像删除邮件一样,因此需要连接实例。

2 个答案:

答案 0 :(得分:2)

如何创建包裹Net::IMAP的服务对象(单例)。你可以把它贴在app/services/imap_service.rb或类似的东西上。有关示例的示例:

require 'singleton' # This is part of the standard library
require 'connection_pool' # https://github.com/mperham/connection_pool

class IMAPService
  include Singleton

  def initialize
    @imap = ConnectionPool.new(size: 15) { Net::IMAP.new(server, 993, true) }
  end

  def inbox(user, password)
    @imap.with do |conn|
      conn.login(user, password)
      conn.select("INBOX")
    end
  end
end

您可以像IMAPService.instance一样访问此单例,例如IMAPService.instance.inbox(user, password)。我根据我们的讨论添加了connect_pool gem,以确保它是线程安全的。 IMAPService上没有attr_reader :imap。但是,如果您不想在此处包含所有必需的方法,则可以添加一个以便可以直接访问代码中的连接池(尽管我建议尽可能使用服务对象)。然后你可以做IMAPService.instance.imap.with { |conn| conn.login(user, password) }而不需要依赖IMAPService中的方法。

值得注意的是,您不必使用Singleton mixin。 Implementing "the lovely" Singleton上有一篇非常好的文章,它将告诉你两种方法。

答案 1 :(得分:0)

如果您希望连接在请求之间保持打开状态,则无法将其作为实例变量存储在控制器中,因为每个请求都有自己的控制器实例。

存储连接的一种方法是使用单例。

以下是一个例子:

class ImapService

  attr_accessor :imap

  def initialize
    @imap = Net::IMAP.new("imap.gmail.com", 993, true)
    @imap.login("username@gmail.com", "password")
    @imap.select("INBOX")
  end

  @@instance = ImapService.new
  private_class_method :new

  def self.instance
    return @@instance
  end
end

这将在您第一次访问时打开连接,如果再次访问它,它将使用旧连接。

您可以在应用程序的任何位置使用ImapService.instance.imap访问imap变量。

相关问题