HTTP-запрос при создании ActiveRecord

Я пытаюсь подписаться на ленту pubsubhubbub (pshb) на superfeedr. Как работает протокол pshb

  1. Вы отправляете POST в хаб с просьбой подписаться на фид и предоставить обратный звонок
  2. хаб отправляет GET на ваш обратный вызов для проверки вашего намерения подписаться
  3. Вы отвечаете: да, я хочу подписаться
  4. хаб отвечает проверкой подписки

Я запускаю сервер локально на моей машине разработчика. У меня есть код, который может успешно подписаться на канал, я проверяю его, выполняя его непосредственно в консоли rails. Теперь я создал модель Feed ActiveRecord и хочу автоматически подписываться каждый раз, когда создается новая запись в Feed. Я добавил обратный вызов ActiveRecord в модель Feed как таковую

after_create :subscribe_feed

Теперь, когда я создаю активную запись, я вижу, что правильный HTTP-запрос уходит, затем происходит длительное зависание (около 5 секунд, когда ничего не происходит в журналах), а затем приходит ответ от сервера pshb о том, что мой обратный вызов не может быть достигнут,

Вот логи (числа, добавленные мной для ориентиров)

(1)   (0.1ms)  begin transaction
(2)  SQL (4.4ms)  INSERT INTO "feeds" ("created_at", "updated_at", "url") VALUES (?, ?, ?)  [["created_at", Fri, 15 Nov 2013 23:08:39 UTC +00:00], ["updated_at", Fri, 15 Nov 2013 23:08:39 UTC +00:00], ["url", "http://push-pub.appspot.com/feed"]]

(3) pshb subscribe parameters: {:headers=>{"Accept"=>"application/json"}, :body=>{"hub.mode"=>"subscribe", "hub.verify"=>"sync", "hub.callback"=>"http://...myserver.../pub_sub/callback", "hub.topic"=>"http://push-pub.appspot.com/feed", "hub.verify_token"=>"superfeedtest", "format"=>"json"}}

(4) # ABOUT 5 SECOND WAIT

(5) Subscribe Response: #<HTTParty::Response:0x7fd2180cd978 parsed_response="Your callback couldn't be reached.\n", @response=#<Net::HTTPUnprocessableEntity 422 Unprocessable Entity readbody=true>, @headers={"server"=>["nginx/0.8.52"], "date"=>["Fri, 15 Nov 2013 23:08:44 GMT"], "content-type"=>["text/plain; charset=utf-8"], "connection"=>["close"], "status"=>["422 Unprocessable Entity"], "x-runtime"=>["10057"], "content-length"=>["35"], "set-cookie"=>["_superfeedr_session=BAh7BzoMdXNlcl9pZGkC%2BIY6D3Nlc3Npb25faWQiJTBiMDExZGYzNzU4Mjk0MTMxNjc4NmE0OTg3MDhlMjJk--85d29756ae4b5ae9464630741372af7656f3894b; path=/; HttpOnly"], "cache-control"=>["no-cache"], "pubsubhubbub-version"=>["0.3"]}>

(6)   (7.2ms)  commit transaction
Redirected to http://67.180.177.165/feeds/28
Completed 302 Found in 10695ms (ActiveRecord: 11.6ms)

(7) Started GET "/pub_sub/callback?hub.challenge=437c4b3b47aa1dbf4072a2d8abb5c39a&hub.lease_seconds=315360000&hub.mode=subscribe&hub.topic=http%3A%2F%2Fpush-pub.appspot.com%2Ffeed&hub.verify_token=superfeedtest" for 173.255.193.75 at 2013-11-15 15:08:50 -0800

Вы можете видеть, что после ответа происходит транзакция фиксации (6), а затем приходит GET с сервера phsb с просьбой подтвердить (7). Таким образом, концентратор может связаться с моим обратным вызовом, но по какой-то причине я не получаю GET от концентратора до истечения времени ожидания?

Я новичок в rails, поэтому не уверен, как все работает, но я предполагаю, что что-то происходит с параллелизмом запросов в середине создания ActiveRecord. Подписка работает сама по себе, но не при создании ActiveRecord, как я настроил в after_create.

Как правильно обрабатывать HTTP-запросы к сторонним серверам, если вы хотите сделать это при создании записи ActiveRecord?

ЗАМЕЧАНИЕ ** в api superfeedr вы можете указать намерение проверки выполняться асинхронно. Когда я указываю, что он асинхронный, я получаю ответ об успехе, но подписка по-прежнему не создается на концентраторе superfeedr. Я напрямую свяжусь с superfeedr по этому вопросу, но в целом я хочу знать, как справиться с этой ситуацией для API, у которых нет опции асинхронности.

1 ответ

Эта проблема является хорошо известной проблемой Rails в среде devlopment, которая допускает только один одновременный HTTP-запрос. Так как вы выполняете after_create :subscribe_feed во время одного HTTP-запроса, когда Superfeedr пытается проверить ваше намерение, он отправит второй запрос на ваш ввод во время первого. Проверка будет зависать до тех пор, пока не будет выполнен запрос на подписку... вызывая какой-то тупик.

Решение простое: используйте hub.verify=async param при подписке, и Superfeedr сначала закроет запрос на подписку (со статусом 202), а затем выдаст подтверждение намерения.

Вы также можете бросить hub.verify param, так как он асинхронный по умолчанию.

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