Мотивация


Микросервисная архитектура позволяет выбирать между технологиями и языками программирования при написании REST api сервисов. Какой язык лучше выбрать, для написании REST api приложения, чтобы обеспечить большее количество одновременных пользователей быстрым и стабильным ответом на одном и том же железе? Чтобы ответить на этот вопрос было бы хорошо увидеть разницу в производительности одного и того же приложения написанного на Java и GO.

Дисклеймер


Все результаты представленные в данном эксперименте являются частным примером использования Java и GO и не должны использоваться для описания производительности этих языков в других условиях.

Условия/Ограничения


  • Никаких дополнительных настроек по увеличению производительности. Фреймворки и библиотеки должны использоваться с настройками по-умолчанию.
  • Никаких ORM фреймворков. Только драйвера баз данных и одинаковые запросы в обоих приложениях.

План


  1. Создадим два простых REST api приложения на Java и GO c PostgreSQL базой данных
  2. Создадим нагрузочные тесты при помощи Jmeter
  3. Запустим каждое приложение, базу и тесты на отдельных AWS экземплярах
  4. Прогоним тесты и соберем результаты

Тестируемое приложение


В качестве системы для тестирования я создал два приложения: 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 экземпляры были созданы:

  1. Bank-go t2.micro (Variable ECUs, 1 vCPUs, 2.5 GHz, Intel Xeon Family, 1 GiB memory, EBS only)
  2. Bank-java t2.micro (Variable ECUs, 1 vCPUs, 2.5 GHz, Intel Xeon Family, 1 GiB memory, EBS only)
  3. Postgres d2.xlarge (14 ECUs, 4 vCPUs, 2.4 GHz, Intel Xeon E52676v3, 30.5 GiB memory, 3 x 2048 GiB Storage Capacity)
  4. 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)


  1. Golem765
    27.09.2018 18:41
    -8

    Ставь лайк если тоже пустил скупую мужскую слезу гордости за родной язык


  1. RPG18
    27.09.2018 18:46
    +2

    Что бросается в глаза на blank-go:


    • стандартный http сервер;
    • стандартная работа с json.

    Поэтому такие низкие результаты.


    1. Laney1
      27.09.2018 19:18
      +8

      еще спамятся функции fmt.Println, которые мало того что не буферизованы и с оверхедом из-за конвертации аргументов, там еще и там внутри мьютекс.


      В общем, этот "бенчмарк" очень похож на знаменитую статью какого-то сотрудника mail.ru пару лет назад, у которого в коде на rust на каждый запрос выполнялась компиляция регекспа


    1. OlegSchwann
      27.09.2018 20:29

      Про json: в одном из учебных примеров easyjson давал 50-и кратный прирост производительности.


      1. JekaMas
        28.09.2018 07:46

        Есть еще такая прелесть без потребности в генераторах кода https://github.com/json-iterator/go


      1. rudenkovk
        28.09.2018 15:16

        подтвержу, на реальном проекте так и було. Не 50 конечно, но 25-35 выдавали.


  1. time2rfc
    27.09.2018 18:54

    Интересно было бы посмотреть на графики потребления cpu\ram во время тестирования, и на результаты с прогреввом java версии. В любом случаи спасибо!


  1. helgihabr
    27.09.2018 19:01
    +6

    1. Как можно тестировать производительность, если у вас на каждый чих стоит println?
    2. Зачем каждый раз открывать/закрывать подключение к бд? Еще и формировать строку подключения тоже каждый раз.
    И пара вопросов по коду:
    1. В джаве return client; вернет json?
    2. В чем заклоючалась нестабильность? Кто и какие ошибки выдавал?


  1. helgihabr
    27.09.2018 19:12
    +2

    И еще момент:
    в го у вас:

    db.SetMaxOpenConns(20) // Sane default
    db.SetMaxIdleConns(0)
    db.SetConnMaxLifetime(time.Nanosecond)
    зачем это?
    И покажите такое в джаве, а то я не нашел.


  1. Linsh
    27.09.2018 19:17
    +1

    Интересно сравнение с ванильным NodeJs. Не планируете добавить?


  1. Gemorroj
    27.09.2018 19:21
    +1

    я использовал самые новые
    Bank-go: Go 1.8


  1. Moxa
    27.09.2018 19:33
    +7

    есть же нормальные бенчмарки, зачем такое странное сравнение?


  1. astec
    27.09.2018 20:10
    +4

    Не верю. Был бы свободный день и Go приложение заработало бы в 5-10 раз быстрее как миниму.


    Должно быть стыдно делать такие тесты и уж тем более выкладывать как статьи.


  1. OlegSchwann
    27.09.2018 20:28

    Грустно на такое смотреть. Подготовка SQL не используется. Это не сложно совсем, один запрос.


  1. unabl4
    27.09.2018 20:32

    А теперь выложите сюда pprof с go и вполне может оказаться, что ботлнек — это работа с базой.
    Алсо, для нагрузочного тестирования есть православные ab / siege.


  1. kolkoni
    27.09.2018 21:08

    Elixir вроде как очень хорош в плане нагрузки, его не рассматривали?


    1. eoffsock
      28.09.2018 00:35

      Мне кажется, что не выиграет. Хотя интересно было бы потестировать. По моему опыту, Elixir больше про стабильность, а не про производительность.

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


  1. Monnoroch
    27.09.2018 21:15

    Of course, I'd also suggest that whoever was the genius who thought it was a good idea to read things create ONE F*CKING BYTE CONNECTION AT A TIME PER REQUEST with system calls TCP handshakes for each byte request 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.

    Original


    1. unabl4
      27.09.2018 22:58

      Жаль, что, скорее всего, больше такого в LKML (или где-либо ещё) не увидишь. Остаётся только перечитывать старенькое и пускать скупую ностальгическую слезу :) Это же просто бесценно.


  1. bromzh
    27.09.2018 22:17

    Вот вам бенчмарки
    Только Java и Go. Разные фреймворки/библиотеки. Там ещё можно попереключать тип приложения и тачки.


  1. kuftachev
    28.09.2018 00:30
    +2

    Ну там же написано, что это "из песочницы", тут же видно, что это не инженерная работа, а кто-то сидел в песочнице во дворе, какой-то злой дядя дал ноут с доступом в интернет и получились такие тесты.


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


    А по сути, тут даже комментировать нечего.


    1. helgihabr
      28.09.2018 09:59

      волновало почему именно такие результаты?
      Совершенно правильный камент.
      Что очень удивляет, так это дикая разница, особенно на первых шагах (почти в 6 раз!).
      Хотя, по сути, здесь нечем тестировать сами языки, т.к. никакой обработки данных в самих ЯП не происходит. Только простейщий прием параметров и отправка сырого запроса в базу. Разница в конкретном тесте должна быть минимальна.
      Автор же нагородил целый огород из горилл и джейсонов, что в данном тесте просто съест львиную долю ресурсов, а все должно просто упереться в систему ввода-вывода и в базу.
      Сами ЯП тут толком и не тестируются.


  1. argonavtt
    28.09.2018 09:28

    Как правило такие тесты провести довольно трудно, нужно очень хорошо разбираться в обеих языках. Если же нет то и результат будет плачевным. Да и смысла этой статьи не вижу, подобных пруд пруди, да и с более правильными тестами. А так holy war на ровном месте.


  1. avraam_linkoln
    28.09.2018 09:32

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


  1. claygod
    28.09.2018 10:40
    -1

    Имея опыт написания роутера для Go могу сказать совершенно точно, что роутер Гориллы по производительности совсем не айс, смысл брать его в бенчмарк не вижу вообще
    image


  1. 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.


    1. zelenin
      28.09.2018 20:43
      +1

      Вы бы и в джаве тогда BankPostgresRepository сконфигурили бы как per call.

      так и сконфигурен)


      1. hummerd
        29.09.2018 00:49

        А разве спринг по дефолту не синглтоны делает?


        1. zelenin
          29.09.2018 06:12

          Я вижу сервис и репозиторий — в них происходит инициализация на реквест.


  1. Throwable
    29.09.2018 22:33

    Bank-java: Java 11, spring boot 2.0.4, spring-web 5.0.8, PostgreSQL JDBC 4.2.4

    О боже! В этом мире еще остались люди, которые не пишут на Spring Boot и не ассоциируют его со словом Java?