3

我目前正在开发一个从许多 IMAP 邮箱中提取邮件的应用程序。看起来赛璐珞很适合这个角色,但我不确定如何聘请演员。

该应用程序将以分布式方式运行。有x 个要轮询的邮箱和y个进程,这些邮箱将在其中进行划分。所以每个进程都有一个他们必须轮询的邮箱列表,这个列表会不时改变。这意味着每个进程维护的连接池是动态的。

我最大的问题是:我应该为每个邮箱生成一个单独的 ImapConnection 演员,还是应该制作一个 ImapListener 演员来管理内部所有连接?

我目前的设计采用了前一种解决方案。有一个中央 Coordinator actor 保持一系列 actor,每个 actor 管理一个 imap 连接。一个新的连接被添加了一个简单的:

@connections << ImapConnection.supervise(account_info)

ImapConnection 要么定期轮询 IMAP 服务器,要么保持一个空闲连接。如果 Coordinator 想要停止轮询邮箱,它会在其 @connections 数组中查找它并正确处理它。

这对我来说似乎是一种合乎逻辑的方法,它产生了赛璐珞的许多好处(例如自动重启崩溃的演员),但我正在努力寻找使用这种方法的其他软件的例子。以这种方式生成 100 个演员是否正确使用了演员模型,还是我应该使用不同的方法?

4

1 回答 1

5

很高兴听到您正在使用Celluloid. 好问题。

不确定您如何创建和维护连接,无论TCPSocket您是否有能力管理。如果你有能力TCPSocket直接管理一个,你应该和它本身Celluloid::IO一样使用Celluloid。我也不知道你把从 IMAP 连接中提取的信息放在哪里。这两件事会影响你的策略。

您的方法还不错,但是是的-可以通过添加一些东西来完成繁重的投票工作来改进它;另一个只持有account_info;以及触发工作和/或维持空闲状态的最终参与者。所以你最终会得到ImapWorker(a pool) ImapMaintainer, 和ImapRegistry. 在这里,我想知道既然您正在轮询,是否需要保持开放连接而不是允许推送信息。如果您计划进行轮询并仍然保持连接打开,那么这三个参与者会执行以下操作:

ImapRegistry把你account_info放在一个Hash. 这将有方法,如add,getremove。我推荐一个Hashof这样你就可以在and@credentials之间使用相同的 ID ;一个在其中保存实时连接,一个在其中保存实例。两者和都由相同的 ID 访问,但一个保持易失连接,而另一个只有静态数据可用于在必要时重新创建连接。通过这种方式,您的举重运动员可能会死亡,重生,整个系统可以自行再生。ImapMaintainerImapRegistry@connectionsaccount_info@credentials@connections@credentials

ImapMaintainer将在其中包含实际@connections内容,并在其中every( interval ) { }内置任务,添加到 whenaccount_info存储在ImapRegistry. 我看到了两项任务,具体取决于您计划轮询的频率。一种可能是简单地触摸 IMAP 连接来维护它,另一种可能是使用 . 轮询 IMAP 服务器ImapWorker。就像说的那样,ImapWorker将是一个保存在池中的池。所以它有, ,和。可能是一种情况,或者您可以在创建连接时为每个连接添加一个计时器。ImapMaintainer@worker@connections@worker#polling#keepalivepolling@connections.each

ImapWorker有两种方法...一种是#touch保持连接活跃。主要的是#poll,它接受您维护的连接,并在其上运行轮询过程。该方法返回信息,甚至更好地存储它,然后工作人员返回到@worker池中。这将给您带来的好处是让轮询过程发生在一个单独的线程中,而不仅仅是一个单独的光纤,并且还允许将最棘手的方面排除在最强大但最不知情的类型的参与者之外。

向后工作,如果ImapRegistry接收#add,它存储account_info并提供ImapMaintainer创建连接的对象和计时器(但它忘记account_info并仅创建连接和计时器,或者只是创建连接并让一个大计时器维护与@worker它的连接pool.ImapMaintainer不可避免地会碰到计时器,因此在计时器的开始和结束时,它可以检查其连接。如果连接由于某种原因消失了,它可以用@registry.get信息重新创建它。在其计时器提示的任务中,它可以运行@worker.poll@worker.alive.

这说明了上述要求,显示了初始化器如何将参与者系统组合在一起,并且具有所提到的方法的不完整框架。

 WORKERS = 9 #de arbitrarily chosen

 class ImapRegistry
     include Celluloid

     def initialize
         @maintainer = ImapMaintainer.supervise
         @credentials = {}
     end

     def add( account_info )
         ...
     end

     def get( id )
         ...
     end

     def remove( id )
         ...
     end
 end

 class ImapMaintainer
     include Celluloid

     def initialize
         @worker = ImapWorker.pool size: WORKERS
         @connections = {}
     end

     def add( id, credential )
         ...
     end

     def remove( id )
         ...
     end

     #de These exist if there is one big timer:
     def polling
         ...
     end

     def keepalive
         ...
     end
 end

 class ImapWorker
     include Celluloid

     def initialize
         #de Nothing needed.
     end

     def poll( connection )
         ...
     end

     def touch( connection )
         ...
     end
 end

 registry = ImapRegistry.supervise

我喜欢Celluloid并希望你能取得很大的成功。请询问您是否需要澄清任何事情,但这至少是您需要考虑的另一种策略。

于 2013-12-06T09:20:06.990 回答