为了监控springboot应用程序的指标
概述
-
- springbootのアプリケーションのメトリクス監視をしたい
http.client.requests
APIのレスポンスタイムはどれくらいなのか?
APIのリクエスト数はどれくらいなのか?
http.server.requests
内部で使用しているAPIのレスポンスはどれくらいなのか
取得したメトリクスはprometheusやgrafanaで可視化!
※本記事では触れません
使用的程式庫
※摘录
buildscript {
ext {
springBootVersion = '2.1.1.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-actuator')
compile('org.springframework.boot:spring-boot-actuator-autoconfigure')
compile('io.micrometer:micrometer-registry-prometheus')
}
spring-boot-starter-web
メトリクスの取得には関係なし、メトリクス取得対象のAPI作成のために使用
spring-boot-actuator
メトリクスを取得するための機能はこの中に入っている
spring-boot-actuator-autoconfigure
spring-boot-actuatorが提供している機能をDIコンテナに登録する役割
micrometer-registry-prometheus
取得したメトリクスをprometheusから読み取れる形式に変換してくれる
被监视的应用程序
只需要返回「你好,世界!」的API
@Controller
@SpringBootApplication
@Slf4j
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@GetMapping
@ResponseBody
public String hello() {
return "Hello World!";
}
}
网络服务器请求
首先要获取应用程序本身的指标!
实施
在实施过程中所需要的是
-
- application.ymlに設定を記載(もちろんapplication.propertiesでも可)
- だけ!!!
management:
endpoints:
web:
exposure:
include: "prometheus"
base-path: "/"
server:
port: 9990
metrics:
web:
server:
requests-metric-name: "http.server.requests"
distribution:
percentiles:
"http.server.requests": 0.5,0.99
-
- この設定をしておくとlocalhost:9990/prometheusにアクセスするとメトリクスが取得できる!
management.metrics配下の設定値はなくても動きます
requests-metric-nameはメトリクスの名前を任意のものに変更するための設定値です
percentilesを設定しておくとパーセンタイルが取得できます
この設定だと50%ileと99%ile
开始执行bootRun!一次又一次地访问(curl localhost:8080),获取指标!
$ curl localhost:9990/prometheus
# TYPE http_server_requests_seconds summary
http_server_requests_seconds{exception="None",method="GET",outcome="SUCCESS",status="200",uri="/",quantile="0.5",} 0.0
http_server_requests_seconds{exception="None",method="GET",outcome="SUCCESS",status="200",uri="/",quantile="0.99",} 0.0
http_server_requests_seconds_count{exception="None",method="GET",outcome="SUCCESS",status="200",uri="/",} 7.0
http_server_requests_seconds_sum{exception="None",method="GET",outcome="SUCCESS",status="200",uri="/",} 0.086270892
恭喜!
谁在做什么?
提供度量标准的是org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter。
主要的处理可能在这个方法中。
private void filterAndRecordMetrics(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws IOException, ServletException {
TimingContext timingContext = TimingContext.get(request);
if (timingContext == null) {
timingContext = startAndAttachTimingContext(request);
}
try {
filterChain.doFilter(request, response);
if (!request.isAsyncStarted()) {
// Only record when async processing has finished or never been started.
// If async was started by something further down the chain we wait
// until the second filter invocation (but we'll be using the
// TimingContext that was attached to the first)
Throwable exception = (Throwable) request
.getAttribute(DispatcherServlet.EXCEPTION_ATTRIBUTE);
record(timingContext, response, request, exception);
}
}
catch (NestedServletException ex) {
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
record(timingContext, response, request, ex.getCause());
throw ex;
}
}
只要这个类继承了OncePerRequestFilter,并且进行了Bean注册,它就能帮忙收集指标哦!
Bean的注册是什么?
我经常会启用beans终点并进行调查。
management:
endpoints:
web:
exposure:
include: "prometheus,beans"
base-path: "/"
server:
port: 9990
在将beans添加到prometheus之后,
可以通过localhost:9990/beans获取已注册的bean列表。
webMvcMetricsFilter: {
aliases: [ ],
scope: "singleton",
type: "org.springframework.boot.web.servlet.FilterRegistrationBean",
resource: "class path resource
[org/springframework/boot/actuate/autoconfigure/metrics/web/servlet/WebMvcMetricsAutoConfiguration.class]",
dependencies: [
"prometheusMeterRegistry",
"webMvcTagsProvider"
]
},
嗯嗯,是WebMvcMetricsAutoConfiguration吗?
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@ConditionalOnBean(MeterRegistry.class)
使用的是2.1.1版本,它看起来会自动帮我们汇总指标数据,因为它是这样的条件语句。
反过来说,如果安装了库就会自动汇总指标数据,所以如果不需要的话最好不要安装库,这是理所当然的。
http.client.requests 可以被放到Python中的一个库,它用于发送HTTP请求。
实施
-
- 意識しなきゃいけないのはRestTemplate
RestTemplateBuilderから直接buildするときは特に意識する必要なし
独自のbuilderを使いたいときを想定して実装
customizersメソッドにBean登録されているMetricsRestTemplateCustomizerを渡すだけ
何も記載しなくてもBean登録はされてるけど、一応99%ile取得を想定してapplication.ymlも修正
management:
endpoints:
web:
exposure:
include: "prometheus,beans"
base-path: "/"
server:
port: 9990
metrics:
web:
server:
requests-metric-name: "http.server.requests"
client:
requests-metric-name: "http.client.requests"
distribution:
percentiles:
"http.server.requests": 0.5,0.99
"http.client.requests": 0.5,0.99
@Controller
@SpringBootApplication
@Slf4j
public class Application {
private RestTemplate restTemplate;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@GetMapping
@ResponseBody
public String hello() {
return restTemplate.getForObject(URI.create("http://localhost:8080/hello"), String.class);
}
public Application(
RestTemplateBuilder restTemplateBuilder,
MetricsRestTemplateCustomizer metricsRestTemplateCustomizer
) {
// デフォルトのRestTemplateBuilderを使う場合は意識する必要ない
// this.restTemplate = restTemplateBuilder
// .build();
this.restTemplate = restTemplateBuilder
.customizers(metricsRestTemplateCustomizer)
.build();
}
@GetMapping("/hello")
@ResponseBody
public String helloApi() {
return "Hello World!";
}
}
可以按照这样的方式收集度量信息。
$ curl http://localhost:9990/prometheus
# TYPE http_client_requests_seconds summary
http_client_requests_seconds{clientName="localhost",method="GET",status="200",uri="/hello",quantile="0.5",} 0.06291456
http_client_requests_seconds{clientName="localhost",method="GET",status="200",uri="/hello",quantile="0.99",} 0.06291456
http_client_requests_seconds_count{clientName="localhost",method="GET",status="200",uri="/hello",} 1.0
http_client_requests_seconds_sum{clientName="localhost",method="GET",status="200",uri="/hello",} 0.064232646
谁在做什么?
-
- メトリクスを取ってくれているのはorg.springframework.boot.actuate.metrics.web.client.MetricsClientHttpRequestInterceptor。
-
- このクラスは先ほどの実装で登録したcustomizer(MetricsRestTemplateCustomizer)が使用している
MetricsRestTemplateCustomizerはRestTemplateMetricsConfigurationによってBean登録されている
デフォルトの(Bean登録されている)RestTemplateBuilderを使う場合は、RestTemplateBuilderのコンストラクタの引数がRestTemplateCustomizerの配列になっているから、登録されるCustomizerを全部拾ってくれるのね!
conditionalはRestTemplateを見ているからRestTemplateを使っていたらcustomizerはBean登録されているのね!
@ConditionalOnClass(RestTemplate.class)
最后
取得99%的分数轻而易举,真让人吃惊。
制造这个的人真厉害啊。。。