26. Dubbo原理解析-監控

Dubbo發佈代碼中,自帶了一個簡易的監控中心實現。對於一般的小業務這個監控中心應該能夠滿足需求,對於那些大業務量的大公司一般都會有自己的監控中心,更加豐富的功能如常用的報警短信通知等等。這章講解分析使得讀者能夠了解一般的監控中心實現,也使得有自己接入監控中心需求的大概知道如何集成自己的監控中心實現。下面我們就以dubbo自帶的監控中心開始講解。

 

監控中心

1.  監控中心啓動,我們先看下dubbo的屬性文件

dubbo.container=log4j,spring,registry,jetty

dubbo.application.name=simple-monitor

dubbo.application.owner=

dubbo.registry.address=zookeeper://127.0.0.1:2181

dubbo.protocol.port=7070

dubbo.jetty.port=8080

dubbo.jetty.directory=${user.home}/monitor

dubbo.charts.directory=${dubbo.jetty.directory}/charts

dubbo.statistics.directory=${user.home}/monitor/statistics

 

相比於provider, consumer的啓動註冊中心多了registry, jetty容器啓動


它們都是基於dubbo的spi擴展機制的。

SpringContainer容器啓動就是加載classpath*:META-INF/spring/ *.xml spring的配置文件

<beanid="monitorService"class="com.alibaba.dubbo.monitor.simple.SimpleMonitorService">

       <propertyname="statisticsDirectory"value="${dubbo.statistics.directory}"/>

       <propertyname="chartsDirectory"value="${dubbo.charts.directory}"/>

</bean>

<dubbo:applicationname="${dubbo.application.name}"owner="${dubbo.application.owner}"/>

<dubbo:registryaddress="${dubbo.registry.address}"/>

<dubbo:protocolname="dubbo"port="${dubbo.protocol.port}"/>

<dubbo:serviceinterface="com.alibaba.dubbo.monitor.MonitorService"ref="monitorService"delay="-1"/>

<dubbo:referenceid="registryService"interface="com.alibaba.dubbo.registry.RegistryService"/>

 

2. SimpleMonitorService

監控中心配置了監控服務的實現SimpleMonitorService, 並且作爲一個普通的dubbo服務暴露到註冊中心,供服務的提供者和服務的消費方調用,將服務提供者和服務的消費方的調用數據保存到監控中心。

監控服務的接口定義

public interface MonitorService {

    /**

     * 監控數據採集.

     * 1. 支持調用次數統計:count://host/interface?application=foo&method=foo&provider=10.20.153.11:20880&success=12&failure=2&elapsed=135423423

     * 1.1host,application,interface,group,version,method記錄監控來源主機,應用,接口,方法信息。

     * 1.2 如果是消費者發送的數據,加上provider地址參數,反之,加上來源consumer地址參數。

     * 1.3 success,faulure,elapsed 記錄距上次採集,調用的成功次數,失敗次數,成功調用總耗時,平均時間將用總耗時除以成功次數。

     *

     * @paramstatistics

     */

    void collect(URLstatistics);

 

    /**

     * 監控數據查詢

     * 1. 支持按天查詢:count://host/interface?application=foo&method=foo&side=provider&view=chart&date=2012-07-03

     * 1.1host,application,interface,group,version,method查詢主機,應用,接口,方法的匹配條件,缺失的條件的表示全部,host0.0.0.0表示全部。

     * 1.2 side=consumer,provider 查詢由調用的哪一端採集的數據,缺省爲都查詢。

     * 1.3 缺省爲view=summary,返回全天彙總信息,支持view=chart表示返回全天趨勢圖表圖片的URL地址,可以進接嵌入其它系統的頁面上展示。

     * 1.4 date=2012-07-03指定查詢數據的日期,缺省爲當天。

     *

     * @param query

     * @returnstatistics

     */

    List<URL> lookup(URL query);

}

 

注: lookup方面可能在開源過程中依賴了阿里的什麼系統,並沒有具體的實現,如果使用着需要此功能則需要根據接口定義自己實現

 

