Кроссовки rabbitmq +: один рабочий или 100?

Допустим, мне нужно провести сложные вычисления для 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 worker

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 распределяет ресурсы для выполнения задач и чем могу помочь. Верно ли, что лучше было бы запускать каждый расчет в отдельном воркере? Например:

ИЗМЕНЕНО MassComplexCalculations worker

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

НОВИНКА PersonalComplexCalculations worker

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. нет никакой разницы

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

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

0
0
491
1

Ответы 1

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

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

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

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

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