Spring Boot 使用 Micrometer 集成 Prometheus 監控 Java 應用性能

1、Micrometer 介紹

Micrometer 爲 Java 平臺上的性能數據收集提供了一個通用的 API,它提供了多種度量指標類型(Timers、Guauges、Counters等),同時支持接入不同的監控系統,例如 Influxdb、Graphite、Prometheus 等。我們可以通過 Micrometer 收集 Java 性能數據,配合 Prometheus 監控系統實時獲取數據,並最終在 Grafana 上展示出來,從而很容易實現應用的監控。

Micrometer 中有兩個最核心的概念,分別是計量器(Meter)和計量器註冊表(MeterRegistry)。計量器用來收集不同類型的性能指標信息,Micrometer 提供瞭如下幾種不同類型的計量器:

  • 計數器(Counter): 表示收集的數據是按照某個趨勢(增加/減少)一直變化的,也是最常用的一種計量器,例如接口請求總數、請求錯誤總數、隊列數量變化等。
  • 計量儀(Gauge): 表示蒐集的瞬時的數據,可以任意變化的,例如常用的 CPU Load、Mem 使用量、Network 使用量、實時在線人數統計等,
  • 計時器(Timer): 用來記錄事件的持續時間,這個用的比較少。
  • 分佈概要(Distribution summary): 用來記錄事件的分佈情況,表示一段時間範圍內對數據進行採樣,可以用於統計網絡請求平均延遲、請求延遲佔比等。

2、環境、軟件準備

本次演示環境,我是在本機 MAC OS 上操作,以下是安裝的軟件及版本:

  • Docker: 18.06.3-ce
  • Oracle VirtualBox: 6.0.8 r130520 (Qt5.6.3)
  • Linux: 7.6.1810
  • Prometheus: v2.11.1
  • Grafana: v6.3.4

注意:這裏爲了快速方便啓動 Prometheus、Grafana 服務,我使用 Docker 方式啓動,所以本機需要安裝好 Docker 環境,這裏忽略 Docker 的安裝過程,着重介紹一下 Spring Boot 項目如何使用 Micrometer 來監控 Java 應用性能,並集成到 Prometheus 最終使用 Grafana Dashboard 展示出來。

3、Spring Boot 工程集成 Micrometer

我們一般說 Spring Boot 集成 Micrometer 值得時 Spring 2.x 版本,因爲在該版本 spring-boot-actuator 使用了 Micrometer 來實現監控,而在 Spring Boot 1.5x 中可以通過micrometer-spring-legacy 來使用 micrometer,顯然在 2.x 版本有更高的集成度,使用起來也非常方便了。那麼創建一個 Spring Boot 2.1.4.RELEASE 版本的工程 springboot2-promethues,首先添加依賴如下:

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-web</artifactId>
	</dependency>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-actuator</artifactId>
	</dependency>
	<dependency>
		<groupId>io.micrometer</groupId>
		<artifactId>micrometer-registry-prometheus</artifactId>
		<version>1.1.4</version>
	</dependency>

這裏引入了 io.micrometermicrometer-registry-prometheus 依賴以及 spring-boot-starter-actuator 依賴,因爲該包對 Prometheus 進行了封裝,可以很方便的集成到 Spring Boot 工程中。

其次在 application.properties 中配置如下:

server.port=8088
spring.application.name=springboot2-prometheus
management.endpoints.web.exposure.include=*
management.metrics.tags.application=${spring.application.name}

這裏 management.endpoints.web.exposure.include=* 配置爲開啓 Actuator 服務,因爲Spring Boot Actuator 會自動配置一個 URL 爲 /actuator/Prometheus 的 HTTP 服務來供 Prometheus 抓取數據,不過默認該服務是關閉的,該配置將打開所有的 Actuator 服務。management.metrics.tags.application 配置會將該工程應用名稱添加到計量器註冊表的 tag 中去,方便後邊 Prometheus 根據應用名稱來區分不同的服務。

然後在工程啓動主類中添加 Bean 如下來監控 JVM 性能指標信息:

