1. 監控中心啓動,咱們先看下dubbo的屬性文件css
dubbo.container=log4j,spring,registry,jettyhtml
dubbo.application.name=simple-monitorjava
dubbo.application.owner=web
dubbo.registry.address=zookeeper://127.0.0.1:2181spring
dubbo.protocol.port=7070瀏覽器
dubbo.jetty.port=8080併發
dubbo.jetty.directory=${user.home}/monitormvc
dubbo.charts.directory=${dubbo.jetty.directory}/chartsapp
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查詢主機,應用,接口,方法的匹配條件,缺失的條件的表示所有,host用0.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採集到數據後經過任務定時調用監控中心服務將數據提交到監控中心。
監控中心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,服務提供方或者消費方的系統相關信息。 同時提供了一系列方法供註冊中心調用查詢。
監控中心將採集到的信息經過內置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)
//有註解 name跟desc屬性都是在頁面中展現給用戶看的
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大概提供了哪些擴展
下面截幾張圖看看監控中心的頁面。