你做得這個SpringBoot監(jiān)控绊茧,看起來真炫酷B料(上篇)

# 前提

spring-actuator做度量統(tǒng)計收集,使用Prometheus(普羅米修斯)進行數(shù)據(jù)收集,Grafana(增強ui)進行數(shù)據(jù)展示鹏秋,用于監(jiān)控生成環(huán)境機器的性能指標(biāo)和業(yè)務(wù)數(shù)據(jù)指標(biāo)尊蚁。一般,我們叫這樣的操作為”埋點”侣夷。SpringBoot中的依賴spring-actuator中集成的度量統(tǒng)計API使用的框架是Micrometer横朋,官網(wǎng)是Micrometer.io。

在實踐中發(fā)現(xiàn)了業(yè)務(wù)開發(fā)者濫用了Micrometer的度量類型Counter百拓,導(dǎo)致無論什么情況下都只使用計數(shù)統(tǒng)計的功能琴锭。這篇文章就是基于Micrometer分析其他的度量類型API的作用和適用場景。

# Micrometer提供的度量類庫

Meter是指一組用于收集應(yīng)用中的度量數(shù)據(jù)的接口衙传,Meter單詞可以翻譯為”米”或者”千分尺”决帖,但是顯然聽起來都不是很合理,因此下文直接叫Meter蓖捶,理解它為度量接口即可地回。Meter是由MeterRegistry創(chuàng)建和保存的,可以理解MeterRegistry是Meter的工廠和緩存中心腺阳,一般而言每個JVM應(yīng)用在使用Micrometer的時候必須創(chuàng)建一個MeterRegistry的具體實現(xiàn)落君。

Micrometer中穿香,Meter的具體類型包括:Timer亭引,Counter,Gauge皮获,DistributionSummary焙蚓,LongTaskTimer,F(xiàn)unctionCounter洒宝,F(xiàn)unctionTimer和TimeGauge购公。

下面分節(jié)詳細介紹這些類型的使用方法和實戰(zhàn)使用場景。而一個Meter具體類型需要通過名字和Tag(這里指的是Micrometer提供的Tag接口)作為它的唯一標(biāo)識雁歌,這樣做的好處是可以使用名字進行標(biāo)記宏浩,通過不同的Tag去區(qū)分多種維度進行數(shù)據(jù)統(tǒng)計。

# MeterRegistry

MeterRegistry在Micrometer是一個抽象類靠瞎,主要實現(xiàn)包括:

SimpleMeterRegistry:每個Meter的最新數(shù)據(jù)可以收集到SimpleMeterRegistry實例中比庄,但是這些數(shù)據(jù)不會發(fā)布到其他系統(tǒng),也就是數(shù)據(jù)是位于應(yīng)用的內(nèi)存中的乏盐。

CompositeMeterRegistry:多個MeterRegistry聚合佳窑,內(nèi)部維護了一個MeterRegistry的列表。

全局的MeterRegistry:工廠類io.micrometer.core.instrument.Metrics中持有一個靜態(tài)final的CompositeMeterRegistry實例globalRegistry父能。

當(dāng)然神凑,使用者也可以自行繼承MeterRegistry去實現(xiàn)自定義的MeterRegistry。

SimpleMeterRegistry適合做調(diào)試的時候使用何吝,它的簡單使用方式如下:

MeterRegistry registry = new SimpleMeterRegistry();

Counter counter = registry.counter("counter");

counter.increment();

CompositeMeterRegistry實例初始化的時候溉委,內(nèi)部持有的MeterRegistry列表是空的鹃唯,如果此時用它新增一個Meter實例,Meter實例的操作是無效的

CompositeMeterRegistry composite = new CompositeMeterRegistry();

Counter compositeCounter = composite.counter("counter");

compositeCounter.increment(); // <- 實際上這一步操作是無效的,但是不會報錯

SimpleMeterRegistry simple = new SimpleMeterRegistry();

composite.add(simple);? // <- 向CompositeMeterRegistry實例中添加SimpleMeterRegistry實例

