Добро пожаловать в форум, Guest  >>   Войти | Регистрация | Поиск | Правила | В избранное | Подписаться
Все форумы / Java Новый топик    Ответить
 Программа для нагрузочного тестирования htttp сервисов  [new]
Valentin Kolesnikov
Member

Откуда:
Сообщений: 3186
Недавно делал такую программу.

Может будет кому интересно.

+
package com.github.http;

import com.github.underscore.lodash.U;
import java.io.File;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import org.HdrHistogram.AtomicHistogram;
import org.HdrHistogram.Histogram;
import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.toolchain.perf.HistogramSnapshot;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
import org.eclipse.jetty.util.thread.Scheduler;
import org.mortbay.jetty.load.generator.HTTP1ClientTransportBuilder;
import org.mortbay.jetty.load.generator.HTTPClientTransportBuilder;
import org.mortbay.jetty.load.generator.LoadGenerator;
import org.mortbay.jetty.load.generator.Resource;
import org.mortbay.jetty.load.generator.util.MonitoringThreadPoolExecutor;

public class HttpLoadGenerator {

    private final Scheduler scheduler = new ScheduledExecutorScheduler();

    private LoadGenerator.Builder prepareLoadGenerator(HTTPClientTransportBuilder clientTransportBuilder, String path) {
        HttpFields headers = new HttpFields();
        headers.put("User-Agent", "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0");
        headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8");
        headers.put("Accept-Language", "en-US,en;q=0.5");
        headers.put("Cookie", "__utma=124097164.2025215041.1465995519.1483973120.1485461487.58; __utmz=124097164.1480932641.29.9.utmcsr=localhost:8080|utmccn=(referral)|utmcmd=referral|utmcct=/; wp-settings-3=editor%3Dhtml%26wplink%3D1%26post_dfw%3Doff%26posts_list_mode%3Dlist; wp-settings-time-3=1483536385; wp-settings-time-4=1485794804; wp-settings-4=editor%3Dhtml; _ga=GA1.2.2025215041.1465995519; wordpress_google_apps_login=30a7b62f9ae5db1653367cafa3accacd; PHPSESSID=r8rr7hnl7kttpq40q7bkbcn5c2; ckon1703=sject1703_bfc34a0618c85; JCS_INENREF=; JCS_INENTIM=1489507850637; _gat=1");
        return new LoadGenerator.Builder()
                .threads(1)
                .port(80)
                .httpClientTransportBuilder(clientTransportBuilder)
                .resource(new Resource(path).requestHeaders(headers))
                .scheduler(scheduler);
    }

    public void startLoadTest(URL url, int resourceRate) throws Exception {
        MonitoringThreadPoolExecutor executor = new MonitoringThreadPoolExecutor(1024, 60, TimeUnit.SECONDS);

        Histogram treeHistogram = new AtomicHistogram(TimeUnit.MICROSECONDS.toNanos(1),
                TimeUnit.SECONDS.toNanos(10), 3);
        Histogram rootHistogram = new AtomicHistogram(TimeUnit.MICROSECONDS.toNanos(1),
                TimeUnit.SECONDS.toNanos(10), 3);

        LoadGenerator loadGenerator = prepareLoadGenerator(new HTTP1ClientTransportBuilder(), url.getPath())
                .warmupIterationsPerThread(0)
                .iterationsPerThread(10)
                .runFor(30, TimeUnit.SECONDS)
                .usersPerThread(100)
                .channelsPerUser(1)
                .resourceRate(resourceRate)
                .sslContextFactory(new SslContextFactory(true))
                .host(url.getHost())
                .port(url.getPort() == -1 ? 80 : url.getPort())
                .executor(executor)
                .resourceListener((Resource.TreeListener) info -> {
                    rootHistogram.recordValue(info.getResponseTime() - info.getRequestTime());
                    treeHistogram.recordValue(info.getTreeTime() - info.getRequestTime());
                })
                .build();
        AtomicLong responses = new AtomicLong();
        List<Long> requestsStatistic = new ArrayList<>();
        Runnable helloRunnable = () -> {
            long responsesForLastSecond = rootHistogram.getTotalCount() - responses.get();
            requestsStatistic.add(responsesForLastSecond);
            responses.set(rootHistogram.getTotalCount());
            double averageResponses = requestsStatistic.stream().mapToDouble(Double::valueOf)
                    .average().orElse(Double.NaN);
            System.err.printf("%s amount of responses for the last second: %d, avarege request rate: %.3f%n",
                    LocalDateTime.now().toString(),
                    responsesForLastSecond,
                    averageResponses);
        };

        ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(1);
        scheduledExecutor.scheduleAtFixedRate(helloRunnable, 0, 1, TimeUnit.SECONDS);

        loadGenerator.begin().join();
        scheduledExecutor.shutdown();

        HistogramSnapshot rootSnapshot = new HistogramSnapshot(rootHistogram, 20, "root response time", "us", TimeUnit.NANOSECONDS::toMicros);
        System.err.println(rootSnapshot);
        System.err.printf("client thread pool - max_threads: %d, max_queue_size: %d, max_queue_latency: %dms%n%n",
                executor.getMaxActiveThreads(),
                executor.getMaxQueueSize(),
                TimeUnit.NANOSECONDS.toMillis(executor.getMaxQueueLatency())
        );

        executor.shutdown();
    }

