Pull to refresh

Comments 30

Отличная статья!
Продолжение приветствуется категорически.
Как я понимаю, главная фишка jmeter не в мощности создаваемой нагрузки а в том, что потом можно посмотретьна различные графики по отзывчивости сервиса на разных уровнях нагрузки.
А если вам именно тупая долбилка нужна, возьмите wrk github.com/wg/wrk (мой форк умеет брать список урлов из файла github.com/seriyps/wrk) или httpperf.

А то, что у вас замер времени останавливается до того, как считается BODY ответа это намеренно сделано?
танк он другого уровня инструмент. Он, как и tsung предназначен для запуска с нескольких машин. На одном сервере он себя не проявит.
Почему не проявит? Наоборот, в большинстве случаев одной машинки более, чем достаточно.
Как минимум упретесь в сеть или количество одновременно открытых сокетов (65535 максимально, из них 65000 может и получится взять). В конце концов нагрузочное тестирование больших обьемов начнет показывать упирание в ресурсы машины, на которой ведется тестирование, а не ресурс, который Вы тестируете. А это не, что нам нужно измерять.

При разработки сервиса по тестированию именно разбиение теста на мелкие фрагменты (100 клиентов на 1000 машин) давало правильные цифры нагрузки на ресурс (только сбор и агрегация данной информации — это отдельная проблема). Но это я по своему опыту сужу.
Ну так это особенности ОС, а не инструмента. В доках есть настройки, которые позволят свести к минимуму влияние этих особенностей. Кроме того, танк может стрелять с одной машинки с нескольких айпишников. Понятное дело, что если вы уперлись в сеть, то ничего из этого не поможет, но и никакой другой инструмент не сможет прокачать через сеть больше, чем через нее может пролезть.

На практике с одного танка получалось более 100 000 запросов в секунду. Это почти 9 миллиардов в сутки — немногие могут похвастаться такими рейтами. Опять же, на практике — очень редко, когда бывает нужно два и более танка. Иначе давно бы уже отладили распределенные тесты. А так в этом просто нет необходимости.
> 100 000 запросов в секунду

Это просто запросы? А параллельных клиентов сколько, которые посылают запросы? Ну и 100000 * 10 КБ POST body ~ 1 Гигабайт в секунду. Утилита могла просто тестировать свою нагрузку.

Я не спорю, может Танк и крут, но пока я не видел утилиты, что может выжать более 50К параллельных клиентов (которые посылают запросы независимо) на одной машине. Да еще и условия самой машины должны быть оптимальны (50К ядерной машины я не видел, а значит и параллельность этих клиентов минимальна — ограничена в CPU + Network)

ЗЫ по докам 30000 эго предел в параллельных юзерах на тест
10 Гбитный интерфейс между машинками, машинки в одном свитче. Не знаю, сколько в среднем у нас было на запрос при таких тестах, но вы учтите, что серваку еще распарсить полученный запрос надо. Это уж точно более затратно, чем просто отправить его, т.е. сервак ляжет раньше танка. А что, ваш сервис держит 100К запросов в секунду при 10-килобайтных запросах?

В процессе тестирования мы наблюдаем за нагрузкой на танк и следим за тем, чтобы проц, диск и сеть не кончились. Если есть сомнения, добавляем второй танк.

Число параллельных запросов и рейт связаны через среднее время ответа вот так:
L = λW (закон Литтла), где L — уровень параллелизма, λ — входящий рейт, W — среднее время ответа.
Т.е. при 100 000 запросов в сек и временах ответа 15 мс, будет 15 * 0.001 * 100 000 = 1500 параллельных запросов. Это меньше, чем 30 000.

Еще, паралельные юзеры != параллельные запросы. У юзеров есть еще think time между запросами.

50 К ядер на 50 К клиентов тоже не нужно, потому что клиент не требует постоянной работы проца. Ну и не надо забывать, что с противоположной стороны провода тоже не магический кристалл, а земная машинка с процессором, памятью и дисками. И ваши клиенты, сколько бы их ни было, встанут в очередь для того, чтобы пролезть через провод (один).
> 10 Гбитный интерфейс между машинками, машинки в одном свитче

Тестирование в локальной сети с реальным миром немного отличается.

> А что, ваш сервис держит 100К запросов в секунду при 10-килобайтных запросах?

Нет, не держит, а создает (все эти параметры меняются). Один из наших продуктов — сервис для нагрузочного тестирования.

> Еще, паралельные юзеры != параллельные запросы. У юзеров есть еще think time между запросами.

Не знаю как у Танка, у нас это настраивается. По умолчанию = 0 (тоесть не «засыпать»). Я говорил про такой виде теста, когда нет think time.

> 50 К ядер на 50 К клиентов тоже не нужно, потому что клиент не требует постоянной работы проца

Если он не засыпает — то требует.

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

Многие наши клиенты тестируют и свои системы, которые держат и 1 000 000 клиентов. Многие достигают этого через DNS записи (DNS Round Robin, Geolocation Routing Policy). Мы позволяет им тестировать с разных регионов — так тест похож более на реальный.