compositeCounter.increment();? // <-計數(shù)成功

全局的MeterRegistry的使用方式更加簡單便捷瓣喊,因為一切只需要操作工廠類Metrics的靜態(tài)方法:

Metrics.addRegistry(new SimpleMeterRegistry());

Counter counter = Metrics.counter("counter", "tag-1", "tag-2");

counter.increment();

# Tag與Meter的命名

Micrometer中俯渤,Meter的命名約定使用英文逗號(dot,也就是”.”)分隔單詞型宝。但是對于不同的監(jiān)控系統(tǒng)八匠,對命名的規(guī)約可能并不相同,如果命名規(guī)約不一致趴酣,在做監(jiān)控系統(tǒng)遷移或者切換的時候梨树,可能會對新的系統(tǒng)造成破壞。

Micrometer中使用英文逗號分隔單詞的命名規(guī)則岖寞,再通過底層的命名轉(zhuǎn)換接口NamingConvention進行轉(zhuǎn)換抡四,最終可以適配不同的監(jiān)控系統(tǒng),同時可以消除監(jiān)控系統(tǒng)不允許的特殊字符的名稱和標(biāo)記等仗谆。開發(fā)者也可以覆蓋NamingConvention實現(xiàn)自定義的命名轉(zhuǎn)換規(guī)則:registry.config().namingConvention(myCustomNamingConvention);指巡。

在Micrometer中,對一些主流的監(jiān)控系統(tǒng)或者存儲系統(tǒng)的命名規(guī)則提供了默認的轉(zhuǎn)換方式隶垮,例如當(dāng)我們使用下面的命名時候:

MeterRegistry registry = ...

registry.timer("http.server.requests");

對于不同的監(jiān)控系統(tǒng)或者存儲系統(tǒng)藻雪,命名會自動轉(zhuǎn)換如下:

Prometheus - http_server_requests_duration_seconds。

Atlas - httpServerRequests狸吞。

Graphite - http.server.requests勉耀。

InfluxDB - http_server_requests。

其實NamingConvention已經(jīng)提供了5種默認的轉(zhuǎn)換規(guī)則:dot蹋偏、snakeCase便斥、camelCase、upperCamelCase和slashes威始。

另外枢纠,Tag(標(biāo)簽)是Micrometer的一個重要的功能,嚴格來說黎棠,一個度量框架只有實現(xiàn)了標(biāo)簽的功能晋渺,才能真正地多維度進行度量數(shù)據(jù)收集。Tag的命名一般需要是有意義的葫掉,所謂有意義就是可以根據(jù)Tag的命名可以推斷出它指向的數(shù)據(jù)到底代表什么維度或者什么類型的度量指標(biāo)些举。

假設(shè)我們需要監(jiān)控數(shù)據(jù)庫的調(diào)用和Http請求調(diào)用統(tǒng)計,一般推薦的做法是:

MeterRegistry registry = ...

registry.counter("database.calls", "db", "users")

registry.counter("http.requests", "uri", "/api/users")

這樣俭厚,當(dāng)我們選擇命名為”database.calls”的計數(shù)器户魏,我們可以進一步選擇分組”db”或者”users”分別統(tǒng)計不同分組對總調(diào)用數(shù)的貢獻或者組成。一個反例如下:

MeterRegistry registry = ...

registry.counter("calls",

? ? "class", "database",

? ? "db", "users");

registry.counter("calls",

? ? "class", "http",

? ? "uri", "/api/users");

通過命名”calls”得到的計數(shù)器,由于標(biāo)簽混亂叼丑,數(shù)據(jù)是基本無法分組統(tǒng)計分析关翎,這個時候可以認為得到的時間序列的統(tǒng)計數(shù)據(jù)是沒有意義的○牛可以定義全局的Tag纵寝,也就是全局的Tag定義之后,會附加到所有的使用到的Meter上(只要是使用同一MeterRegistry)星立,全局的Tag可以這樣定義:

MeterRegistry registry = ...

