Сравняване на вградени контейнери за сървлети в Spring Boot

1. Въведение

Нарастващата популярност на родните приложения в облака и микро-услугите генерират повишено търсене на вградени контейнери за сървлети. Spring Boot позволява на разработчиците лесно да създават приложения или услуги, използвайки 3-те най-зрели налични контейнера: Tomcat, Undertow и Jetty.

В този урок ще демонстрираме начин за бързо сравняване на реализациите на контейнери, използвайки показатели, получени при стартиране и при известно натоварване.

2. Зависимости

Нашата настройка за всяка налична реализация на контейнер винаги ще изисква да декларираме зависимост от spring-boot-starter-web в нашия pom.xml .

Като цяло искаме да посочим родителя си като spring-boot-starter-parent и след това да включим желаните от нас стартери:

 org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE     org.springframework.boot spring-boot-starter   org.springframework.boot spring-boot-starter-web   

2.1. Tomcat

Не се изискват допълнителни зависимости при използване на Tomcat, защото той е включен по подразбиране при използване на spring-boot-starter-web .

2.2. Пристанище

За да използваме Jetty, първо трябва да изключим spring-boot-starter-tomcat от spring-boot-starter-web .

След това просто декларираме зависимост от spring-boot-starter-jetty :

 org.springframework.boot spring-boot-starter-web   org.springframework.boot spring-boot-starter-tomcat     org.springframework.boot spring-boot-starter-jetty  

2.3. Undertow

Настройката за Undertow е идентична с Jetty, с изключение на това, че използваме spring-boot-starter-undertow като наша зависимост:

 org.springframework.boot spring-boot-starter-web   org.springframework.boot spring-boot-starter-tomcat     org.springframework.boot spring-boot-starter-undertow 

2.4. Задвижващ механизъм

Ще използваме изпълнителния механизъм на Spring Boot като удобен начин за подчертаване на системата и заявка за показатели.

Вижте тази статия за подробности относно актуатора. Ние просто добавяме зависимост в нашия pom, за да го направим достъпно:

 org.springframework.boot spring-boot-starter-actuator 

2.5. Apache Bench

Apache Bench е програма за тестване на натоварване с отворен код, която се доставя в комплект с уеб сървъра на Apache.

Потребителите на Windows могат да изтеглят Apache от един от доставчиците на трети страни, свързани тук. Ако Apache вече е инсталиран на вашата машина с Windows, трябва да можете да намерите ab.exe във вашата директория apache / bin .

Ако сте на машина с Linux, ab може да бъде инсталиран с помощта на apt-get с:

$ apt-get install apache2-utils

3. Показатели за стартиране

3.1. колекция

За да съберем нашите показатели за стартиране, ще регистрираме манипулатор на събития, който да се задейства в ApplicationReadyEvent на Spring Boot .

Ще извлечем програмно метриките, които ни интересуват, като работим директно с MeterRegistry, използван от компонента Actuator:

@Component public class StartupEventHandler { // logger, constructor private String[] METRICS = { "jvm.memory.used", "jvm.classes.loaded", "jvm.threads.live"}; private String METRIC_MSG_FORMAT = "Startup Metric >> {}={}"; private MeterRegistry meterRegistry; @EventListener public void getAndLogStartupMetrics( ApplicationReadyEvent event) { Arrays.asList(METRICS) .forEach(this::getAndLogActuatorMetric); } private void processMetric(String metric) { Meter meter = meterRegistry.find(metric).meter(); Map stats = getSamples(meter); logger.info(METRIC_MSG_FORMAT, metric, stats.get(Statistic.VALUE).longValue()); } // other methods }

Избягваме необходимостта от ръчно заявяване на крайни точки на Actuator REST или от стартиране на самостоятелна JMX конзола, като регистрираме интересни показатели при стартиране в нашия обработчик на събития.

3.2. Избор

Има голям брой показатели, които Actuator предоставя от кутията. Избрахме 3 показателя, които помагат за получаване на общ преглед на ключовите характеристики по време на работа, след като сървърът работи:

  • jvm.memory.used - общата памет, използвана от JVM от стартирането
  • jvm.classes.loaded - общият брой заредени класове
  • jvm.threads.live - общият брой активни нишки. В нашия тест тази стойност може да се разглежда като брой нишки „в покой“

4. Показатели по време на изпълнение

4.1. колекция

В допълнение към предоставянето на метрики за стартиране, ще използваме крайната точка / metrics, изложена от Actuator като целеви URL адрес, когато стартираме Apache Bench, за да натоварим приложението.

За да тестваме реално приложение под товар, вместо това можем да използваме крайни точки, предоставени от нашето приложение.

След като сървърът стартира, ще получим команден ред и ще изпълним ab :

ab -n 10000 -c 10 //localhost:8080/actuator/metrics

В командата по-горе сме посочили общо 10 000 заявки, използвайки 10 едновременни нишки.

4.2. Избор

Apache Bench is able to very quickly give us some useful information including connection times and the percentage of requests that are served within a certain time.

For our purposes, we focused on requests-per-second and time-per-request (mean).

5. Results

On startup, we found that the memory footprint of Tomcat, Jetty, and Undertow was comparable with Undertow requiring slightly more memory than the other two and Jetty requiring the smallest amount.

For our benchmark, we found that the performance of Tomcat, Jetty, and Undertow was comparable but that Undertow was clearly the fastest and Jetty only slightly less fast.

Metric Tomcat Jetty Undertow
jvm.memory.used (MB) 168 155 164
jvm.classes.loaded 9869 9784 9787
jvm.threads.live 25 17 19
Requests per second 1542 1627 1650
Average time per request (ms) 6.483 6.148 6.059

Note that the metrics are, naturally, representative of the bare-bones project; the metrics of your own application will most certainly be different.

6. Benchmark Discussion

Developing appropriate benchmark tests to perform thorough comparisons of server implementations can get complicated. In order to extract the most relevant information, it's critical to have a clear understanding of what's important for the use case in question.

It's important to note that the benchmark measurements collected in this example were taken using a very specific workload consisting of HTTP GET requests to an Actuator endpoint.

It's expected that different workloads would likely result in different relative measurements across container implementations. If more robust or precise measurements were required, it would be a very good idea to set up a test plan that more closely matched the production use case.

In addition, a more sophisticated benchmarking solution such as JMeter or Gatling would likely yield more valuable insights.

7. Choosing a Container

Selecting the right container implementation should likely be based on many factors that can't be neatly summarized with a handful of metrics alone. Comfort level, features, available configuration options, and policy are often equally important, if not more so.

8. Conclusion

В тази статия разгледахме реализациите на вградения контейнер за сервлети Tomcat, Jetty и Undertow. Проучихме характеристиките на изпълнение на всеки контейнер при стартиране с конфигурациите по подразбиране, като разгледахме показатели, изложени от компонента Actuator.

Изпълнихме измислено натоварване спрямо работещата система и след това измерихме производителността с помощта на Apache Bench.

И накрая, обсъдихме достойнствата на тази стратегия и споменахме няколко неща, които трябва да имате предвид при сравняване на еталоните за изпълнение. Както винаги, целият изходен код може да бъде намерен в GitHub.