Мои товарищи по команде разрабатывают REST API с использованием Spring Boot + PostgreSQL + Redis + Kafka и т. Д., В то время как я создаю инструмент мониторинга с использованием Prometheus, Grafana и т. Д. Для мониторинга этого API.
Этот API предоставляет четыре конечные точки. Давайте назовем их A, B, C и D.
Я хотел собрать метрики "приблизительного времени отклика каждой конечной точки API". Поэтому я написал простые скрипты (Bash и Perl), которые измеряют время, затраченное на вызов каждой конечной точки, используя curl
. А затем я зарегистрировал сценарии, используя crontab, чтобы выполнять их раз в минуту.
# get response time
curl -XGET -s -w "\\n%{http_code}\\n%{time_total}\\n" "http://for.example/A" | tail -n 1 >> log_A
curl -XGET -s -w "\\n%{http_code}\\n%{time_total}\\n" "http://for.example/B" | tail -n 1 >> log_B
...
Результат, как вы видите ниже, несколько странный:
Для вызова одной конечной точки потребовалось около 100 мс, а для вызова остальных - всего несколько миллисекунд.
Я заглянул в него и обнаружил, что первый вызов каждого задания cron занимает намного больше времени . То есть, если я измерил A, B, C, то D, A заняло 100 мс. Если я измерил B, C, D, то A, B потребовалось 100 мс. Следующие три конечные точки откликнутся очень скоро. На рисунке я изменил последовательность и сразу понял, что это повлияло на результат.
Я подозреваю, что соединение между загрузочным приложением Spring и базой данных (или redis, или kafka?) Отключено из-за истечения срока действия, поэтому для повторного подключения требуется время. Но я думаю, что одна минута слишком коротка для любой конфигурации, чтобы истечь любое соединение. Во всяком случае, с какого момента я должен начать?
Буду признателен за любой совет.
Edit:
После написания этого поста я запустил очень простое приложение Spring Boot REST API, код которого был взят из руководства по загрузке Spring (https://spring.io/guides/gs/rest-service), без использования БД и каких-либо внешних вещей. Этот API всегда занимает всего 4мс до curl
. Поэтому я больше подозреваю о внешних вещах.
Если вам нужно посмотреть настройки загрузочного приложения Spring. Вот pom.xml
и application.yaml
:
<?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">
<parent>
<groupId>com.company</groupId>
<artifactId>sylphid</artifactId>
<version>0.1.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>personal</artifactId>
<version>0.3.0</version>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-batch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- kafka -->
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
<version>${spring-kafka.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${swagger.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-spi -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-spi</artifactId>
<version>${swagger.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-spring-web -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-spring-web</artifactId>
<version>${swagger.version}</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>false</filtering>
</resource>
</resources>
<finalName>personal</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.21.0</version>
<configuration>
<images>
<image>
<name>sylphid/${project.build.finalName}</name>
<build>
<from>openjdk:8u162-jdk</from>
<entryPoint>java -Dspring.profiles.active=docker -jar /application/${project.build.finalName}.jar</entryPoint>
<assembly>
<basedir>/application</basedir>
<descriptorRef>artifact</descriptorRef>
<inline>
<id>assembly</id>
<files>
<file>
<source>target/${project.build.finalName}.jar</source>
</file>
</files>
</inline>
</assembly>
<tags>
<tag>latest</tag>
<tag>${project.version}</tag>
</tags>
<ports>
<port>8080</port>
</ports>
</build>
<run>
<namingStrategy>alias</namingStrategy>
</run>
<alias>${project.build.finalName}</alias>
</image>
</images>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>sonatype-nexus-snapshots</id>
<name>Sonatype Nexus Snapshots</name>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
<releases>
<enabled>false</enabled>
</releases>
</repository>
</repositories>
</project>
spring:
profiles: allnative
application:
name: personal
jpa:
database-platform: org.hibernate.dialect.PostgreSQL9Dialect
properties:
hibernate:
temp.use_jdbc_metadata_defaults: false
show_sql: false
format_sql: false
use_sql_comments: false
hibernate:
ddl-auto: update
datasource:
driver-class-name: org.postgresql.Driver
url: jdbc:postgresql://127.0.0.1:5432/sylphid
username: postgres
batch:
initialize-schema: always
cache:
type: redis
redis:
key-prefix: sylphid_
time-to-live: 60m
redis:
host: 127.0.0.1
port: 6379
kafka:
bootstrap-servers: 127.0.0.1:9092
consumer:
auto-offset-reset: earliest
group-id: bookclub
server:
port: 13480
app:
topic:
selection: bookclub.selection
management:
endpoints:
web:
exposure:
include: "*"
endpoint:
health:
show-details: always
logging:
file: allnative.log
Edit (2):
Я звонил curl
для каждой конечной точки без задержки:
curl endpointA; curl endpointB; curl endpointC; curl endpointD
( almost 60 seconds interval before next cronjob )
curl endpointA; curl endpointB; curl endpointC; curl endpointD
...
Вчера я попытался вставить некоторую задержку между каждым curl
выполнением, предположив, что «интервал между последним запросом и текущим запросом влияет на время ответа текущего запроса». (как ... человек-работник может заснуть, если никто не просит его что-то сделать в течение длительного времени. Когда кто-то зовет его, ему нужно еще немного , чтобы проснуться. Если кто-то еще зовет его пока он не спит, он может выполнять вторую работу быстрее.)
Когда я вставил задержку более 2 секунд, я увидел, что другая конечная точка стала работать медленно. Когда я вставил 10 секунд задержки:
curl endpointA; sleep 10; curl endpointB; sleep 10;...
( about 20 seconds interval, because crontab still executes every minutes )
curl endpointA; sleep 10; curl endpointB; sleep 10;...
Вот результат. Каждые конечные точки начали реагировать примерно через 100 мс.
Edit (3)
В качестве новой попытки я попытался использовать Jetty в качестве встроенного сервера вместо Tomcat. Результат был несколько впечатляющим. Приложение, использующее Jetty, показало очень стабильное время отклика почти для каждого измерения. Даже Jetty иногда показывал большое время отклика (около 300 мс или около того), но это были очень редкие случаи.
Я наблюдал за двумя тестовыми наборами (Tomcat и Jetty) в течение нескольких часов, и я собираюсь наблюдать один или несколько дней. Если этот результат будет продолжаться, я планирую рассказать об этом своим товарищам по команде и предложить заменить встроенный сервер на Jetty.
Однако, если Tomcat является причиной проблемы, я понятия не имею, почему простое приложение Spring Boot из официального руководства не показывает такой симптом.