registry.counter("calls",

? ? "class", "database",

? ? "db", "users");

registry.counter("calls",

? ? "class", "http",

? ? "uri", "/api/users");

MeterRegistry registry = ...

registry.config().commonTags("stack", "prod", "region", "us-east-1");

// 和上面的意義是一樣的

registry.config().commonTags(Arrays.asList(Tag.of("stack", "prod"), Tag.of("region", "us-east-1")));

像上面這樣子使用爽茴,就能通過主機,實例绰垂,區(qū)域室奏,堆棧等操作環(huán)境進行多維度深入

分析。

還有兩點點需要注意:

1劲装、Tag的值必須不為null胧沫。

2、Micrometer中占业,Tag必須成對出現(xiàn)绒怨,也就是Tag必須設(shè)置為偶數(shù)個,實際上它們以Key=Value的形式存在谦疾,具體可以看io.micrometer.core.instrument.Tag接口:

public interface Tag extends Comparable<Tag> {

? ? String getKey();

? ? String getValue();

? ? static Tag of(String key, String value) {

? ? ? ? return new ImmutableTag(key, value);

? ? }

? ? default int compareTo(Tag o) {

? ? ? ? return this.getKey().compareTo(o.getKey());

? ? }

}

當(dāng)然南蹂,有些時候,我們需要過濾一些必要的標(biāo)簽或者名稱進行統(tǒng)計餐蔬,或者為Meter的名稱添加白名單碎紊,這個時候可以使用MeterFilter佑附。MeterFilter本身提供一些列的靜態(tài)方法樊诺,多個MeterFilter可以疊加或者組成鏈實現(xiàn)用戶最終的過濾策略。例如:

MeterRegistry registry = ...

registry.config()

? ? .meterFilter(MeterFilter.ignoreTags("http"))

? ? .meterFilter(MeterFilter.denyNameStartsWith("jvm"));

表示忽略”http”標(biāo)簽音同,拒絕名稱以”jvm”字符串開頭的Meter词爬。更多用法可以參詳一下MeterFilter這個類。

Meter的命名和Meter的Tag相互結(jié)合权均,以命名為軸心顿膨,以Tag為多維度要素,可以使度量數(shù)據(jù)的維度更加豐富叽赊,便于統(tǒng)計和分析恋沃。

# Meters

前面提到Meter主要包括:Timer,Counter必指,Gauge囊咏,DistributionSummary,LongTaskTimer,F(xiàn)unctionCounter梅割,F(xiàn)unctionTimer和TimeGauge霜第。下面逐一分析它們的作用和個人理解的實際使用場景(應(yīng)該說是生產(chǎn)環(huán)境)。

# Counter

Counter是一種比較簡單的Meter户辞,它是一種單值的度量類型泌类,或者說是一個單值計數(shù)器。Counter接口允許使用者使用一個固定值(必須為正數(shù))進行計數(shù)底燎。準(zhǔn)確來說:Counter就是一個增量為正數(shù)的單值計數(shù)器刃榨。這個舉個很簡單的使用例子:

使用場景:

Counter的作用是記錄XXX的總量或者計數(shù)值,適用于一些增長類型的統(tǒng)計双仍,例如下單喇澡、支付次數(shù)、Http請求總量記錄等等殊校,通過Tag可以區(qū)分不同的場景晴玖,對于下單,可以使用不同的Tag標(biāo)記不同的業(yè)務(wù)來源或者是按日期劃分为流,對于Http請求總量記錄呕屎,可以使用Tag區(qū)分不同的URL。用下單業(yè)務(wù)舉個例子:

//實體

@Data

public class Order {

? ? private String orderId;

? ? private Integer amount;

? ? private String channel;

? ? private LocalDateTime createTime;

}