@SpringBootApplication
public class Springboot2PrometheusApplication {

    public static void main(String[] args) {
        SpringApplication.run(Springboot2PrometheusApplication.class, args);
    }

    @Bean
    MeterRegistryCustomizer<MeterRegistry> configurer(@Value("${spring.application.name}") String applicationName){
        return registry -> registry.config().commonTags("application", applicationName);
    }
}

最後,啓動服務,瀏覽器訪問 http://127.0.0.1:8088/actuator/prometheus 就可以看到應用的 一系列不同類型 metrics 信息,例如 http_server_requests_seconds summaryjvm_memory_used_bytes gaugejvm_gc_memory_promoted_bytes_total counter 等等。
spring-prometheus-actuator

4、配置 Prometheus 監控應用指標

Prometheus 的安裝配置可參考之前文章 Prometheus 監控報警系統 AlertManager 之郵件告警,寫的很詳細,這裏就不在詳細說明了,修改 prometheus.yml 配置,在上篇文章配置示例基礎上,添加上邊啓動的服務地址來執行監控。

$ vim prommetheus.yml
......
- job_name: 'application'
    scrape_interval: 5s
    metrics_path: '/actuator/prometheus'
    file_sd_configs:
      - files: ['/usr/local/prometheus/groups/applicationgroups/*.json']

這裏依然採用 file_sd_configs 方式動態服務發現,新建 <local_dir>/groups/applicationgroups/application.json 文件如下:

$ vim groups/applicationgroups/application.json
[
    {
        "targets": [
            "192.168.1.124:8088"
        ],
        "labels": {
            "instance": "springboot2-prometheus",
            "service": "springboot2-prometheus-service"
        }
    }
]

這裏 192.168.1.124:8088 就是上邊本地啓動的服務地址,也就是 Prometheus 要監控的服務地址,同時添加一些與應用相關的標籤,方便後期執行 PromSQL 查詢語句區分。最後重啓 Prometheus 服務,查看 Prometheus UI 界面確認 Target 是否添加成功。
spring-prometheus-target

在 Graph 頁面執行一個簡單的查詢,也是獲取 springboot2-prometheus 服務的 JVM 性能指標值的。

5、配置 Grafana Dashboard 展示監控項

Prometheus 現在已經可以正常監控到應用 JVM 信息了,那麼我們可以配置 Grafana Dashboard 來優雅直觀的展示出來這些監控值了。首先創建 Grafana 服務,這裏爲了方便,依舊採用 Docker 的方式啓動,啓動命令如下:

$ docker run -d -p 3000:3000 --name=grafana grafana/grafana

啓動完畢後,瀏覽器訪問 http://192.168.1.121:3000 即可,首次登錄使用 admin:admin 默認賬戶密碼登錄並修改密碼。登錄完畢,需要添加數據源,這裏我們要添加的就是上邊 Prometheus 數據源,配置如下圖:
spring-prometheus-datasource
配置完畢,接下來需要導入對應的監控 JVM 的 Dashboard 模板,模板編號爲 4701
spring-prometheus-dashboard
導入完畢後,就可以看到 JVM (Micrometer) 各種類型指標監控圖形化以後的頁面。
spring-prometheus-jvm-dashboard

6、自定義監控指標並展示到 Grafana

上邊是 spring-boot-actuator 集成了 Micrometer 來提供的默認監控項,覆蓋 JVM 各個層間的監控,配合 Grafana Dashboard 模板基本可以滿足我們日常對 Java 應用的監控。當然,它也支持自定義監控指標,實現各個方面的監控,例如統計訪問某一個 API 接口的請求數,統計實時在線人數、統計實時接口響應時間等功能,而這些都可以通過使用上邊的四種計量器來實現。接下來,來演示下如何自定義監控指標並展示到 Grafana 上。

6.1、監控某幾個 API 請求次數

我們繼續在 springboot2-promethues 工程上添加 IndexController.java,來實現分別統計訪問 index 及 core 接口請求次數,代碼如下:

@RestController
@RequestMapping("/v1")
public class IndexController {

