源碼角度了解Skywalking之Skywalking是如何進行JVM監(jiān)控的
大家都知道Skywalking可以監(jiān)控Java的JVM情況,包括垃圾回收情況等等,那么它是怎么實現(xiàn)的呢?今天就帶大家一探究竟。
通過前幾篇的文章我們知道,Skywalking啟動的時候,會加載各種BootService實現(xiàn)類,而有關(guān)JVM的BootService實現(xiàn)類就是JVMService
JVMService
JVMService可以看做一個定時器,它收集JVM cpu、內(nèi)存、內(nèi)存池和gc 信息等等參數(shù),并將收集到的信息通過GRPCChannelManager提供的通道發(fā)送給Collector,GRPCChannelManager這個類我們在上篇文章我們就進行了介紹,主要是用來建立連接管理通道的
JVMService實現(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ù)制代碼
準備階段就是創(chuàng)建一個LinkedBlockingQueue類型的阻塞隊列,隊列大小為600,這個隊列保存的是JVMMetric對象,然后創(chuàng)建了一個Sender對象,找到GRPCChannelManager對象,并把Sender對象加入監(jiān)聽類中。
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()方法中定義兩個定時線程池每隔一秒創(chuàng)建一個線程,一個是生產(chǎn)線程,一個是消費線程,生產(chǎn)者的邏輯對應(yīng)JVMService的run()方法,而消費者的邏輯在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對象,創(chuàng)建完成后放入隊列中,從準備方法中我們知道隊列大小設(shè)置是600,當隊列滿的時候,會取出最久的那個淘汰掉,然后放入新的JVMMetric對象到隊尾。
從組裝JVMMetric對象的過程我們可以看到,JVMMetric對象中包含了當前時間,CPU指標、內(nèi)存指標還有垃圾回收指標。這三個指標的獲取都是 利用枚舉實現(xiàn)的單例模式 ,然后getGCList()方法中調(diào)用了GCModule抽象類的getGCList()方法,在獲取新生代和老年代垃圾回收器名稱的時候定義了抽象方法,具體邏輯每個垃圾回收器重寫各自的抽象方法, 這是模板方法的體現(xiàn)
Sender
run()方法:
Sender對象作為消費者,它的功能就是通過drainTo()方法來把隊列中的數(shù)據(jù)轉(zhuǎn)移到buffer集合中,然后發(fā)送到Collector中。
總結(jié)
這篇文章我們講了Skywalking是怎么進行JVM參數(shù)收集的,主要涉及到的類是JVMService,它的類定義了一個LinkedBlockingQueue類型的阻塞隊列,長度是600,然后啟動方法中啟動了兩個定時線程池創(chuàng)建了生產(chǎn)者JVMService線程和消費者Sender線程,JVMService作為生產(chǎn)者收集CPU、內(nèi)存、垃圾回收信息等放入隊列,Sender作為消費者從隊列獲取到所有JVM信息發(fā)送給Collector
:heart: 感謝大家
如果你覺得這篇內(nèi)容對你挺有有幫助的話幫忙點點關(guān)注!