public class CounterMain {

? ? private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

? ? static {

? ? ? ? ? ? Metrics.addRegistry(new SimpleMeterRegistry());

? ? ? ? }

? ? ? ? public static void main(String[] args) throws Exception {

? ? ? ? ? ? Order order1 = new Order();

? ? ? ? ? ? order1.setOrderId("ORDER_ID_1");

? ? ? ? ? ? order1.setAmount(100);

? ? ? ? ? ? order1.setChannel("CHANNEL_A");

? ? ? ? ? ? order1.setCreateTime(LocalDateTime.now());

? ? ? ? ? ? createOrder(order1);

? ? ? ? ? ? Order order2 = new Order();

? ? ? ? ? ? order2.setOrderId("ORDER_ID_2");

? ? ? ? ? ? order2.setAmount(200);

? ? ? ? ? ? order2.setChannel("CHANNEL_B");

? ? ? ? ? ? order2.setCreateTime(LocalDateTime.now());

? ? ? ? ? ? createOrder(order2);

? ? ? ? ? ? Search.in(Metrics.globalRegistry).meters().forEach(each -> {

? ? ? ? ? ? ? ? StringBuilder builder = new StringBuilder();

? ? ? ? ? ? ? ? builder.append("name:")

? ? ? ? ? ? ? ? ? ? ? ? .append(each.getId().getName())

? ? ? ? ? ? ? ? ? ? ? ? .append(",tags:")

? ? ? ? ? ? ? ? ? ? ? ? .append(each.getId().getTags())

? ? ? ? ? ? ? ? ? ? ? ? .append(",type:").append(each.getId().getType())

? ? ? ? ? ? ? ? ? ? ? ? .append(",value:").append(each.measure());

? ? ? ? ? ? ? ? System.out.println(builder.toString());

? ? ? ? ? ? });

? ? }

? ? private static void createOrder(Order order) {

? ? ? ? //忽略訂單入庫等操作

? ? ? ? Metrics.counter("order.create",

? ? ? ? ? ? ? ? "channel", order.getChannel(),

? ? ? ? ? ? ? ? "createTime", FORMATTER.format(order.getCreateTime())).increment();

? ? }

}

控制臺輸出

name:order.create,tags:[tag(channel=CHANNEL_A), tag(createTime=2018-11-10)],type:COUNTER,value:[Measurement{statistic='COUNT', value=1.0}]

name:order.create,tags:[tag(channel=CHANNEL_B), tag(createTime=2018-11-10)],type:COUNTER,value:[Measurement{statistic='COUNT', value=1.0}]

上面的例子是使用全局靜態(tài)方法工廠類Metrics去構(gòu)造Counter實例敬察,實際上秀睛,

io.micrometer.core.instrument.Counter接口提供了一個內(nèi)部建造器類

Counter.Builder去實例化Counter,Counter.Builder的使用方式如下:

public class CounterBuilderMain {

? ? ? ? public static void main(String[] args) throws Exception{

? ? ? ? ? ? Counter counter = Counter.builder("name")? //名稱

? ? ? ? ? ? ? ? ? ? .baseUnit("unit") //基礎(chǔ)單位

? ? ? ? ? ? ? ? ? ? .description("desc") //描述

? ? ? ? ? ? ? ? ? ? .tag("tagKey", "tagValue")? //標(biāo)簽

? ? ? ? ? ? ? ? ? ? .register(new SimpleMeterRegistry());//綁定的MeterRegistry

? ? ? ? ? ? counter.increment();

? ? ? ? }

}

# FunctionCounter

FunctionCounter是Counter的特化類型莲祸,它把計數(shù)器數(shù)值增加的動作抽象成接口類型ToDoubleFunction蹂安,這個接口JDK1.8中對于Function的特化類型接口。

FunctionCounter的使用場景和Counter是一致的锐帜,這里介紹一下它的用法:

