etspring
@etspring
Начитанное быдло

Как правильно использовать http persistent connection в ruby?

Доброго времени суток.

Имеется задача: Опрашивать внешний API, который отдает определенный набор данных на глубину 180+ дней от текущей даты. POST-запрос отправляется в формате XML, в нем задаются 3 параметра "ИЗ ГОРОДА", "В ГОРОД", "ДАТА".
API поддерживает обработку нескольких направлений ( до 40 ) на одну дату.
API поддерживает множественные подключения ( до 10 одновременных ) и множественные запросы ( до 100 / сек )

Так как направлений много - примерно 75_000, то кол-во запросов = 13_500_000 если опрашивать каждое на каждый день на глубину 180 дней от текущей даты.
Для экономии времени на опросах направлений решено использовать http persistent connection чтобы сократить время на переоткрытие соединенния для каждого запроса + запрашивать разом 40 направлений. А так же разогнать это при помощи threads/forks.
В качестве http-клиента используется gem 'net-http-persistent', '4.0.0'

Вопрос - как это правильно сделать? Опрос при помощи Python-скрипта показывает в разы лучшую производительность
На ruby делается так:
dates = ['2020-11-10', '2020-11-11', '2020-11-12', .... ]
directions.each_slice(7500) do |slice|
  fork do
    fork_counter = 10
    Signal.trap("CLD")  { fork_counter += 1 } 
    #
    uri = URI("https://api.url/")
    http = Net::HTTP::Persistent.new(name: "slice_#{rand(1..10000)}")
    #
    http.max_requests = 100000000
    http.keep_alive = 600
    http.read_timeout = 5
    http.reuse_ssl_sessions = true
    #
    post = Net::HTTP::Post.new(uri)
    #
    slice.each_slice(40) do |directions_to_ask|
      Process.wait if fork_counter <= 0
      fork_counter -= 1
          headers = { 'Content-Type' => 'text/xml', 'Accept-Encoding' => 'gzip', 'Connection' => 'Keep-Alive' }
          headers.map{ |k,v| post.add_field(k,v) }
          segments = directions_to_ask.map{ |x|
            "<OriginDestination date='#{date}' origin='#{x[0]}' destination='#{x[1]}'></OriginDestination>"
          }
          data = <<-SOAP
            <SOAP-ENV:Envelope>
             .....
             .....
              <SOAP-ENV:Body><SchedulesAvailability">#{segments.join("\n")}</SchedulesAvailability></SOAP-ENV:Body>
            </SOAP-ENV:Envelope>
          SOAP

          post.body = data
          response = http.request(uri, post)
    end
    http.shutdown
  end
end
Process.waitall


Что можно исправить/ускорить?
Возможно есть ошибка. Заранее спасибо за ответ.
  • Вопрос задан
  • 104 просмотра
Пригласить эксперта
Ответы на вопрос 2
c3gdlk
@c3gdlk
Ментор в http://rubyboost.ru/
Попробуйте сначала организовать коннекшн пул, а потом в циклах брать свободный. Ну и thread быстрее чем форк. Смысл форка только если в внутри много трендов, а у вас максимум 10.
Ответ написан
На ruby делается так:
Интересный огород.
Попробуйте concurrent-ruby, async-http.
Если найду код, добавлю.

Насчет fork верно замечено, Запорожченко Олег.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы