在控制器操作之间共享 Net::IMAP 连接
Share a Net::IMAP connection between controller actions
我只有一个控制器和其中的一些操作来处理与 IMAP 相关的不同功能。所以我的问题是我不想为每个动作创建一个单独的连接。例如在一个动作中我可以做类似的事情(它不是实际的代码):
def index
@imap = Net::IMAP.new(server, 993, true)
@imap.login(user, password)
@imap.select("INBOX")
end
再次在同一个控制器内的另一个操作中,如果我需要做一些与 IMAP 相关的事情,那么我将不得不再次创建 @imap
变量。
我是第一次使用 IMAP,所以根据我的理解,每个操作中的 new
方法都会创建另一个到服务器的连接,我听说 google 有连接限制 (15) IMAP 连接数。
我无法序列化此连接对象或将其存储在 Redis 或 Memcached 等任何其他服务中或对其进行缓存,那么我如何才能一次创建此连接并在所有其他操作中使用它,至少在同一个控制器内的操作如果可能的?如果不可能,那么还有其他解决方案来处理这个问题吗?
当然,我可以从邮箱中缓存我需要的数据,但这没什么用,因为还有一些不需要数据的其他操作,需要在邮箱中进行一些操作比如删除邮件,所以需要连接实例。
你如何创建一个包装你的服务对象(单例)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 上有一篇非常好的文章,它将向您展示这两种方法。
如果您希望连接在请求之间保持打开状态,则不能将其作为实例变量存储在控制器中,因为每个请求都有自己的控制器实例。
存储连接的一种方法是使用单例。
这是一个例子:
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 变量。
我只有一个控制器和其中的一些操作来处理与 IMAP 相关的不同功能。所以我的问题是我不想为每个动作创建一个单独的连接。例如在一个动作中我可以做类似的事情(它不是实际的代码):
def index
@imap = Net::IMAP.new(server, 993, true)
@imap.login(user, password)
@imap.select("INBOX")
end
再次在同一个控制器内的另一个操作中,如果我需要做一些与 IMAP 相关的事情,那么我将不得不再次创建 @imap
变量。
我是第一次使用 IMAP,所以根据我的理解,每个操作中的 new
方法都会创建另一个到服务器的连接,我听说 google 有连接限制 (15) IMAP 连接数。
我无法序列化此连接对象或将其存储在 Redis 或 Memcached 等任何其他服务中或对其进行缓存,那么我如何才能一次创建此连接并在所有其他操作中使用它,至少在同一个控制器内的操作如果可能的?如果不可能,那么还有其他解决方案来处理这个问题吗?
当然,我可以从邮箱中缓存我需要的数据,但这没什么用,因为还有一些不需要数据的其他操作,需要在邮箱中进行一些操作比如删除邮件,所以需要连接实例。
你如何创建一个包装你的服务对象(单例)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 上有一篇非常好的文章,它将向您展示这两种方法。
如果您希望连接在请求之间保持打开状态,则不能将其作为实例变量存储在控制器中,因为每个请求都有自己的控制器实例。
存储连接的一种方法是使用单例。
这是一个例子:
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 变量。