public class FunctionCounterMain {

? ? ? ? public static void main(String[] args) throws Exception {

? ? ? ? ? ? MeterRegistry registry = new SimpleMeterRegistry();

? ? ? ? ? ? AtomicInteger n = new AtomicInteger(0);

? ? ? ? ? ? //這里ToDoubleFunction匿名實現(xiàn)其實可以使用Lambda表達式簡化為AtomicInteger::get

? ? ? ? ? ? FunctionCounter.builder("functionCounter", n, new ToDoubleFunction<AtomicInteger>() {

? ? ? ? ? ? ? ? @Override

? ? ? ? ? ? ? ? public double applyAsDouble(AtomicInteger value) {

? ? ? ? ? ? ? ? ? ? return value.get();

? ? ? ? ? ? ? ? }

? ? ? ? ? ? }).baseUnit("function")

? ? ? ? ? ? ? ? ? ? .description("functionCounter")

? ? ? ? ? ? ? ? ? ? .tag("createOrder", "CHANNEL-A")

? ? ? ? ? ? ? ? ? ? .register(registry);

? ? ? ? ? ? //下面模擬三次計數(shù)? ? ? ?

? ? ? ? ? ? n.incrementAndGet();

? ? ? ? ? ? n.incrementAndGet();

? ? ? ? ? ? n.incrementAndGet();

? ? ? ? }

}

FunctionCounter使用的一個明顯的好處是田盈,我們不需要感知FunctionCounter實例的存在,實際上我們只需要操作作為FunctionCounter實例構(gòu)建元素之一的AtomicInteger實例即可缴阎,這種接口的設(shè)計方式在很多框架里面都可以看到允瞧。

# Timer

Timer(計時器)適用于記錄耗時比較短的事件的執(zhí)行時間,通過時間分布展示事件的序列和發(fā)生頻率蛮拔。所有的Timer的實現(xiàn)至少記錄了發(fā)生的事件的數(shù)量和這些事件的總耗時述暂,從而生成一個時間序列。

Timer的基本單位基于服務(wù)端的指標(biāo)而定建炫,但是實際上我們不需要過于關(guān)注Timer的基本單位畦韭,因為Micrometer在存儲生成的時間序列的時候會自動選擇適當(dāng)?shù)幕締挝弧imer接口提供的常用方法如下:

public interface Timer extends Meter {

? ? ...

? ? void record(long var1, TimeUnit var3);

? ? default void record(Duration duration) {

? ? ? ? this.record(duration.toNanos(), TimeUnit.NANOSECONDS);

? ? }

? ? <T> T record(Supplier<T> var1);

? ? <T> T recordCallable(Callable<T> var1) throws Exception;

? ? void record(Runnable var1);

? ? default Runnable wrap(Runnable f) {

? ? ? ? return () -> {

? ? ? ? ? ? this.record(f);

? ? ? ? };

? ? }

? ? default <T> Callable<T> wrap(Callable<T> f) {

? ? ? ? return () -> {

? ? ? ? ? ? return this.recordCallable(f);

? ? ? ? };

? ? }

? ? long count();

? ? double totalTime(TimeUnit var1);

? ? default double mean(TimeUnit unit) {

? ? ? ? return this.count() == 0L ? 0.0D : this.totalTime(unit) / (double)this.count();

? ? }

? ? double max(TimeUnit var1);

? ? ...

}

實際上肛跌,比較常用和方便的方法是幾個函數(shù)式接口入?yún)⒌姆椒ǎ?/p>

Timer timer = ...

timer.record(() -> dontCareAboutReturnValue());

timer.recordCallable(() -> returnValue());

Runnable r = timer.wrap(() -> dontCareAboutReturnValue());

Callable c = timer.wrap(() -> returnValue());

使用場景:

根據(jù)個人經(jīng)驗和實踐艺配,總結(jié)如下:

記錄指定方法的執(zhí)行時間用于展示据过。

記錄一些任務(wù)的執(zhí)行時間,從而確定某些數(shù)據(jù)來源的速率妒挎,例如消息隊列消息的消費速率等绳锅。

這里舉個實際的例子,要對系統(tǒng)做一個功能酝掩,記錄指定方法的執(zhí)行時間鳞芙,還是用下單方法做例子:

