Правильный способ поддерживать много связей с целлулоидом?

В настоящее время я работаю над приложением, которое извлекает почту из многих почтовых ящиков IMAP. Похоже, что Целлулоид не подходит для этой роли, но я не уверен, как нанять актеров.

Приложение будет работать в распределенном режиме. Есть x почтовых ящиков для опроса и y процессов, среди которых они будут разделены. Таким образом, у каждого процесса есть список почтовых ящиков, которые они должны опрашивать, и этот список будет меняться время от времени. Это означает, что пул соединений, поддерживаемых каждым процессом, является динамическим.

Мой самый большой вопрос: должен ли я создавать отдельный актер ImapConnection для каждого почтового ящика или я должен создать один актер ImapListener, который управляет всеми соединениями внутри?

Мой нынешний дизайн показывает прежнее решение. Есть один центральный субъект-координатор, который хранит множество действующих лиц, каждый из которых управляет одним подключением к imap. Новое соединение добавляется с помощью простого:

@connections << ImapConnection.supervise(account_info)

ImapConnection либо опрашивает сервер IMAP через регулярные промежутки времени, либо поддерживает соединение IDLE. Если Координатор хочет прекратить опрос почтового ящика, он ищет его в своем массиве @connections и правильно удаляет его.

Для меня это кажется логичным подходом, который дает много преимуществ Celluloid (например, автоматический перезапуск разбившихся актеров), но я изо всех сил пытаюсь найти примеры другого программного обеспечения, которое использует этот подход. Правильно ли используется модель актера для сотен актеров, или я должен использовать другой подход?

1 ответ

Решение

Очень рад слышать, что вы используете Celluloid, Хороший вопрос.

Не уверен, как вы создаете соединения и поддерживаете их, будь то TCPSocket у вас есть возможность управлять или нет. Если у вас есть возможность управлять TCPSocket напрямую, вы должны использовать Celluloid::IO так же как Celluloid сам. Я также не знаю, куда вы помещаете информацию, извлеченную из подключений IMAP. Эти две вещи влияют на вашу стратегию.

Ваш подход не плохой, но да - его можно улучшить, добавив что-то, чтобы сделать вашу тяжелую работу, опросить рабочих; другой держать account_info только; и последний актер, чтобы запустить работу и / или поддерживать состояние IDLE. Так что вы бы в конечном итоге ImapWorker (бассейн), ImapMaintainer, а также ImapRegistry, Прямо здесь, мне интересно, если вы проводите опрос, нужно ли вам сохранять открытое соединение, а не разрешать передачу информации. Если вы планируете опросить и по-прежнему сохранять открытые связи, вот что сделали бы эти три субъекта:

ImapRegistry держит ваш account_info в Hash, Это будет иметь методы, такие как add, get, а также remove, Я рекомендую Hash из @credentials так что вы можете использовать один и тот же идентификатор между ImapMaintainer а также ImapRegistry; каждый держит живые связи в своем @connectionsи один держит account_info случаи в его @credentials, И то и другое @connections а также @credentials доступны по одному и тому же идентификатору, но один поддерживает энергозависимое соединение, тогда как другой имеет только статические данные, которые можно использовать для воссоздания соединения в случае необходимости. Таким образом, ваши тяжелые атлеты могут умереть, возродиться, и вся система сможет восстановиться.

ImapMaintainer будет иметь фактическое @connections в этом и every( interval ) { } задачи, встроенные в него, добавляются к account_info хранится в ImapRegistry, Я вижу две задачи, в зависимости от того, с какой частотой вы планируете опросить. Одним из них может быть простое прикосновение к соединению IMAP для его поддержания, а другим - опрос сервера IMAP с помощью ImapWorker, ImapWorker будет сохранен в пуле ImapMaintainer как говорят @worker, Так оно и есть @connections, @worker, #polling, а также #keepalive, polling может быть @connections.each ситуации, или у вас может быть таймер для каждого соединения, добавленный в момент создания соединения.

ImapWorker есть два метода... один #touch это поддерживает связь. Основным является #poll, которая принимает поддерживаемое вами соединение и запускает на нем процесс опроса. Этот метод возвращает информацию или, что еще лучше, сохраняет ее, затем работник возвращается к @worker бассейн. Это даст вам преимущество того, что процесс опроса будет проходить в отдельном потоке, а не в отдельном волокне, а также позволит исключить наиболее сложный аспект в самом надежном, но в то же время неосознаваемом виде актера.

Работать задом наперед, если ImapRegistry получает #addхранит account_info и дает это ImapMaintainer который создает соединение и таймеры (но это забывает account_info и только создает соединение и таймер (ы) или просто создает соединение и позволяет одному большому таймеру поддерживать соединение с @worker который является бассейном. 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 и надеюсь, что у вас есть большой успех с этим. Пожалуйста, спросите, хотите ли вы что-нибудь прояснить, но это, по крайней мере, еще одна стратегия, которую вы должны рассмотреть.

Другие вопросы по тегам