Как мы перешли с 30 серверов на 2: Go
Когда мы выпустили первую версию IronWorker около 3 лет назад, она была написана на Ruby, а API было написано на Rails. Через некоторое время нагрузка стала быстро расти и мы быстро достигли предела возможностей наших Ruby приложений. Короче говоря, мы перешли на Go. И если вы хотите узнать подробности — продолжайте читать…
Первая версия
Во-первых, немного истории: мы написали первую версию IronWorker, первоначально названную SimpleWorker (неплохое название, не так ли?) на Ruby. Мы были консалтинговой компанией, создающей приложения для других компаний, и в то время были 2 популярные вещи: Amazon Web Services и Ruby On Rails. И таким образом мы создавали приложения, используя Ruby on Rails и AWS, и привлекали новых клиентов. Причина по которой мы создали IronWorker была в том чтобы «почесать там где чешется». У нас было несколько клиентов, использующих устройства, которые постоянно отправляли данные в режиме 24/7 и нам приходилось эти данные принимать и преобразовывать во что-то полезное. Решалась эта задача путем запуска тяжелых процессов по расписанию, которые каждый час, каждый день и так далее обрабатывали данные. Мы решили создать что-то, что мы сможем использовать для всех наших клиентов, без необходимости постоянно поднимать и поддерживать отдельную инфраструктуру для каждого из них, чтобы обрабатывать его данные. Таким образом мы создали «обработчик как сервис», который мы сначала использовали для своих задач, а затем мы решили, что, возможно, кому то еще понадобится подобный сервис и мы сделали его публичным. Так и родился IronWorker.
Постоянная загрузка процессоров на наших серверах была около 50-60%. Когда нагрузка выросла, мы добавили еще серверов, чтобы сохранить загруженность процессоров примерно на уровне 50%. Это нас устраивало пока нас устраивала цена, которую мы платили за такое количество серверов. Большей проблемой было то, как мы справлялись со скачками нагрузки. Очередной скачок нагрузки (трафика) создавал эффект домино, который мог вывести из строя целый кластер. Во время такого скачка нагрузки, превышающей обычную всего на 50%, наши Rails-сервера начинали использовать процессор на 100% и переставали отвечать на запросы. Это заставляло балансировщик нагрузки думать, что этот сервер упал и перераспределять нагрузку между остальными серверами. И, так как, помимо обработки запросов упавшего сервера, оставшимся серверам приходилось еще обрабатывать пиковую нагрузку, то обычно проходило немного времени до падения следующего сервера, который опять исключался из пула балансировщика, и так далее. Довольно скоро все сервера приходили в нерабочее состояние Это явление также известно как colossal clusterf**k(+Blake Mizerany)
Единственный способ избежать этого с приложениями, которые у нас были на тот момент — использовать огромное количество дополнительных мощностей, чтобы снизить нагрузку на наши сервера и быть готовыми к пиковой нагрузке. Но это означало тратить огромное количество денег. Что-то надо было менять.
Мы переписали его
Мы решили переписать API. Это было простое решение, Честно говоря, наше API, написанное на Ruby on Rails, не было масштабируемым. Основываясь на многолетнем опыте разработки подобных вещей на Java, которые могли обрабатывать большую нагрузку используя куда меньшие ресурсы чем Ruby on Rails, я знал, что мы можем сделать это куда лучше. Таким образом, решение сводилось к тому, какой язык использовать.
Выбор языка
Я был открыт для новых идей, так как последнее, что я хотел сделать — это вернуться к Java. Java есть (был?) прекрасный язык, в котором много преимуществ, таких как производительность, но после написания кода на Ruby в течение нескольких лет, я был восхищен тем, как продуктивно я могу писать код. Ruby это удобный, понятный и простой язык
Мы посмотрели на другие скриптовые языки с лучшей производительностью, чем у Ruby (что было несложно), такие как Python и JavaScript/Node.js. Также мы рассмотрели базирующиеся на JVM — Scala и Clojure, и другие языки, как Erlang (который использует AWS) и Go (golang). Go победил. То, что конкуррентность была фундаментальной частью языка, было отлично; стандартная библиотека ядра содержала почти все что нам нужно было для построения API; он емкий; он быстро компилируется; программировать на Go просто приятно — так же, как и на Ruby, и, в конце концов, числа не лгут. После построения прототипа и тестирования производительности мы поняли, что можем без проблем обрабатывать большие нагрузки при помощи Go. И, после некоторых обсуждений с командой («Все в порядке, его поддерживает Google»), мы решили использовать Golang.
Когда мы впервые решили попробовать Go, это было рискованное решение. Не было большого коммьюнити, не было большого количества open source проектов, не было успешных случаев применения Go в продакшне. Также мы не были уверены, что мы сможем нанять талантливых программистов, если мы выберем Go. Но вскоре мы поняли, что мы сможем нанять лучших программистов именно потому, что мы выбрали Go. Мы были одной из первых компаний, которые публично заявили об использовании Go в продакшне, и первой компанией, которая разместила объявление о вакансии в рассылке golang. После этого лучшие разработчики захотели работать на нас, потому что они смогли бы использовать Go в работе.
После Go
После того как мы запустили версию на Go, мы снизили количество серверов до двух (второй нужен больше для надежности). Нагрузка на сервера стала минимальной, как будто ничего и не использовало ресурсов. Загрузка процессора составляла менее 5%, и приложение использовало несколько сотен КБ памяти (при запуске) в сравнении с Rails приложениями, которые отъедали ~ 50 МБ (при запуске). Сравните это с JVM! Прошло много времени. И с тех пор у нас больше никогда не было colossal clusterf**k.
Мы сильно выросли с тех времен. Сейчас у нас намного больше трафика, мы запустили дополнительные сервисы (IronMQ и IronCache), и сейчас мы используем сотни серверов, чтобы покрыть нужды клиентов. И весь бэкенд реализован на Go. В ретроспективе это было отличное решение — выбрать Go, поскольку это позволило нам построить отличные продукты, чтобы расти и масштабироваться, а также привлечь талантливых людей. И, я думаю, что сделанный выбор будет продолжать помогать нам расти в обозримом будущем.
PS Перевод сделан с разрешения Travis, и если возникнут какие либо вопросы, то он и другие члены команды Iron.io с удовольствием ответят на них.