public class TimerMain {

? ? ? ? private static final Random R = new Random();

? ? ? ? static {

? ? ? ? ? ? Metrics.addRegistry(new SimpleMeterRegistry());

? ? ? ? }

? ? ? ? public static void main(String[] args) throws Exception {

? ? ? ? ? ? Order order1 = new Order();

? ? ? ? ? ? order1.setOrderId("ORDER_ID_1");

? ? ? ? ? ? order1.setAmount(100);

? ? ? ? ? ? order1.setChannel("CHANNEL_A");

? ? ? ? ? ? order1.setCreateTime(LocalDateTime.now());

? ? ? ? ? ? Timer timer = Metrics.timer("timer", "createOrder", "cost");

? ? ? ? ? ? timer.record(() -> createOrder(order1));

? ? ? ? }

? ? ? ? private static void createOrder(Order order) {

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? TimeUnit.SECONDS.sleep(R.nextInt(5)); //模擬方法耗時

? ? ? ? ? ? } catch (InterruptedException e) {

? ? ? ? ? ? ? ? //no-op

? ? ? ? ? ? }

? ? ? ? }

}

在實際生產(chǎn)環(huán)境中,可以通過spring-aop把記錄方法耗時的邏輯抽象到一個切面中期虾,這樣就能減少不必要的冗余的模板代碼原朝。上面的例子是通過Mertics構(gòu)造Timer實例,實際上也可以使用Builder構(gòu)造:

MeterRegistry registry = ...

Timer timer = Timer

? ? .builder("my.timer")

? ? .description("a description of what this timer does") // 可選

? ? .tags("region", "test") // 可選

? ? .register(registry);

另外镶苞,Timer的使用還可以基于它的內(nèi)部類Timer.Sample喳坠,通過start和stop兩個方法記錄兩者之間的邏輯的執(zhí)行耗時。例如:

Timer.Sample sample = Timer.start(registry);

// 這里做業(yè)務(wù)邏輯

Response response = ...

sample.stop(registry.timer("my.timer", "response", response.status()));

# FunctionTimer

FunctionTimer是Timer的特化類型茂蚓,它主要提供兩個單調(diào)遞增的函數(shù)(其實并不是單調(diào)遞增壕鹉,只是在使用中一般需要隨著時間最少保持不變或者說不減少):一個用于計數(shù)的函數(shù)和一個用于記錄總調(diào)用耗時的函數(shù),它的建造器的入?yún)⑷缦拢?/p>

public interface FunctionTimer extends Meter {

? ? static <T> Builder<T> builder(String name, T obj, ToLongFunction<T> countFunction,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ToDoubleFunction<T> totalTimeFunction,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? TimeUnit totalTimeFunctionUnit) {

? ? ? ? return new Builder<>(name, obj, countFunction, totalTimeFunction, totalTimeFunctionUnit);

? ? }

? ? ...

}

官方文檔中的例子如下:

IMap<?, ?> cache = ...; // 假設(shè)使用了Hazelcast緩存

registry.more().timer("cache.gets.latency", Tags.of("name", cache.getName()), cache,

? ? c -> c.getLocalMapStats().getGetOperationCount(),? //實際上就是cache的一個方法聋涨,記錄緩存生命周期初始化的增量(個數(shù))

? ? c -> c.getLocalMapStats().getTotalGetLatency(),? // Get操作的延遲時間總量晾浴,可以理解為耗時

? ? TimeUnit.NANOSECONDS

);

按照個人理解,ToDoubleFunction用于統(tǒng)計事件個數(shù)牍白,ToDoubleFunction用于記錄執(zhí)行總時間脊凰,實際上兩個函數(shù)都只是Function函數(shù)的變體,還有一個比較重要的是總時間的單位totalTimeFunctionUnit茂腥。簡單的使用方式如下:

