2017-07-14 49 views
3

我只有一个控制器和一些动作来处理与IMAP相关的不同功能。所以我的问题是我不想为每个动作创建一个单独的连接。例如,在一个动作我可以这样做(这是不实际的代码):在控制器动作之间共享一个Net :: IMAP连接

def index 
@imap = Net::IMAP.new(server, 993, true) 
@imap.login(user, password) 
@imap.select("INBOX") 
end 
在同一个控制器内的另一个动作

再次,如果我需要做的IMAP那么相关的东西,我将不得不创建再次变量@imap

我正在使用IMAP第一次按照我的理解new在每个动作中的方法将创建另一个连接到服务器,我听说谷歌有关于IMAP连接数量的连接限制(15)。

我不能序列化这个连接对象或将它存储在任何其他服务中,比如Redis或Memcached,或者缓存它,那么我该如何创建这个连接一次并使用它所有其他的动作,如果可能的话至少在同一个控制器内部动作?如果不可能,那么任何其他解决方案来处理这个问题?

当然,我可以从邮箱中缓存我需要的数据,但这无法帮助很多,因为有一些其他操作不需要数据,所以需要在邮箱中执行一些操作删除邮件,这样就需要连接实例。

+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中的方法。

值得注意的是,您不必使用mixin。在Implementing "the lovely" Singleton上有一篇非常好的文章,它将向您展示如何做到这一点。

+0

这看起来不错,所以我会尝试,但我仍然会等待任何其他答案。如果我没有得到更好的答案,那么我会接受你的答案。 – Sajan

+0

有一个问题,我们需要attr_reader吗? – Sajan

+0

不,这只是一种习惯 - 我把它删除了。我还更新了我的评论,建议首先执行服务对象而不是应用程序控制器对象,因为它保证保持其他答案指出的状态。我们也有两种不同风格的单人写作(我在文章中已经介绍过),但我更喜欢我的风格,因为在快速阅读时更容易理解。不会有性能问题,如果是的话,你可以手动完成(但不会)。不过,我通常建议不要优化,除非它是一个瓶颈。 – Dbz

0

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

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

下面是一个例子:

class ImapService 

    attr_accessor :imap 

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

    @@instance = ImapService.new 
    private_class_method :new 

    def self.instance 
    return @@instance 
    end 
end 

这将打开该连接的第一次访问它,如果你再次访问它,它就会使用旧的链接。

您将在您的应用程序的任何位置访问带有ImapService.instance.imap的imap变量。

+0

嘿谢谢你的答案。我有一个担心,你能说它是线程安全的吗?并且将用户的imap连接暴露给puma的多线程模式中的另一个用户,因为这将默认共享类?也请阅读Semjon对其他答案的评论。 – Sajan

+0

此解决方案在多线程环境中也可能有一些意外的行为。 在我的例子中,它将共享相同的连接,并与多个用户,这将是一个问题,我会用更好的解决方案更新我的答案。 –