    @Autowired
    MeterRegistry registry;

    private Counter counter_core;
    private Counter counter_index;

    @PostConstruct
    private void init(){
        counter_core = registry.counter("app_requests_method_count", "method", "IndexController.core");
        counter_index = registry.counter("app_requests_method_count", "method", "IndexController.index");
    }

    @RequestMapping(value = "/index")
    public Object index(){
        try{
            counter_index.increment();
        } catch (Exception e) {
            return e;
        }
        return counter_index.count() + " index of springboot2-prometheus.";
    }

    @RequestMapping(value = "/core")
    public Object coreUrl(){
        try{
            counter_core.increment();
        } catch (Exception e) {
            return e;
        }
        return counter_core.count() + " coreUrl Monitor by Prometheus.";
    }
}

說明一下,這裏是一個簡單的 RestController 接口,使用了 Counter 計量器來統計訪問 /v1/index/v1/core 接口訪問量。因爲訪問數會持續的增加,所以這裏使用 Counter 比較合適。啓動服務,我們來分別訪問一下這兩個接口,爲了更好的配合下邊演示,可以多訪問幾次。
spring-prometheus-counter-index
spring-prometheus-counter-core
服務可以正常訪問,並且訪問了 6 次 /v1/index,訪問了 10 次 /v1/core。接下來,我們可以到 Prometheus UI 界面上使用 PromSQL 查詢自定義的監控信息了。分別添加 Graph 並執行如下查詢語句,查詢結果如下:
spring-prometheus-graph
spring-prometheus-graph
可以看到正確統計出來這兩個接口請求的訪問數,這裏解釋一下查詢語句:app_requests_method_count_total{application="springboot2-prometheus", instance="springboot2-prometheus", method="IndexController.core"} 這裏的

  • app_requests_method_count_total 爲上邊代碼中設置的 Counter 名稱。
  • application 爲初始化 registry 時設置的通用標籤,標註應用名稱,這樣做好處就是可以根據應用名稱區分不同的應用。
  • instance<local_dir>/groups/applicationgroups/application.json 中配置的 instance 實例名稱,用來區分應用實例。
  • method 爲上邊代碼中設置的 Counter 標籤名稱,可以用來區分不同的方法,這樣就不用爲每一個方法設置一個 Counter 了。

接下來,我們在 Grafana Dashboard 上添加一個新的 Panel 並添加 Query 查詢,最後圖形化展示出來。首先添加一個 Panel 並命名爲 自定義監控指標,然後點擊 Add Query 增加一個新的 Query 查詢,查詢語句爲上邊的 PromSQL 語句,不過這裏爲了更好的擴展性,我們可以將 applicationinstance 兩個參數賦值爲變量,而這些變量可以直接從 Prometheus 上傳遞過來,最終的查詢語句爲 app_requests_method_count_total{application="$application", instance="$instance", method="IndexController.core"},最後修改 Title 爲 實時訪問量 /v1/core,保存一下,返回首頁就可以看到剛添加的 Dashboard 了,是不是很直觀。
spring-prometheus-grafana-jvm

6.2、監控所有 API 請求次數

上邊針對某個或某幾個接口請求次數做了監控,如果我們想針對整個應用監控所有接口請求總次數,這個該如何實現呢?監控請求次數可以繼續使用 Counter 計數器,整個應用所有請求,我們自然而然的想到了 Spring AOP,通過切面注入可以做到統計所有請求記錄,添加依賴如下:

...
	<dependency>
		<groupId>org.aspectj</groupId>
		<artifactId>aspectjrt</artifactId>
		<version>1.9.4</version>
	</dependency>
	<dependency>
		<groupId>org.aspectj</groupId>
		<artifactId>aspectjweaver</artifactId>
		<version>1.9.4</version>
	</dependency>
	<dependency>
		<groupId>cglib</groupId>
		<artifactId>cglib</artifactId>
		<version>3.2.12</version>
	</dependency>

添加 AspectAop.java 代碼到 Springboot2PrometheusApplication.java 同級目錄, 代碼如下:

