Мотивация
Микросервисная архитектура позволяет выбирать между технологиями и языками программирования при написании REST api сервисов. Какой язык лучше выбрать, для написании REST api приложения, чтобы обеспечить большее количество одновременных пользователей быстрым и стабильным ответом на одном и том же железе? Чтобы ответить на этот вопрос было бы хорошо увидеть разницу в производительности одного и того же приложения написанного на Java и GO.
Дисклеймер
Все результаты представленные в данном эксперименте являются частным примером использования Java и GO и не должны использоваться для описания производительности этих языков в других условиях.
Условия/Ограничения
- Никаких дополнительных настроек по увеличению производительности. Фреймворки и библиотеки должны использоваться с настройками по-умолчанию.
- Никаких ORM фреймворков. Только драйвера баз данных и одинаковые запросы в обоих приложениях.
План
- Создадим два простых REST api приложения на Java и GO c PostgreSQL базой данных
- Создадим нагрузочные тесты при помощи Jmeter
- Запустим каждое приложение, базу и тесты на отдельных AWS экземплярах
- Прогоним тесты и соберем результаты
Тестируемое приложение
В качестве системы для тестирования я создал два приложения: bank-java и bank-go. Это наверное самое простое банк-приложение в мире. Все что оно умеет делать это создавать новых клиентов с начальным балансом, переводить средства от одного клиента другому и показывать баланс.
API:
- Post /client/new/{balance} — создает клиента с начальным балансом
- Post /transaction — переводит средства от одного клиента другому
- Get /client/{id}/balance — показывает баланс
Фреймворки и библиотеки
При выборе тех стэка я использовал самые новые, популярные и простые фреймворки и библиотеки, чтобы как можно быстрее реализовать нужный функционал.
Bank-java: Java 11, spring boot 2.0.4, spring-web 5.0.8, PostgreSQL JDBC 4.2.4
Bank-go: Go 1.8, gorilla/mux, github.com/lib/pq
Тестовая среда
В качестве тестовой среды был использован AWS.
Следующие EC2 экземпляры были созданы:
- Bank-go t2.micro (Variable ECUs, 1 vCPUs, 2.5 GHz, Intel Xeon Family, 1 GiB memory, EBS only)
- Bank-java t2.micro (Variable ECUs, 1 vCPUs, 2.5 GHz, Intel Xeon Family, 1 GiB memory, EBS only)
- Postgres d2.xlarge (14 ECUs, 4 vCPUs, 2.4 GHz, Intel Xeon E52676v3, 30.5 GiB memory, 3 x 2048 GiB Storage Capacity)
- Bank-test t2.2xlarge (Variable ECUs, 8 vCPUs, 2.3 GHz, Intel Broadwell E5-2686v4, 32 GiB memory, EBS only)
Все экземпляры используют Ubuntu Server 18.04 LTS (HVM), SSD Volume Type
Тестовый проект
Jmeter тест вызывает каждый API из списка сверху, проверяет что статус ответа равен 200 и тело ответа содержит id. Для каждого приложения я запустил его с числом одновременных пользователей 1000, 2000 и т.д. до 10000.
С полным логом результатов можно ознакомиться здесь.
Агрегированные результаты
Описание результатов
Оба приложения работают отлично при числе пользователей 1000. Начиная с 2000 GO приложение начинает значительно терять в производительности и чуть-чуть в стабильности. У Java этот рубеж начинается с 3000.
Заключение
Используя одно и то же оборудование REST api приложение Java может поддерживать вдвое* больше одновременных пользователей, чем приложение GO с базой данных PostgreSQL.
*Данная цифра верна только при использовании тех же условий испытания что и в данном эксперименте
Комментарии (30)
RPG18
27.09.2018 18:46+2Что бросается в глаза на blank-go:
- стандартный http сервер;
- стандартная работа с json.
Поэтому такие низкие результаты.
Laney1
27.09.2018 19:18+8еще спамятся функции fmt.Println, которые мало того что не буферизованы и с оверхедом из-за конвертации аргументов, там еще и там внутри мьютекс.
В общем, этот "бенчмарк" очень похож на знаменитую статью какого-то сотрудника mail.ru пару лет назад, у которого в коде на rust на каждый запрос выполнялась компиляция регекспа
OlegSchwann
27.09.2018 20:29Про json: в одном из учебных примеров easyjson давал 50-и кратный прирост производительности.
JekaMas
28.09.2018 07:46Есть еще такая прелесть без потребности в генераторах кода https://github.com/json-iterator/go
rudenkovk
28.09.2018 15:16подтвержу, на реальном проекте так и було. Не 50 конечно, но 25-35 выдавали.
time2rfc
27.09.2018 18:54Интересно было бы посмотреть на графики потребления cpu\ram во время тестирования, и на результаты с прогреввом java версии. В любом случаи спасибо!
helgihabr
27.09.2018 19:01+61. Как можно тестировать производительность, если у вас на каждый чих стоит println?
2. Зачем каждый раз открывать/закрывать подключение к бд? Еще и формировать строку подключения тоже каждый раз.
И пара вопросов по коду:
1. В джаве return client; вернет json?
2. В чем заклоючалась нестабильность? Кто и какие ошибки выдавал?
helgihabr
27.09.2018 19:12+2И еще момент:
в го у вас:
db.SetMaxOpenConns(20) // Sane default
зачем это?
db.SetMaxIdleConns(0)
db.SetConnMaxLifetime(time.Nanosecond)
И покажите такое в джаве, а то я не нашел.
astec
27.09.2018 20:10+4Не верю. Был бы свободный день и Go приложение заработало бы в 5-10 раз быстрее как миниму.
Должно быть стыдно делать такие тесты и уж тем более выкладывать как статьи.
OlegSchwann
27.09.2018 20:28Грустно на такое смотреть. Подготовка SQL не используется. Это не сложно совсем, один запрос.
unabl4
27.09.2018 20:32А теперь выложите сюда pprof с go и вполне может оказаться, что ботлнек — это работа с базой.
Алсо, для нагрузочного тестирования есть православные ab / siege.
kolkoni
27.09.2018 21:08Elixir вроде как очень хорош в плане нагрузки, его не рассматривали?
eoffsock
28.09.2018 00:35Мне кажется, что не выиграет. Хотя интересно было бы потестировать. По моему опыту, Elixir больше про стабильность, а не про производительность.
Можно конечно узкие места подправить, скажем нативная JSON либа (jiffy) будет раз в пять быстрее стоковой либы. Если в день через приложение проходят миллионы запросов, разница может оказаться ощутимой.
Monnoroch
27.09.2018 21:15Of course, I'd also suggest that whoever was the genius who thought it was a good idea to
read thingscreate ONE F*CKINGBYTECONNECTIONAT A TIMEPER REQUEST withsystem callsTCP handshakes for eachbyterequest should be retroactively aborted. Who the f*ck does idiotic things like that? How did they noty die as babies, considering that they were likely too stupid to find a tit to suck on?
Linus.
Originalunabl4
27.09.2018 22:58Жаль, что, скорее всего, больше такого в LKML (или где-либо ещё) не увидишь. Остаётся только перечитывать старенькое и пускать скупую ностальгическую слезу :) Это же просто бесценно.
bromzh
27.09.2018 22:17Вот вам бенчмарки
Только Java и Go. Разные фреймворки/библиотеки. Там ещё можно попереключать тип приложения и тачки.
kuftachev
28.09.2018 00:30+2Ну там же написано, что это "из песочницы", тут же видно, что это не инженерная работа, а кто-то сидел в песочнице во дворе, какой-то злой дядя дал ноут с доступом в интернет и получились такие тесты.
Если бы это делал хоть немного инженер, его бы хотя бы волновало почему именно такие результаты? Почему его результаты так отличаются от результатов более разумных существ? Ну и много других вопросов.
А по сути, тут даже комментировать нечего.
helgihabr
28.09.2018 09:59волновало почему именно такие результаты?
Совершенно правильный камент.
Что очень удивляет, так это дикая разница, особенно на первых шагах (почти в 6 раз!).
Хотя, по сути, здесь нечем тестировать сами языки, т.к. никакой обработки данных в самих ЯП не происходит. Только простейщий прием параметров и отправка сырого запроса в базу. Разница в конкретном тесте должна быть минимальна.
Автор же нагородил целый огород из горилл и джейсонов, что в данном тесте просто съест львиную долю ресурсов, а все должно просто упереться в систему ввода-вывода и в базу.
Сами ЯП тут толком и не тестируются.
argonavtt
28.09.2018 09:28Как правило такие тесты провести довольно трудно, нужно очень хорошо разбираться в обеих языках. Если же нет то и результат будет плачевным. Да и смысла этой статьи не вижу, подобных пруд пруди, да и с более правильными тестами. А так holy war на ровном месте.
avraam_linkoln
28.09.2018 09:32Очередной пост, в котором чувак, не разобравшийся нормально в технологии, пытается что то сравнивать
claygod
28.09.2018 10:40-1Имея опыт написания роутера для Go могу сказать совершенно точно, что роутер Гориллы по производительности совсем не айс, смысл брать его в бенчмарк не вижу вообще
hummerd
28.09.2018 15:33Вы бы показали код на GO человеку, умеющиму писать на GO.
Зачем на каждый чих вызывать newDb()?
DB.Close
It is rare to Close a DB, as the DB handle is meant to be long-lived and shared between many goroutines.
Вы бы и в джаве тогда BankPostgresRepository сконфигурили бы как per call.
Throwable
29.09.2018 22:33Bank-java: Java 11, spring boot 2.0.4, spring-web 5.0.8, PostgreSQL JDBC 4.2.4
О боже! В этом мире еще остались люди, которые не пишут на Spring Boot и не ассоциируют его со словом Java?
Golem765
Ставь лайк если тоже пустил скупую мужскую слезу гордости за родной язык