public class FunctionTimerMain {

? ? ? ? public static void main(String[] args) throws Exception {

? ? ? ? ? ? //這個是為了滿足參數(shù),暫時不需要理會

? ? ? ? ? ? Object holder = new Object();

? ? ? ? ? ? AtomicLong totalTimeNanos = new AtomicLong(0);

? ? ? ? ? ? AtomicLong totalCount = new AtomicLong(0);

? ? ? ? ? ? FunctionTimer.builder("functionTimer", holder, p -> totalCount.get(),

? ? ? ? ? ? ? ? ? ? p -> totalTimeNanos.get(), TimeUnit.NANOSECONDS)

? ? ? ? ? ? ? ? ? ? .register(new SimpleMeterRegistry());

? ? ? ? ? ? totalTimeNanos.addAndGet(10000000);

? ? ? ? ? ? totalCount.incrementAndGet();

? ? ? ? }

}

# LongTaskTimer

LongTaskTimer也是一種Timer的特化類型狸涌,主要用于記錄長時間執(zhí)行的任務(wù)的持續(xù)時間,在任務(wù)完成之前最岗,被監(jiān)測的事件或者任務(wù)仍然處于運行狀態(tài)帕胆,任務(wù)完成的時候,任務(wù)執(zhí)行的總耗時才會被記錄下來仑性。

LongTaskTimer適合用于長時間持續(xù)運行的事件耗時的記錄惶楼,例如相對耗時的定時任務(wù)。在Spring應(yīng)用中诊杆,可以簡單地使用@Scheduled和@Timed注解,基于spring-aop完成定時調(diào)度任務(wù)的總耗時記錄:

@Timed(value = "aws.scrape", longTask = true)

@Scheduled(fixedDelay = 360000)

void scrapeResources() {

? ? //這里做相對耗時的業(yè)務(wù)邏輯

}

當(dāng)然何陆,在非spring體系中也能方便地使用LongTaskTimer:

public class LongTaskTimerMain {

? ? ? ? public static void main(String[] args) throws Exception{

? ? ? ? ? ? MeterRegistry meterRegistry = new SimpleMeterRegistry();

? ? ? ? ? ? LongTaskTimer longTaskTimer = meterRegistry.more().longTaskTimer("longTaskTimer");

? ? ? ? ? ? longTaskTimer.record(() -> {

? ? ? ? ? ? ? ? //這里編寫Task的邏輯

? ? ? ? ? ? });

? ? ? ? ? ? //或者這樣

? ? ? ? ? ? Metrics.more().longTaskTimer("longTaskTimer").record(()-> {

? ? ? ? ? ? ? ? //這里編寫Task的邏輯

? ? ? ? ? ? });

? ? ? ? }

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末晨汹,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子贷盲,更是在濱河造成了極大的恐慌淘这,老刑警劉巖剥扣,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異铝穷,居然都是意外死亡钠怯,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門曙聂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來晦炊,“玉大人,你說我怎么就攤上這事宁脊《瞎” “怎么了?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵榆苞,是天一觀的道長稳衬。 經(jīng)常有香客問我,道長坐漏,這世上最難降的妖魔是什么薄疚? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮赊琳,結(jié)果婚禮上输涕,老公的妹妹穿的比我還像新娘。我一直安慰自己慨畸,他們只是感情好莱坎,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著寸士,像睡著了一般檐什。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上弱卡,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天乃正,我揣著相機與錄音,去河邊找鬼婶博。 笑死瓮具,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的凡人。 我是一名探鬼主播名党,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼挠轴!你這毒婦竟也來了传睹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤岸晦,失蹤者是張志新(化名)和其女友劉穎欧啤,沒想到半個月后睛藻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡邢隧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年店印,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片倒慧。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡按摘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出迫靖,到底是詐尸還是另有隱情院峡,我是刑警寧澤,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布系宜,位于F島的核電站照激,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏盹牧。R本人自食惡果不足惜俩垃,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望汰寓。 院中可真熱鬧口柳,春花似錦、人聲如沸有滑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽毛好。三九已至望艺,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間肌访,已是汗流浹背找默。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留吼驶,地道東北人惩激。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像蟹演,于是被迫代替她去往敵國和親风钻。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

推薦閱讀更多精彩內(nèi)容