RabbitMQ + кроссовки: один рабочий или 100? - PullRequest
0 голосов
/ 31 октября 2018

Допустим, мне нужно сделать сложные вычисления для 100 пользователей. Моя текущая конфигурация выглядит так:

производитель

class Producer
  class << self
    def publish(target, options = {})
      connection = Bunny.new(some_params).start
      channel    = connection.create_channel
      exchange   = channel.fanout("#{target}_exchange", durable: true)

      exchange.publish(options.to_json)
    end
  end
end

MassComplexCalculations рабочий

module UsersWorkers
  class MassComplexCalculations
    include Sneakers::Worker

    from_queue "#{ENV['RAILS_ENV']}.users.mass_complex_calculations_queue",
               exchange: "#{ENV['RAILS_ENV']}.users.mass_complex_calculations_exchange"

    def work(options)
      parsed_options = JSON.parse(options)

      ActiveRecord::Base.connection_pool.with_connection do
        User.where(id: parsed_options['ids']).each do |user|
          ::Services::Users::ComplexCalculations.call(user)
        end
      end
      ack!
    end
  end
end

бегущий рабочий

Producer.publish("#{ENV['RAILS_ENV']}.users.mass_complex_calculations", ids: User.limit(100).ids)

Я не совсем понимаю, как AMQP распределяет ресурсы для выполнения задач и как я могу помочь. Правильно ли, что каждый расчет лучше проводить на отдельном работнике? Например:

ИЗМЕНЕНО MassComplexРасчет работника

module UsersWorkers
  class MassComplexCalculations
    include Sneakers::Worker

    from_queue "#{ENV['RAILS_ENV']}.users.mass_complex_calculations_queue",
               exchange: "#{ENV['RAILS_ENV']}.users.mass_complex_calculations_exchange"

    def work(options)
      parsed_options = JSON.parse(options)

      ActiveRecord::Base.connection_pool.with_connection do
        parsed_options['ids'].each do |id|
          Producer.publish("#{ENV['RAILS_ENV']}.users.personal_complex_calculations", id: id)
        end
      end
      ack!
    end
  end
end

NEW PersonalComplexCalculations рабочий

module UsersWorkers
  class PersonalComplexCalculations
    include Sneakers::Worker

    from_queue "#{ENV['RAILS_ENV']}.users.personal_complex_calculations_queue",
               exchange: "#{ENV['RAILS_ENV']}.users.personal_complex_calculations_exchange"

    def work(options)
      parsed_options = JSON.parse(options)
      user           = User.find(parsed_options['id'])

      ActiveRecord::Base.connection_pool.with_connection do
        ::Services::Users::ComplexCalculations.call(user)
      end
      ack!
    end
  end
end

В моем понимании, возможны два варианта:

  1. первая реализация может работать медленнее, потому что она будет вызывать службу для каждого пользователя, в то время как во втором варианте у нас будет 100 одновременно работающих работников, которые будут выполнять свою работу параллельно
  2. разницы нет

Так какой подход лучше? А может, даже один из них совершенно не прав?

Заранее спасибо.

1 Ответ

0 голосов
/ 31 октября 2018

Ни одно из ваших предположений не подтвердится. Вам не гарантируется наличие 100 параллельных рабочих, поскольку кроссовки имеют размер пула потоков по умолчанию, который вы не обязательно переопределяете:

https://github.com/jondot/sneakers/blob/master/lib/sneakers/worker.rb#L20

И если у вас нет настроенного пула соединений ActiveRecord, по крайней мере, 100 подключений, ваш код также будет блокироваться и ждать из-за нехватки ресурсов здесь.

в ОБЩЕМ, выполнение такого рода задач параллельно, вероятно, будет быстрее большую часть времени - но это не гарантируется.

...