В прошлом году мы много работали над использованием тайм-аутов. Если вы не укажете тайм-аут при создании соединения, наш HTTP-клиент, обернутый We :: Call, вызовет исключение. Это привело к тому, что от всей группы инженеров потребовалось учитывать тайм-ауты, что, в свою очередь, улучшило производительность всей нашей архитектуры. Таймауты - это совсем другая статья, но терпят неудачу быстро, часто терпят неудачу.

Это подводит нас к сути дела: мы заметили, что таймауты занимают в два раза больше времени, чем должны. Если мы установим We::Call::Connection.new(…, timeout: 5), это займет 10 секунд. Как вы можете себе представить, это имело ужасные последствия для нисходящего потока, и поток, который был заблокирован на такое долгое время, приводил к ужасным резервным копиям в очередях запросов!

Кто-то предположил, что, возможно, We :: Call не смогли правильно передать тайм-аут Фарадею, а 10 секунд были просто тайм-аутом сервера… Это казалось разумным, поэтому я немедленно начал отладку.

После некоторой отладки я заметил, что тайм-аут 1 займет 2 секунды, 2 секунды займет 4 секунды, 4 секунды займет 8 секунд ...

КАКИЕ?!

Раздраженный, я думаю, что бросил в Google какой-то нелепый поисковый запрос вроде:

почему таймауты Фарадея удвоены

Удивительно, но я нашел ответ.

Это вовсе не ошибка We :: Call или Faraday, а адаптер по умолчанию, который он использует как внутренности для своих операций: Net :: HTTP. Это явно используется Фарадеем по умолчанию, потому что оно встроено в Ruby.

Еще в 2012 году была сделана фиксация, которая закончилась в версии 2.0.0, чтобы автоматически повторить любой сделанный запрос, который был идемпотентным:

IDEMPOTENT_METHODS_ = %w/GET HEAD PUT DELETE OPTIONS TRACE/

Теоретически это отличная идея, но в конечном итоге она создает огромную загадку для всех, кто этого не ожидает. Некоторые из самых опытных разработчиков в этой компании понятия не имели, что происходит, так как же мы можем ожидать, что кто-то еще узнает ?!

Это не значит, что мы просто сборище неудачливых глупцов. Этот совет есть повсюду, как и в Общие шаблоны› Повторить попытку при неудаче , суть которого довольно высока в определенных поисковых запросах:

response = Net::HTTP.get_response("google.com", "/") rescue retry

Это работает, но нужно будет сделать две попытки, прежде чем rescue retry попадет. 😅

NewRelic также не сообщает об этом как о 2-кратных запросах, а просто показывает один вызов NetHTTP, который измеряется вдвое дольше. Может быть, отчеты NewRelic можно улучшить, или, может быть, они непрозрачны для их агента Ruby, но то, что это действует как неконтролируемое значение по умолчанию, ужасно.

Запрос на перенос, чтобы сделать его настраиваемым, объединен и появится в Ruby v2.5.0, когда он будет выпущен. Он сохраняет поведение по умолчанию неизменным, что будет продолжать сбивать с толку людей, но, по крайней мере, это позволит Фарадею реализовать контроль над ним в своем адаптере. Вероятно, они по умолчанию будут использовать 0 повторных попыток, а затем, используя их faraday.request :retry, max: 1, вы можете увеличить это количество при желании.

Ожидание, пока все приложения перейдут на Ruby v2.5.0, не казалось решением этой проблемы, поэтому я начал искать альтернативные адаптеры, которые может использовать Фарадей (чтобы мы могли продолжать использовать все наши замечательные промежуточные программы), и всплыл типичный как сильный соперник.

Typhoeus - популярный выбор для тех, кто хочет выполнять асинхронные вызовы, но пока не может обновить свою архитектуру до HTTP / 2 по разным причинам. Я все еще настаиваю на расширении HTTP / 2, чтобы помочь улучшить и упростить наши API-интерфейсы, но в то же время, позволить людям использовать типичные асинхронные функции кажется отличным шагом.

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

Typhoeus является оболочкой ethon, которая представляет собой низкоуровневую оболочку libcurl. Использование libcurl в качестве основы дает нам поддержку HTTP / 1.1 KeepAlive, что значительно сокращает время, необходимое для создания HTTP-соединения.

Тестировать это было довольно просто. Давайте создадим 100 реалистичных запросов POST, чтобы гарантировать отсутствие кеширования, и не использовать какие-либо функции, такие как встроенная сериализация JSON или асинхронная функция.

                       user     system      total        real                       net_http:              0.910000   0.190000   1.100000 (  9.382847)                       net_http_persistent:   0.980000   0.170000   1.150000 (  9.245193)                       patron:                0.150000   0.110000   0.260000 (  2.233322)                       httpclient:            0.150000   0.100000   0.250000 (  2.142556)                       typhoeus:              0.120000   0.070000   0.190000 (  2.138841)

Patron, httpclient и typhoeus оборачивают libcurl, и они несколько раз переключались на .1 быстрее всех. В конце концов, выбор в пользу Typhoeus был очевиден и прост благодаря столь же удивительному увеличению производительности, а также асинхронности.

Мы опробовали Typhoeus на системе, которая получает много обращений и работает как оценщик для удаленных источников данных. Это была идеальная система для тестирования, поскольку выполнение HTTP-запросов - это почти все, что она делает, и все, что можно сделать для ускорения, ускоряет работу всей системы.

Мы развернули We :: Call v0.7.0-pre1 и потеряли инструменты до конца дня, что привело к преувеличенному провалу на графике. Typheous поддерживает NewRelic, поэтому мы развернули исправление, и вы можете легко увидеть улучшения.

Вещи не всегда могут быть на 100% единорогами. Сейчас Typheous, похоже, не различает тайм-аут открытия и тайм-аут чтения, что для наших нужд (и для отладки в целом) очень важно.

Это отстой, но отстой гораздо меньше, чем когда все загадочно складывается вдвое, и никто не знает почему.

Адаптер Typheous в Faraday ≤ v0.13 также ужасно устарел, и теперь Typheous собирает свои собственные. Это было удалено в мастере (и исчезнет в v0.14). Набор тестов Фарадея работает против внешнего адаптера тифозного вируса благодаря некоторой помощи со стороны сопровождающего Фарадея iMacTia.

В общем, сейчас ситуация лучше, чем была, но нужно отправить больше PR и сделать больше открытых источников!

Что бы вы ни делали, убедитесь, что вы понимаете, что Net :: HTTP делает это, и, если это пугает, вы переключаетесь на один из этих адаптеров на основе libcurl!

Также помните об этой разнице в скорости при переходе с «традиционных» HTTP-вызовов REST или RESTish / RPC. Использование HTTP-клиента, который не поддерживает keep-alive, будет намного медленнее, чем переключение на какой-нибудь новый модный инструмент, который это делает, но это ложное улучшение.

Старайтесь не выливать ребенка вместе с водой в ванну и просто используйте HTTP-клиент, который включает для вас функцию keep-alive. Или найдите тот, который позволяет включить его самостоятельно. 👍🏼

Первоначально опубликовано на сайте engineering.wework.com 14 декабря 2017 г.