@Component
@Aspect
public class AspectAop {

    @Autowired
    MeterRegistry registry;

    private Counter counter_total;

    ThreadLocal<Long> startTime = new ThreadLocal<>();

    @Pointcut("execution(public * com.promethues.demo.controller.*.*(..))")
    private void pointCut(){}

    @PostConstruct
    public void init(){
        counter_total = registry.counter("app_requests_count", "v1", "core");
    }

    @Before("pointCut()")
    public void doBefore(JoinPoint joinPoint)throws Throwable {
        startTime.set(System.currentTimeMillis());
        counter_total.increment();
    }

    @AfterReturning(returning = "returnVal", pointcut = "pointCut()")
    public void doAftereReturning(Object returnVal){
        System.out.println("請求執行時間:" + (System.currentTimeMillis() - startTime.get()));
    }
}

這裏 Spring AOP 操作代碼就不在說了,我們創建了一個名稱爲 app_requests_count 的 Counter,所有請求過來都會執行 counter_total.increment(); 操作,從而實現統計所有請求總數。重啓服務,訪問多次不同的接口,然後在 Prometheus UI 界面執行 PromSQL 查詢,查詢語句爲 app_requests_count_total{application="springboot2-prometheus", instance="springboot2-prometheus", v1="core"} 查詢結果如下:
spring-prometheus-graph
可以看到,能夠正確統計出來所有的請求數量,現在,我們可以在 Grafana 上之前的面板上增加一個新的 Query 並圖形化顯示出來了,Query 語句爲: app_requests_count_total{application="$application", instance="$instance",v1="core"}, 添加完成後,展示效果如下:
spring-prometheus-grafana-counter

6.3、監控實時在線人數

接下來,來演示下如何監控瞬時數據變化,例如實時交易總金額,實時網絡請求響應時間,實時在線人數等,這裏我們簡單模擬一下實時在線人數監控,這裏採用 Gauge 計量儀來做爲指標統計類型,在 IndexController.java 中添加相關代碼如下:

@RestController
@RequestMapping("/v1")
public class IndexController {

    @Autowired
    MeterRegistry registry;

    private Counter counter_core;
    private Counter counter_index;
    private AtomicInteger app_online_count;

    @PostConstruct
    private void init(){
        counter_core = registry.counter("app_requests_method_count", "method", "IndexController.core");
        counter_index = registry.counter("app_requests_method_count", "method", "IndexController.index");
        app_online_count = registry.gauge("app_online_count", new AtomicInteger(0));
    }

    @RequestMapping(value = "/index")
    public Object index(){
        try{
            counter_index.increment();
        } catch (Exception e) {
            return e;
        }
        return counter_index.count() + " index of springboot2-prometheus.";
    }

    @RequestMapping(value = "/core")
    public Object coreUrl(){
        try{
            counter_core.increment();
        } catch (Exception e) {
            return e;
        }
        return counter_core.count() + " coreUrl Monitor by Prometheus.";
    }
    
    @RequestMapping(value = "/online")
    public Object onlineCount(){
        int people = 0;
        try {
            people = new Random().nextInt(2000);
            app_online_count.set(people);
        } catch (Exception e){
            return e;
        }
        return "current online people: " + people;
    }
}

重啓服務,訪問一下 /v1/online 接口,得到一個 2000 以內的隨機數作爲實時在線人數,瀏覽器訪問一下,得到結果如下:
spring-prometheus-online
我們在 Prometheus UI 界面執行一下 PromeSQL 查詢語句 app_online_count{application="springboot2-prometheus", instance="springboot2-prometheus"},同樣能夠對應獲取到實時數據。
spring-prometheus-graph
繼續在 Grafana 上之前的面板上增加一個新的 Query 並圖形化顯示出來,Query 語句爲: app_online_count{application="$application", instance="$instance"}, 添加完成後,展示效果如下:
spring-prometheus-guage
注意:這裏我採用了 Grafana 中 Gauge 圖形來展示的,可以根據實際要求來展示對應的數據。

參考資料

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章