MonitorService的dubbo默認實現SimpleMonitorService

Collect方法被遠程調用後將數據url(傳過來的url包含監控需要的數據)保存到一個阻塞隊列中BlockingQueue<URL>

啓動定時任務將統計日誌記錄到本地,

String filename =${user.home}/monitor/statistics

                        + "/" + day

                        + "/" +statistics.getServiceInterface()

                        + "/" +statistics.getParameter(METHOD)

                        + "/" + consumer

                        + "/" + provider

                        + "/" + type + "." + key

這是文件在本地存儲的格式


文件內容如圖保存時間方法消費耗時


 

3. 起定時任務利用JFreeeChart繪製圖表,保存路徑

${user.home}\monitor\charts\date\interfaceName\methodName

 

 

 

產生監控數據

註冊中心暴露了MonitorService服務,它是被誰調用的呢,監控中心的數據是從哪裏來呢,下面我們看下服務提供方與服務的消費方式如何介入監控中心的。

在服務的提供方跟消費方的dubbo配置加入如下配置

通過註冊中心<dubbo:monitor protocol="registry" />

或者直連  <dubbo:monitor address="127.0.0.1:7070" />


在構建服務的調用鏈的時候有如上基於監控的擴展,下面我們就來看下這個類

@Activate(group = {Constants.PROVIDER, Constants.CONSUMER})

//此過濾器在服務的提供方,服務的消費方應用中被激活,也就是起作用

public class MonitorFilter implements Filter {

private MonitorFactory monitorFactory;