Не буду спорить по поводу Танка, но на одной машине я пока не верю, что можно сделать высоконагруженное тестирование (хотя понятие «высоконагруженное» у каждого инженера разное).
Понял, в вашем случае распределенные тесты — действительно необходимость.
Мы в наших тестах стараемся исключить внешние факторы, поэтому чаще всего это машинки в одном ДЦ, а при больших нагрузках — в одном свитче. Даже есть кейсы, когда все тестируется в рамках одной машинки, а генератор и сервис раскидываются по ядрам.
В нашем случае очень важно время отклика сервера (время получения BODY можно опустить), есть определенные временные границы, в которые должен вписаться ответ(120мс, после этого соединение просто закрывается с другой стороны). И в нашем случае важно считать не просто время, но и какого типа ответ пришел (200, 204 или ...) и данный тест писался из реальных потребностей и должен был обхватывать конкретную узкую задачу. Я очень слаб в C/C++ по этому дорабатывать под свою задачу имеющийся проект было бы более затратно по времени, по этому сделал именно так. И было очень интересно попробовать Go
Он параллелится запуская потоки, что не так эффективно, как epool например. В какой то момент вы начинаете мерить не производительность сервера а производительность siege.
В wrk используется http парсер от nginx. Потом он запускает потоков по числу процессоров и в каждом из них стартует event loop с epool / kqueue / select — в зависимости от того, что доступно. Очень эффективная утилита.
Умеет ли он ходить на несколько урлов по порядку, сохраняя куки?
Тогда его область применения крайне мала. В нагрузочном тестировании крайне желательно повторить действия типичного пользователя.
Про сервис очень интересно было бы почитать.
Меня смутило эта ваша любовь к начальному counter`у в значении -1 :)
А так — здорово, рад что сообщество начинает что-то выкладывать на golang, сам уже имею два успешных внедрения на продакшне.
Если я Вас правильно понял, то поясню :) изначально в случае ошибки поток продолжал работу и использовалось continue, и что бы счетчики все считались делал это в начале цикла, потом сделал прекращение цикла, а данный блок не переписывал, так и остался начальный индекс -1
А как организовывали красивый вывод в консоль?
Загляните в исходники, там один файл за это отвечает, и просто выводит агрегиолванные данные в виде таблички
Почему бы не использовать агентов jmeter, если вы проседаете по CPU на одном инстансе jmeter.
Они вполне хорошо справляются с задачей создания распределенной нагрузки с разных узлов.
JMeter падал с OOM на 10 параллельных загрузках 100МБ файла при 3ГБ выделенных для Java и, съедая при этом 100% от обоих ядер.
Написал скрипт на Ruby+EM, который делал 100 таких загрузок параллельно, потреблял 20МБ памяти и 5% процессора. Там, правда всего один файл был строк на 15.
Зафолловил ГитХаб и добавил звездочку проекту:) Отдельное спасибо за пост, на хабре golangовые посты — редкость.
Кстати, как полезная мелочь, скомпилируйте и добавьте релиз для go-meter. Это удобно для тех, у кого Go не установлен и нечем скрмпилировать.
Весьма радует, что вы выбрали и осилили Go для решения данной задачи: код достаточно хорошо работает и решает свои задачи, несмотря на небольшое знание языка. Однако, при этом код изобилует потенциальными проблемами, которые неизбежно будут копировать ваши читатели.
Например, вместо:
func (this *Settings) Load(fileName string) error {
file, e := ioutil.ReadFile(fileName); if e != nil {
return e
}
e = json.Unmarshal(file, this); if e != nil {
return e
}
return nil
}

Лучше пользоваться ридерами — как в случае больших конфигов, так и просто идеологически правильнее:

func (this *Settings) Load(fileName string) (e error) {
var file os.File
if file, e = os.Open(fileName); e != nil {
return
}
defer file.Close()
decoder := json.NewDecoder(file)
e = decoder.Decode(this)
return
}

Плюс много мелких деталей, делающий код лаконичнее: именованный возврат, defer'ы, функции внутри условия.

По возможности, лучше не читать из канала по счётчику: в случае проблем программа может банально зависнуть в ожидании канала. Лучше закрывать каналы в defer'е и принимать содержимое канала через range.

Вы передаёте Status по ссылке и при этом меняете — у вас нет никаких гарантий, что counter на момент прочтения будет читать именно тот Status, который был отправлен.

Вы создаёте очень большой канал: это совершенно лишний расход памяти.

Полностью инициализировать все переменные в структуре не обязательно: по умолчанию они будут созданы нулевыми, в зависимости от типа, т.е. вместо status := &Status{false, false, false, nil, 0, true, nil, nil, nil} можно делать status := &Status{isFinished: true}

Вообще говоря, есть куча мелочей, которые могут значительно ускорить код: один раз создавать http.Request'ы, вместо diff := time.Now().Sub(lastPrint); if diff.Seconds() > 1 { PrintStatus(); } использовать time.Tick в отдельной goroutine'е, использовать ссылки только там, где нужно, оптимизировать выделение памяти внутри циклов, итд…

PS Возможно, под ваши нужды хватило бы функционала Benchmark'ов в testing'е.
Спасибо за замечания, есть куда развивать и учиться само собой. Постараюсь поправить все моменты и доконца проникнуться новым подходом. В процессе написания возникало много вопросов, с поиском ответов было сложновато. Самое сложное было найти ответ на: лучшая практика решения… задачи. К примеру как организовать проект, как организовать ведение логов и т.д. Ответов не много приходится учиться на своих ошибках
На самом деле, большинство таких вопросов отпадает после прочтения «Effective Go» и изучения Codewalk'ов. На практике тоже можно, просто дольше и мучительнее.

Кстати, попробуйте тесты в Go: они здесь вполне простые и удобные (включая нагрузочные тесты), и пригодны для пресловутой «разработки через тестирование» (включая оптимизацию производительности).

А так, не стесняйтесь, спрашивайте: rocket science здесь нет.
Sign up to leave a comment.

Articles