    public static void main(String[] args) throws Exception {
        Options options = new Options();
        options.addOption("c", "config", true, "The config file.");
        options.addOption("?", "help", false, "This help text.");
        
        if (args.length > 0) {
            CommandLineParser parser = new BasicParser();
            CommandLine cmd;
            try {
                cmd = parser.parse(options, args);
            } catch (ParseException e) {
                help(options);
                return;
            }
            if (cmd.hasOption("?") || !cmd.hasOption("c")) {
                help(options);
                return;
            }
            String config = cmd.getOptionValue("c");
            if (new File(config).isFile()) {
                try {
                    final byte[] bytes = Files.readAllBytes(Paths.get(config, new String[0]));
                    String text = new String(bytes, StandardCharsets.UTF_8);
                    Map<String, Object> result = (Map<String, Object>) U.fromJson(text);
                    String url = (String) result.get("url");
                    Long requestRate = (Long) result.get("requestRate");
                    HttpLoadGenerator httpLoadGenerator = new HttpLoadGenerator();
                    httpLoadGenerator.startLoadTest(new URL(url), requestRate.intValue());
                } catch (Exception ex) {
                    System.out.println(config + " - " + ex.getMessage());
                }
            }
        } else {
            help(options);
        }
    }
        
    private static void help(Options options) {
        HelpFormatter formater = new HelpFormatter();
        formater.printHelp(100, "java -jar http-load-generator.jar", "", options, "");
    }
}


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.github.javadev</groupId>
    <artifactId>http-load-generator</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>Http load generator</name>
    <description>The http load generator</description>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <build>
        <finalName>${project.artifactId}</finalName>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.0.2</version>
                <configuration>
                    <archive>
                        <manifest>
                            <mainClass>com.github.http.HttpLoadGenerator</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>2.4.3</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <filters>
                                <filter>
                                    <artifact>*:*</artifact>
                                    <excludes>
                                        <exclude>META-INF/maven/**</exclude>
                                        <exclude>META-INF/COPYRIGHT.html</exclude>
                                        <exclude>META-INF/LICENSE*</exclude>
                                        <exclude>META-INF/NOTICE*</exclude>
                                        <exclude>META-INF/README.txt</exclude>
                                        <exclude>META-INF/DEPENDENCIES*</exclude>
                                        <exclude>LICENSE.txt</exclude>
                                        <exclude>rhinoDiff.txt</exclude>
                                        <exclude>license/**</exclude>
                                    </excludes>
                                </filter>
                            </filters>
                            <dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <dependencies>
        <dependency>
            <groupId>org.mortbay.jetty.loadgenerator</groupId>
            <artifactId>jetty-load-generator-client</artifactId>
            <version>1.0.0-BETA0</version>
        </dependency>
        <dependency>
            <groupId>commons-cli</groupId>
            <artifactId>commons-cli</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>com.github.javadev</groupId>
            <artifactId>underscore</artifactId>
            <version>1.42</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>




С уважением, Валентин
7 май 19, 09:13    [21879605]     Ответить | Цитировать Сообщить модератору
 Re: Программа для нагрузочного тестирования htttp сервисов  [new]
mayton
Member

Откуда: loopback
Сообщений: 41377
Имеет смысл нарисовать сравненение к примеру с JMeter и показать чем эта утилита лучше.
Проще там в настройке e.t.c. Кроме того в коде видно около десятка настроечных параметров
которые не вынесены в CLI. Почему?
7 май 19, 11:17    [21879709]     Ответить | Цитировать Сообщить модератору
 Re: Программа для нагрузочного тестирования htttp сервисов  [new]
dimonz80
Member

Откуда:
Сообщений: 198
Да кому нужен очередной клон ab/curl/wget/JMeter?

ИМХО что-то типа GPSS с возможностью фладить по попсовым сетевым протоколам был бы в сто раз интереснее
7 май 19, 14:19    [21879935]     Ответить | Цитировать Сообщить модератору
 Re: Программа для нагрузочного тестирования htttp сервисов  [new]
Valentin Kolesnikov
Member

Откуда:
Сообщений: 3186
mayton
Имеет смысл нарисовать сравненение к примеру с JMeter и показать чем эта утилита лучше.
Проще там в настройке e.t.c. Кроме того в коде видно около десятка настроечных параметров
которые не вынесены в CLI. Почему?


Есть ещё конфигурационный файл с 2-мя параметрами:

url - url для GET запросов
requestRate - число запросов в секунду.

С уважением, Валентин
7 май 19, 15:37    [21880083]     Ответить | Цитировать Сообщить модератору
 Re: Программа для нагрузочного тестирования htttp сервисов  [new]
Valentin Kolesnikov
Member

Откуда:
Сообщений: 3186
dimonz80
Да кому нужен очередной клон ab/curl/wget/JMeter?

ИМХО что-то типа GPSS с возможностью фладить по попсовым сетевым протоколам был бы в сто раз интереснее


Это ещё одна утилита для нагрузочного тестирования.

С уважением, Валентин
7 май 19, 15:41    [21880089]     Ответить | Цитировать Сообщить модератору
 Re: Программа для нагрузочного тестирования htttp сервисов  [new]
mayton
Member

Откуда: loopback
Сообщений: 41377
Valentin Kolesnikov
mayton
Имеет смысл нарисовать сравненение к примеру с JMeter и показать чем эта утилита лучше.
Проще там в настройке e.t.c. Кроме того в коде видно около десятка настроечных параметров
которые не вынесены в CLI. Почему?


Есть ещё конфигурационный файл с 2-мя параметрами:

url - url для GET запросов
requestRate - число запросов в секунду.

С уважением, Валентин

Ну... я до конца не понимаю суть этих параметров.
LoadGenerator loadGenerator = prepareLoadGenerator(new HTTP1ClientTransportBuilder(), url.getPath())
                .warmupIterationsPerThread(0)
                .iterationsPerThread(10)
                .runFor(30, TimeUnit.SECONDS)
                .usersPerThread(100)

однако хотелось-бы иметь возможноть ими управлять. Даже 30 секунд.... Это такая спорная цифра.
У кого-то база еще за 30 секунд не прогреется. Хорошую базу вообще можно 30 минут греть.
Ну вобщем... больше контроля.
7 май 19, 15:46    [21880096]     Ответить | Цитировать Сообщить модератору
 Re: Программа для нагрузочного тестирования htttp сервисов  [new]
вадя
Member

Откуда: Екатеринбург
Сообщений: 16055
Valentin Kolesnikov
url - url для GET запросов
requestRate - число запросов в секунду.
а как быть с websocket?
7 май 19, 16:19    [21880131]     Ответить | Цитировать Сообщить модератору
 Re: Программа для нагрузочного тестирования htttp сервисов  [new]
Sergunka
Member

Откуда:
Сообщений: 1877
Я помнится сделал тул для моделирования работы сенсоров на разных устройствах. В качестве конфигурации взял простой json через рест.

{
  "name": "SIM0_",
  "schedule":40,
  "frequency":1000,
  "start": false,
  "sensors":[{"sensor" : "Vibration",
               "method" : "spline",
               "x":[0.00, 10.0, 20.0, 30.0, 40.0],
               "y":[1.00, 2.0, 3.0, 2.0, 1.0]
              },
              {"sensor" : "Rotor_Speed",
               "method" : "spline",
               "x":[0.00, 10.0, 20.0, 30.0, 40.0],
               "y":[100.00, 200.0, 300.0, 200.0, 100.0]
              },
              {"sensor" : "Temperature",
               "method" : "line",
               "x":[0.00, 10.0, 20.0, 30.0, 40.0],
               "y":[10.00, 20.0, 30.0, 20.0, 10.0]
              }
    ]
}


https://vyatkins.wordpress.com/2016/07/30/velociraptor/

Все это крутится на томкете или Jetty довольно легко маштабируется по порту так как данные гонит через вебсокет. Расписание связи указано в секундах, а частота замеров в миллисекундах.
7 май 19, 18:16    [21880268]     Ответить | Цитировать Сообщить модератору
 Re: Программа для нагрузочного тестирования htttp сервисов  [new]
Локшин Марк
Member

Откуда: Воронеж
Сообщений: 3144
Valentin Kolesnikov
dimonz80
Да кому нужен очередной клон ab/curl/wget/JMeter?

ИМХО что-то типа GPSS с возможностью фладить по попсовым сетевым протоколам был бы в сто раз интереснее


Это ещё одна утилита для нагрузочного тестирования.

С уважением, Валентин


А Вы прежде чем свой вело утилиту писать, смотрели тот же JMeter например?
7 май 19, 18:39    [21880294]     Ответить | Цитировать Сообщить модератору
 Re: Программа для нагрузочного тестирования htttp сервисов  [new]
dimonz80
Member

Откуда:
Сообщений: 198
Valentin Kolesnikov
dimonz80
Да кому нужен очередной клон ab/curl/wget/JMeter?

ИМХО что-то типа GPSS с возможностью фладить по попсовым сетевым протоколам был бы в сто раз интереснее


Это ещё одна утилита для нагрузочного тестирования.

С уважением, Валентин



Бомбить запросами с максимально скоростью (или ограниченным rpm) - не нагрузочное тестирование. Было бы интереснее создать хотя-бы пуассоновский поток запросов. А совсем хорошо - возможность задавать различные характеристики потока запросов. Это ИМХО самое главное, чего не хватает упомянутому JMeter для того, чтобы сделать его серьезной тулзой для именно нагрузочного тестирования. Короче - нагрузочное тестирование без теории массового обслуживания - не нагрузочное, вот!
8 май 19, 01:22    [21880495]     Ответить | Цитировать Сообщить модератору
 Re: Программа для нагрузочного тестирования htttp сервисов  [new]
dimonz80
Member

Откуда:
Сообщений: 198
dimonz80
Valentin Kolesnikov
пропущено...


Это ещё одна утилита для нагрузочного тестирования.

С уважением, Валентин



Бомбить запросами с максимально скоростью (или ограниченным rpm) - не нагрузочное тестирование. Было бы интереснее создать хотя-бы пуассоновский поток запросов. А совсем хорошо - возможность задавать различные характеристики потока запросов. Это ИМХО самое главное, чего не хватает упомянутому JMeter для того, чтобы сделать его серьезной тулзой для именно нагрузочного тестирования. Короче - нагрузочное тестирование без теории массового обслуживания - не нагрузочное, вот!


Я идиот! В JMeter уже все есть
8 май 19, 01:57    [21880520]     Ответить | Цитировать Сообщить модератору
 Re: Программа для нагрузочного тестирования htttp сервисов  [new]
mayton
Member

Откуда: loopback
Сообщений: 41377
В любом случае приложение достойно внимания. Есть класс утилит которые работают из консоли
и ставят своей задачей быстро что-то протестировать. Или как можно скорее подтвердить негативный сценарий.
И будет ли распределение Пуассоном или не-пуассоном - не суть важно. Просто проверить что микросервис
падает уже на 10-й транзакции, запущеной в параллель.

Например безопасник может подёргать nmap для того чтобы проверить открытые порты на вашей рабочей станции.
Здесь утилита колесникова (будь она реализована как shell-скрипт и запускаема из /usr/bin) станет просто
инструментом быстрой проверки. Она не будет претендовать на лавры JMeter но время готовности к работе
- будет ее преимуществом. Кому это надо - решать пользователю.
8 май 19, 10:38    [21880734]     Ответить | Цитировать Сообщить модератору
 Re: Программа для нагрузочного тестирования htttp сервисов  [new]
qasta
Member

Откуда:
Сообщений: 71
mayton
В любом случае приложение достойно внимания. Есть класс утилит которые работают из консоли
и ставят своей задачей быстро что-то протестировать.


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

Протестировать обычный GET - в JMeter собирается за минут 5-10 (параллельно попивая кофе и читая новости).
8 май 19, 11:57    [21880817]     Ответить | Цитировать Сообщить модератору
 Re: Программа для нагрузочного тестирования htttp сервисов  [new]
mayton
Member

Откуда: loopback
Сообщений: 41377
5-10 минут - это невыносимо долго. Согласись.
8 май 19, 12:06    [21880824]     Ответить | Цитировать Сообщить модератору
 Re: Программа для нагрузочного тестирования htttp сервисов  [new]
chpasha
Member

Откуда:
Сообщений: 8046
mayton
5-10 минут - это невыносимо долго. Согласись.

да это ваще пипец. быстрее 10 строчек кода написать с Executors.newFixedThreadPool и http-клиентом
8 май 19, 13:13    [21880942]     Ответить | Цитировать Сообщить модератору
 Re: Программа для нагрузочного тестирования htttp сервисов  [new]
qasta
Member

Откуда:
Сообщений: 71
mayton
5-10 минут - это невыносимо долго. Согласись.


Согласен :)
8 май 19, 14:10    [21881055]     Ответить | Цитировать Сообщить модератору
 Re: Программа для нагрузочного тестирования htttp сервисов  [new]
Sergunka
Member

Откуда:
Сообщений: 1877
dimonz80
Было бы интереснее создать хотя-бы пуассоновский поток запросов.


К слову сказать как Вы его создаете когда надо?

P.S. К слову сказать на хабре обнаружил статью где боле менее объясняется история вопроса
https://habr.com/ru/company/yandex/blog/431650/
8 май 19, 20:44    [21881479]     Ответить | Цитировать Сообщить модератору
 Re: Программа для нагрузочного тестирования htttp сервисов  [new]
dimonz80
Member

Откуда:
Сообщений: 198
Sergunka
dimonz80
Было бы интереснее создать хотя-бы пуассоновский поток запросов.


К слову сказать как Вы его создаете когда надо?



Задать время между запросами как случайную величину с экспоненциальной плотность распределения вероятностей
9 май 19, 01:46    [21881595]     Ответить | Цитировать Сообщить модератору
 Re: Программа для нагрузочного тестирования htttp сервисов  [new]
Sergunka
Member

Откуда:
Сообщений: 1877
dimonz80
Sergunka
пропущено...


К слову сказать как Вы его создаете когда надо?



Задать время между запросами как случайную величину с экспоненциальной плотность распределения вероятностей


Ну, это понятно из учебника, код когда нибудь писали? Интересно было бы взглянуть... ну или объясните на пальцах

Из моего опыта там серьезный момент когда сервис занят, что происходит как долго запрос "висит" в очереди?
9 май 19, 02:40    [21881601]     Ответить | Цитировать Сообщить модератору
 Re: Программа для нагрузочного тестирования htttp сервисов  [new]
dimonz80
Member

Откуда:
Сообщений: 198
Sergunka
dimonz80
пропущено...


Задать время между запросами как случайную величину с экспоненциальной плотность распределения вероятностей


Ну, это понятно из учебника, код когда нибудь писали? Интересно было бы взглянуть... ну или объясните на пальцах


Код писал для диплома много лет назад, сейчас не найду уже. Только там нагрузка эмулировалась на уровне отдельных пакетов.
Чтобы получить функцию задержки между запросами, надо взять функцию, обратную ПРВ, от равномерно-распределенной случайной величины (обычный rand()). Как-то так, если мне склироз не изменяет


Почитать по теме можно вот такое
Б.С.Лившиц, А.П.Пшеничников, А.Д.Харкевич Теория Телетрафика, гл 7
В. Столлингс Современные Компьютерные сети, гл. 7-9


Из моего опыта там серьезный момент когда сервис занят, что происходит как долго запрос "висит" в очереди?


Дык моделирование для того и нужно, чтобы ответить на вопрос "а с какой вероятностью и временем ожидания будет обслужен запрос, когда на входе поток в 100500 rps, а очередь только на 10 запросов?"
9 май 19, 03:54    [21881604]     Ответить | Цитировать Сообщить модератору
Все форумы / Java Ответить