源碼角度了解Skywalking之Skywalking是如何進(jìn)行JVM監(jiān)控的
大家都知道Skywalking可以監(jiān)控Java的JVM情況,包括垃圾回收情況等等,那么它是怎么實(shí)現(xiàn)的呢?今天就帶大家一探究竟。
通過(guò)前幾篇的文章我們知道,Skywalking啟動(dòng)的時(shí)候,會(huì)加載各種BootService實(shí)現(xiàn)類(lèi),而有關(guān)JVM的BootService實(shí)現(xiàn)類(lèi)就是JVMService
JVMService
JVMService可以看做一個(gè)定時(shí)器,它收集JVM cpu、內(nèi)存、內(nèi)存池和gc 信息等等參數(shù),并將收集到的信息通過(guò)GRPCChannelManager提供的通道發(fā)送給Collector,GRPCChannelManager這個(gè)類(lèi)我們?cè)谏掀恼挛覀兙瓦M(jìn)行了介紹,主要是用來(lái)建立連接管理通道的
JVMService實(shí)現(xiàn)BootService接口和Runnable接口
我們按照調(diào)用方法的順序分析一下吧
prepare()方法
JVMService的prepare()方法:
public static int BUFFER_SIZE = 60 * 10;public void prepare() throws Throwable { queue = new LinkedBlockingQueue(Config.Jvm.BUFFER_SIZE); sender = new Sender(); ServiceManager.INSTANCE.findService(GRPCChannelManager.class).addChannelListener(sender); }復(fù)制代碼
準(zhǔn)備階段就是創(chuàng)建一個(gè)LinkedBlockingQueue類(lèi)型的阻塞隊(duì)列,隊(duì)列大小為600,這個(gè)隊(duì)列保存的是JVMMetric對(duì)象,然后創(chuàng)建了一個(gè)Sender對(duì)象,找到GRPCChannelManager對(duì)象,并把Sender對(duì)象加入監(jiān)聽(tīng)類(lèi)中。
boot()方法
JVMService的boot()方法:
public void boot() throws Throwable { collectMetricFuture = Executors .newSingleThreadScheduledExecutor(new DefaultNamedThreadFactory(“JVMService-produce”)) .scheduleAtFixedRate(new RunnableWithExceptionProtection(this, new RunnableWithExceptionProtection.CallbackWhenException() { @Override public void handle(Throwable t) { logger.error(“JVMService produces metrics failure.”, t); } }), 0, 1, TimeUnit.SECONDS); sendMetricFuture = Executors .newSingleThreadScheduledExecutor(new DefaultNamedThreadFactory(“JVMService-consume”)) .scheduleAtFixedRate(new RunnableWithExceptionProtection(sender, new RunnableWithExceptionProtection.CallbackWhenException() { @Override public void handle(Throwable t) { logger.error(“JVMService consumes and upload failure.”, t); } } ), 0, 1, TimeUnit.SECONDS); }復(fù)制代碼
boot()方法中定義兩個(gè)定時(shí)線程池每隔一秒創(chuàng)建一個(gè)線程,一個(gè)是生產(chǎn)線程,一個(gè)是消費(fèi)線程,生產(chǎn)者的邏輯對(duì)應(yīng)JVMService的run()方法,而消費(fèi)者的邏輯在Sender的run()方法中
run()方法
JVMService的run()方法:
public void run() { if (RemoteDownstreamConfig.Agent.SERVICE_ID != DictionaryUtil.nullValue() && RemoteDownstreamConfig.Agent.SERVICE_INSTANCE_ID != DictionaryUtil.nullValue() ) { long currentTimeMillis = System.currentTimeMillis(); try { JVMMetric.Builder jvmBuilder = JVMMetric.newBuilder(); jvmBuilder.setTime(currentTimeMillis); jvmBuilder.setCpu(CPUProvider.INSTANCE.getCpuMetric()); jvmBuilder.addAllMemory(MemoryProvider.INSTANCE.getMemoryMetricList()); jvmBuilder.addAllMemoryPool(MemoryPoolProvider.INSTANCE.getMemoryPoolMetricsList()); jvmBuilder.addAllGc(GCProvider.INSTANCE.getGCList()); JVMMetric jvmMetric = jvmBuilder.build(); if (!queue.offer(jvmMetric)) { queue.poll(); queue.offer(jvmMetric); } } catch (Exception e) { logger.error(e, “Collect JVM info fail.”); } } }復(fù)制代碼
run()中的邏輯主要是創(chuàng)建JVMMetric對(duì)象,創(chuàng)建完成后放入隊(duì)列中,從準(zhǔn)備方法中我們知道隊(duì)列大小設(shè)置是600,當(dāng)隊(duì)列滿的時(shí)候,會(huì)取出最久的那個(gè)淘汰掉,然后放入新的JVMMetric對(duì)象到隊(duì)尾。
從組裝JVMMetric對(duì)象的過(guò)程我們可以看到,JVMMetric對(duì)象中包含了當(dāng)前時(shí)間,CPU指標(biāo)、內(nèi)存指標(biāo)還有垃圾回收指標(biāo)。這三個(gè)指標(biāo)的獲取都是 利用枚舉實(shí)現(xiàn)的單例模式 ,然后getGCList()方法中調(diào)用了GCModule抽象類(lèi)的getGCList()方法,在獲取新生代和老年代垃圾回收器名稱(chēng)的時(shí)候定義了抽象方法,具體邏輯每個(gè)垃圾回收器重寫(xiě)各自的抽象方法, 這是模板方法的體現(xiàn)
Sender
run()方法:
Sender對(duì)象作為消費(fèi)者,它的功能就是通過(guò)drainTo()方法來(lái)把隊(duì)列中的數(shù)據(jù)轉(zhuǎn)移到buffer集合中,然后發(fā)送到Collector中。
總結(jié)
這篇文章我們講了Skywalking是怎么進(jìn)行JVM參數(shù)收集的,主要涉及到的類(lèi)是JVMService,它的類(lèi)定義了一個(gè)LinkedBlockingQueue類(lèi)型的阻塞隊(duì)列,長(zhǎng)度是600,然后啟動(dòng)方法中啟動(dòng)了兩個(gè)定時(shí)線程池創(chuàng)建了生產(chǎn)者JVMService線程和消費(fèi)者Sender線程,JVMService作為生產(chǎn)者收集CPU、內(nèi)存、垃圾回收信息等放入隊(duì)列,Sender作為消費(fèi)者從隊(duì)列獲取到所有JVM信息發(fā)送給Collector
:heart: 感謝大家
如果你覺(jué)得這篇內(nèi)容對(duì)你挺有有幫助的話幫忙點(diǎn)點(diǎn)關(guān)注!