    public Result invoke(Invoker<?>invoker, Invocation invocation) throws RpcException {

    if(invoker.getUrl().hasParameter(Constants.MONITOR_KEY)) {

         //有注監控中心處理

1.  獲取invoker的調用上下文

2.  記錄起始時間戳

3.  併發計數加一

try {

4.  調用調用鏈的下一步

5.  採集調用信息

} finally {

6.  併發計數減一

}

    } else {

         //沒有配置監控中心,直接往下調用

         return invoker.inovke(invocation);

   }

}

 

上面第5點信息採集

1. 計算調用耗時

2. 獲取併發數

3. 獲取服務名稱

4. 獲取方法名

5. 判斷是服務消費方監控還是服務提供方監控

6. 由工廠類monitorFactory.getMonitor(監控url),獲取DubboMonitor對象,

構建調用監控中心服務的的Url, url中包括了監控中心所需的監控信息

monitor.collect(newURL(Constants.COUNT_PROTOCOL,

                  NetUtils.getLocalHost(),localPort,

                  service + "/" +method,

                  MonitorService.APPLICATION, application,

                   MonitorService.INTERFACE,service,

                   MonitorService.METHOD,method,

                   remoteKey, remoteValue,

                   error ?MonitorService.FAILURE : MonitorService.SUCCESS, "1",

                   MonitorService.ELAPSED,String.valueOf(elapsed),

                   MonitorService.CONCURRENT,String.valueOf(concurrent),

                   Constants.INPUT_KEY, input,

                  Constants.OUTPUT_KEY, output));

 

DubboMonitor是調用監控中心的服務的封裝,之所以沒有直接調監控中心而是通過DubboMonitor調用,是因爲監控是附加功能,不應該影響主鏈路更不應該損害主鏈路的新能,DubboMonitor採集到數據後通過任務定時調用監控中心服務將數據提交到監控中心。

 

RegistryContainer

監控中心refer引用了註冊中心暴露的RegistryService服務,主要是被下面的RegistryContainer使用的。

 

RegistryContainer主要是到註冊中心收集服務,分組,版本信息,並註冊回調當註冊中心數據發生變化的時候更新到監控中心

下面看下RegistryContainer的start方法流程:

1. 通過SpringContainer獲取前面初始化的RegistryService, 得到其實是對註冊中心的一個遠程代理服務

2. 構建訂閱註冊中心數據的URL,看可以看出下面的url是訂閱服務提供者和服務消費者的所有服務

subscribeUrl = newURL(Constants.ADMIN_PROTOCOL, NetUtils.getLocalHost(), 0,"",

                Constants.INTERFACE_KEY,Constants.ANY_VALUE,//所有服務

                Constants.GROUP_KEY,Constants.ANY_VALUE,//所有分組

                Constants.VERSION_KEY, Constants.ANY_VALUE,//所有版本

                Constants.CLASSIFIER_KEY,Constants.ANY_VALUE,//所有分類

Constants.CATEGORY_KEY,Constants.PROVIDERS_CATEGORY + ","  + Constants.CONSUMERS_CATEGORY,//服務的提供者和服務的消費者

                 Constants.CHECK_KEY,String.valueOf(false));//不檢查

3.  調註冊中心服務registry.subscirbe(subscribeUrl,listener)訂閱所有數據, NotifyListener在監控中心暴露爲回調服務,由註冊中心回調

回調接口NotifyListener實現的功能主要是按服務提供者和服務的消費者分類,收集服務名稱,服務的url,服務提供方或者消費方的系統相關信息。 同時提供了一系列方法供註冊中心調用查詢。

 

JettyContainer

監控中心將採集到的信息通過內置jetty來展現給用戶,這裏爲了不依賴與jsp, velocity,freemarker等一些編寫web應用的技術,採用在servlet中將html,css,js打印出來

JettyContainer的start方法啓動了內置的jettyweb容器

將監控中心訪問的本地文件目錄設置到ResourceFilter中,並設置這個filter的訪問映射到jetty中   , ResourceFilter主要是讀取本地保存的JFreeChart繪製的圖片到瀏覽器中去。

將監控中心的前置控制器PageServlet, 以及這個servlet的訪問映射配置到jetty中。之所以叫PageServet爲前置控制器,就像其他的mvc框架一樣用來分發具體的業務類

 

PageServet的init初始化方法在web容器啓動的時候加載所有的頁面處理器PageHandler, 用來根據不同的請求生成不同的頁面,前面說過這裏頁面html都是通過java代碼打印出來的。

PageServet的init方法加載所有PageHandler時會判斷PageHandler上是否有@Menu註解,將有註解的PageHandler加入集合,以被HomePageHandl er用來生成主頁以及各個頁面的uri

PageServet的doGet, doPost接收瀏覽器請求,請求以xx.hml形式,xx就是PageHandler擴展的key,找到對應的PageHandler繪製對應的頁面返回給瀏覽器。

 

 

@Menu(name = "Home",desc = "Home page.", order = Integer.MIN_VALUE)

//有註解 namedesc屬性都是在頁面中展示給用戶看的

public class HomePageHandlerimplements PageHandler {

    public Page handle(URL url) {

        List<List<String>> rows =new ArrayList<List<String>>();

        for (PageHandler handler :PageServlet.getInstance().getMenus()) {

            String uri =ExtensionLoader.getExtensionLoader(PageHandler.class).getExtensionName(handler); //這個uri其實就是PageHandler擴展配置的key頁面中用它來請求選擇具體的handler繪製     //出具體的page

            Menu menu =handler.getClass().getAnnotation(Menu.class);

            List<String> row = newArrayList<String>();

            row.add("<ahref=\"" + uri + ".html\">" + menu.name() +"</a>");

            row.add(menu.desc());

            rows.add(row);

        }

        return new Page("Home","Menus",  new String[]{"Menu Name", "Menu Desc"}, rows);  //一個Page實體就是一個頁面,這裏包含所有主要HomePage的頁面內容

    }

}

 

PageHandler的在com.alibaba.dubbo.container.page.PageHandler文件中的擴展配置

index=com.alibaba.dubbo.container.page.pages.HomePageHandler

providers=com.alibaba.dubbo.monitor.simple.pages.ProvidersPageHandler

consumers=com.alibaba.dubbo.monitor.simple.pages.ConsumersPageHandler

。。。。

下面截圖看下dubbo大概提供了哪些擴展


 下面截幾張圖看看監控中心的頁面。





    

發佈了69 篇原創文章 · 獲贊 99 · 訪問量 46萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章