Статьи

Микросервисы Java и Go — нагрузочное тестирование (реванш)

Когда Go впервые появился в ноябре 2009 года, мы мало что слышали об этом, и наше первое взаимодействие произошло в 2012 году, когда Google официально выпустила версию 1 Go. Наша команда решила убедить нашего клиента использовать его для своего проекта, но это было трудно продать, и клиент отклонил нашу рекомендацию (в основном из-за недостатка знаний в своей команде поддержки). 

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

В одной из статей, с которыми мы сталкивались, сравнивались Java и Go для реализации микросервисов, и которые могли бы обслуживать больше пользователей, использующих подобное оборудование. Имея многолетний опыт работы с Java и зная его сильные и слабые стороны, а также имея пару месяцев опыта в Go, но уже зная его достоинства (скомпилированный язык, goroutines! = Threads), мы ожидали увидеть очень близкие результаты с меньшей площадью (CPU) / использование памяти) для Go. 

Итак, мы были удивлены, увидев, что Go начал отставать от 2k одновременных пользователей. Поскольку мы решили потратить свое время на изучение Go, для нас было очень важно выяснить, что привело к таким результатам. В этой статье мы объясним, как мы исследовали проблему и как мы проверяли наши изменения.


Вам также может понравиться:
Учебное пособие по Голангу: Изучите Голанг на примерах .

Краткое описание оригинального эксперимента

Автор создал банковский сервис с 3 API:

  • POST / client / new / {balance} — создать нового клиента с начальным балансом.

  • POST / транзакция — переводит деньги с одного счета на другой.

  • GET / client / {id} / balance — возвращает текущий баланс для клиента. 

и реализовал это с помощью Java и Go . Для постоянного хранения автор использует PostgreSQL. Чтобы протестировать сервисы, автор создал сценарий jmeter и запустил его с различными наборами одновременных пользователей от 1k до 10k. 

Все это было запущено на AWS.

Джава

Идти

Количество     

пользователи

Время отклика     

(Сек)

ошибки    

(%)

Время отклика     

(Сек)

ошибки    

(%)

4k

5,96

2,63%

14,20

6,62%

10k

42,59

16,03%

46,50

39,30%

TLDR;

Основной причиной проблемы было ограниченное количество доступных соединений в Postgres (100 подключений по умолчанию) и неправильное использование объектов БД SQL. После исправления этих проблем обе службы показали схожие результаты, и единственное отличие заключалось в увеличении нагрузки на процессор и память в Java (в Java все больше). 

Давайте погрузимся в …

Мы решили начать анализировать, почему частота ошибок была такой высокой в ​​версии Go. Для этого мы добавили логирование в исходный код и сами запустили нагрузочные тесты. После анализа журнала мы заметили, что все ошибки были связаны с открытием соединений с базой данных.

Изучив код, первое, что привлекло наше внимание, было то, что для каждого вызова API создавался новый sql.DB ( https://github.com/nikitsenka/bank-go/blob/2ab1ef2ce8959dd1bc5eb5d324e39ab296efbbe5/bank/postgres. перейти # L57 ).


Идти