dubbo監控機制之監控中心實現分析

這裏的監控中心以dubbo-ops\dubbo-monitor-simple項目說
總的來講是監控中心啓動一個sevlet容器,經過web頁面向用戶多維度的展現dubbo服務信息。以下圖css

從頁面結構來講,圖中紅框框住的幾個部分,第一行是菜單,第二上是導航,第三行是表格title,最後一部分是表格。咱們先從服務服務啓動類開始html

public class MonitorStarter {
    public static void main(String[] args) {
    //經過main方法啓動
        System.setProperty(Constants.DUBBO_PROPERTIES_KEY, "conf/dubbo.properties");
         Main.main(args);
    }
}

再看下Main 的main方法java

public static void main(String[] args) {
        try {
            //經過dubbo.container 獲取要啓動的容器名,多個以逗號分割
            if (args == null || args.length == 0) {
                String config = ConfigUtils.getProperty(CONTAINER_KEY, loader.getDefaultExtensionName());
                args = Constants.COMMA_SPLIT_PATTERN.split(config);
            }
             //經過spi 加裝容器實例 放入list
            final List<Container> containers = new ArrayList<Container>();
            for (int i = 0; i < args.length; i++) {
                containers.add(loader.getExtension(args[i]));
            }
            logger.info("Use container type(" + Arrays.toString(args) + ") to run dubbo serivce.");
             //優雅停機的回調設置
            if ("true".equals(System.getProperty(SHUTDOWN_HOOK_KEY))) {
                Runtime.getRuntime().addShutdownHook(new Thread() {
                    public void run() {
                        for (Container container : containers) {
                            try {
                                container.stop();
                                logger.info("Dubbo " + container.getClass().getSimpleName() + " stopped!");
                            } catch (Throwable t) {
                                logger.error(t.getMessage(), t);
                            }
                            try {
                                LOCK.lock();//重入鎖
                                STOP.signal();//釋放信號量
                            } finally {
			        //釋放鎖
                                LOCK.unlock();
                            }
                        }
                    }
                });
            }
            //分別調用容器的start方法 啓動指定的容器。
            for (Container container : containers) {
                container.start();
                logger.info("Dubbo " + container.getClass().getSimpleName() + " started!");
            }
            System.out.println(new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]").format(new Date()) + " Dubbo service server started!");
        } catch (RuntimeException e) {
            e.printStackTrace();
            logger.error(e.getMessage(), e);
            System.exit(1);
        }
        try {
            LOCK.lock();//獲取鎖
            STOP.await();//等待信號量
        } catch (InterruptedException e) {
	      //不經過鉤子函數停機,記錄異常
            logger.warn("Dubbo service server stopped, interrupted by other thread!", e);
        } finally {
	//釋放鎖
            LOCK.unlock();
        }
    }

在dubbo.properties 文件中配置項web

dubbo.container=log4j,spring,registry,jetty

經過spi相關文件找到以下實現spring

spring=com.alibaba.dubbo.container.spring.SpringContainer
log4j=com.alibaba.dubbo.container.log4j.Log4jContainer
registry=com.alibaba.dubbo.monitor.simple.container.RegistryContainer
jetty=com.alibaba.dubbo.monitor.simple.container.JettyContainer

這裏重點解析下面spring,registry,jetty三種實現。具體從分析他們的start方法入手數據結構

SpringContainer

public void start() {
         //經過指定配置文件,啓動了一個spring容器
        String configPath = ConfigUtils.getProperty(SPRING_CONFIG);
        if (configPath == null || configPath.length() == 0) {
            configPath = DEFAULT_SPRING_CONFIG;
        }
        context = new ClassPathXmlApplicationContext(configPath.split("[,\\s]+"));
        context.start();
    }

看下spring配置app

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
	http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE"/>
        <property name="location" value="classpath:conf/dubbo.properties"/>
    </bean>

    <bean id="monitorService" class="com.alibaba.dubbo.monitor.simple.SimpleMonitorService">
    </bean>

    <dubbo:application name="${dubbo.application.name}" owner="${dubbo.application.owner}"/>
    <!--指定註冊中心地址-->
    <dubbo:registry client="curator" address="${dubbo.registry.address}"/>
    <!--服務發佈協議和端口-->
    <dubbo:protocol name="dubbo" port="${dubbo.protocol.port}"/>
    <!--發佈com.alibaba.dubbo.monitor.MonitorService服務 實現類是com.alibaba.dubbo.monitor.simple.SimpleMonitorService-->
     <!--這就是消費方和服務提供方在上報統計數據時用的服務實現-->
    <dubbo:service interface="com.alibaba.dubbo.monitor.MonitorService" ref="monitorService" delay="-1"/>
    <!--注入com.alibaba.dubbo.registry.RegistryService服務引用 這個服務實現有dubbo自身實現,不須要從註冊中心獲取-->
    <!--具體在RegistryProtocol的refer方法中作了特殊處理,源碼在下面-->
    <dubbo:reference id="registryService" interface="com.alibaba.dubbo.registry.RegistryService"/>

</beans>

RegistryProtocol的refer方法:ide

public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        //經過register 能夠獲取具體,註冊中心協議,這裏是zookeeper,並設置爲url 協議。
        url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
        //這裏會經過ZookeeperRegistryFactory的getRegistry方法實現,獲得zookeeper的Registry 實現ZookeeperRegistry類,
        //而Registry 接口繼承了RegistryService接口
        Registry registry = registryFactory.getRegistry(url);
        if (RegistryService.class.equals(type)) {
            //因此這裏直接把ZookeeperRegistry做爲RegistryService服務的實現,建立代理
            return proxyFactory.getInvoker((T) registry, type, url);
        }

        // group="a,b" or group="*"
        Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
        String group = qs.get(Constants.GROUP_KEY);
        if (group != null && group.length() > 0) {
            if ((Constants.COMMA_SPLIT_PATTERN.split(group)).length > 1
                    || "*".equals(group)) {
                return doRefer(getMergeableCluster(), registry, type, url);
            }
        }
        //這裏cluster是Cluster$Adpative類對象
        return doRefer(cluster, registry, type, url);
    }

這裏SpringContainer的主要完成了,MonitorService服務發佈,建立RegistryService服務代理實現。函數

RegistryContainer

public void start() {
         //驗證註冊中心地址
        String url = ConfigUtils.getProperty(REGISTRY_ADDRESS);
        if (url == null || url.length() == 0) {
            throw new IllegalArgumentException("Please set java start argument: -D" + REGISTRY_ADDRESS + "=zookeeper://127.0.0.1:2181");
        }
	//經過spring 容器獲取獲取registryService服務,因此須要spring容器先啓動
        registry = (RegistryService) SpringContainer.getContext().getBean("registryService");
        URL subscribeUrl = new URL(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));
	//經過registry去註冊中心訂閱,服務消費者,提供者信息
        //這裏 subscribeUrl = admin://10.47.16.51?category=providers,consumers&check=false&classifier=*&group=*&interface=*&version=*
        //做用是訂閱全部的服務提供和消費方節點。
        registry.subscribe(subscribeUrl, new NotifyListener() {
            //經過監聽器回調方法,把訂閱的服務信息系分類存儲到相應數據結構中
            //以備使用
            public void notify(List<URL> urls) {
                if (urls == null || urls.size() == 0) {
                    return;
                }
                Map<String, List<URL>> proivderMap = new HashMap<String, List<URL>>();
                Map<String, List<URL>> consumerMap = new HashMap<String, List<URL>>();
                for (URL url : urls) {
                    String application = url.getParameter(Constants.APPLICATION_KEY);
                    if (application != null && application.length() > 0) {
                        //應用統計
                        applications.add(application);
                    }
                    String service = url.getServiceInterface();
                    //服務統計
                    services.add(service);
                    //獲取url的類別信息,默認分類是 providers
                    String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
                    if (Constants.PROVIDERS_CATEGORY.equals(category)) {//服務提供者信息
                        if (Constants.EMPTY_PROTOCOL.equals(url.getProtocol())) {
                            serviceProviders.remove(service);
                        } else {
                            List<URL> list = proivderMap.get(service);
                            if (list == null) {
                                list = new ArrayList<URL>();
                                proivderMap.put(service, list);
                            }
                            list.add(url);
                            if (application != null && application.length() > 0) {
                                //獲取提供服務的應用集合
                                Set<String> serviceApplications = providerServiceApplications.get(service);
                                if (serviceApplications == null) {
                                    providerServiceApplications.put(service, new ConcurrentHashSet<String>());
                                    serviceApplications = providerServiceApplications.get(service);
                                }
                                serviceApplications.add(application);
                                 //應用提供的服務集合
                                Set<String> applicationServices = providerApplicationServices.get(application);
                                if (applicationServices == null) {
                                    providerApplicationServices.put(application, new ConcurrentHashSet<String>());
                                    applicationServices = providerApplicationServices.get(application);
                                }
                                applicationServices.add(service);
                            }
                        }
                    } else if (Constants.CONSUMERS_CATEGORY.equals(category)) {//消費者列表
                        if (Constants.EMPTY_PROTOCOL.equals(url.getProtocol())) {
                            serviceConsumers.remove(service);
                        } else {
                            List<URL> list = consumerMap.get(service);
                            if (list == null) {
                                list = new ArrayList<URL>();
                                consumerMap.put(service, list);
                            }
                            list.add(url);
                            if (application != null && application.length() > 0) {
                                Set<String> serviceApplications = consumerServiceApplications.get(service);
                                if (serviceApplications == null) {
				   //消費者的應用列表
                                    consumerServiceApplications.put(service, new ConcurrentHashSet<String>());
                                    serviceApplications = consumerServiceApplications.get(service);
                                }
                                serviceApplications.add(application);

                                Set<String> applicationServices = consumerApplicationServices.get(application);
                                if (applicationServices == null) {
                                    consumerApplicationServices.put(application, new ConcurrentHashSet<String>());
                                    applicationServices = consumerApplicationServices.get(application);
                                }
                                applicationServices.add(service);
                            }

                        }
                    }
                }
                if (proivderMap != null && proivderMap.size() > 0) {
		     //全部的服務提供者
                    serviceProviders.putAll(proivderMap);
                }
                if (consumerMap != null && consumerMap.size() > 0) {
		    //全部消費者信息
                    serviceConsumers.putAll(consumerMap);
                }
            }
        });
    }

RegistryContainer主要完成了從註冊中心對服務信息的收集,並提供了相關方法對收集到的數據進行使用,例如工具

//獲取全部的應用
    public Set<String> getApplications() {
        return Collections.unmodifiableSet(applications);
    }

    /***
     * reverse:true 獲取某個應用,全部服務的消費者
     * false:某個應用,全部服務提供者
     * @param application
     * @param reverse
     * @return
     */
    public Set<String> getDependencies(String application, boolean reverse) {
        if (reverse) {
            Set<String> dependencies = new HashSet<String>();
            //應用的全部的服務
            Set<String> services = providerApplicationServices.get(application);
            if (services != null && services.size() > 0) {
                for (String service : services) {
                    //全部服務消費者
                    Set<String> applications = consumerServiceApplications.get(service);
                    if (applications != null && applications.size() > 0) {
                        dependencies.addAll(applications);
                    }
                }
            }
            return dependencies;
        } else {
            Set<String> dependencies = new HashSet<String>();
            Set<String> services = consumerApplicationServices.get(application);
            if (services != null && services.size() > 0) {
                for (String service : services) {
                    //全部服務提供者
                    Set<String> applications = providerServiceApplications.get(service);
                    if (applications != null && applications.size() > 0) {
                        dependencies.addAll(applications);
                    }
                }
            }
            return dependencies;
        }
    }
   //獲取全部的服務
    public Set<String> getServices() {
        return Collections.unmodifiableSet(services);
    }

   
   //獲取某個應用全部提供全部的服務urls
    public List<URL> getProvidersByApplication(String application) {
        List<URL> urls = new ArrayList<URL>();
        if (application != null && application.length() > 0) {
            for (List<URL> providers : serviceProviders.values()) {
                for (URL url : providers) {
                    if (application.equals(url.getParameter(Constants.APPLICATION_KEY))) {
                        urls.add(url);
                    }
                }
            }
        }
        return urls;
    }

加上這篇(http://www.javashuo.com/article/p-tfzzkevu-kp.html)博文提到的上報信息,可知監控中心的監控信息大體可分爲兩類,一類是服務消費者和提供者經過MonitorService服務上報來的,服務調用動態信息,還有一類是監控中心經過訂閱註冊中心獲取的服務分佈靜態信息。

JettyContainer

public void start() {
        //jetty 服務端口
        String serverPort = ConfigUtils.getProperty(JETTY_PORT);
        int port;
        if (serverPort == null || serverPort.length() == 0) {
            //默認端口8080
            port = DEFAULT_JETTY_PORT;
        } else {
            port = Integer.parseInt(serverPort);
        }
        //初始化鏈接器
        connector = new SelectChannelConnector();
        connector.setPort(port);
        ServletHandler handler = new ServletHandler();

        String resources = ConfigUtils.getProperty(JETTY_DIRECTORY);
        if (resources != null && resources.length() > 0) {
            //添加過濾器,具體過濾邏輯看ResourceFilter
            //指定過濾器,過濾器匹配的路徑/*
            FilterHolder resourceHolder = handler.addFilterWithMapping(ResourceFilter.class, "/*", Handler.DEFAULT);
            //設置初始參數值
            resourceHolder.setInitParameter("resources", resources);
        }
        //添加servlet處理,處理邏輯能夠看PageServlet和匹配路徑
        ServletHolder pageHolder = handler.addServletWithMapping(PageServlet.class, "/*");
        //設置初始參數值
        pageHolder.setInitParameter("pages", ConfigUtils.getProperty(JETTY_PAGES));
        pageHolder.setInitOrder(2);

        Server server = new Server();
        server.addConnector(connector);
        //添加處理請求的handler
        server.addHandler(handler);
        try {
            //在指定端口啓動sevlet容器服務,接受用戶請求
            server.start();
        } catch (Exception e) {
            throw new IllegalStateException("Failed to start jetty server on " + NetUtils.getLocalHost() + ":" + port + ", cause: " + e.getMessage(), e);
        }
    }

ResourceFilter

//初始化資源路徑
     public void init(FilterConfig filterConfig) throws ServletException {
        //根據初始化參數,獲取資源路徑,支持多個資源路徑 放入resources
        String config = filterConfig.getInitParameter("resources");
        if (config != null && config.length() > 0) {
            String[] configs = Constants.COMMA_SPLIT_PATTERN.split(config);
            for (String c : configs) {
                if (c != null && c.length() > 0) {
                    c = c.replace('\\', '/');
                    if (c.endsWith("/")) {
                        c = c.substring(0, c.length() - 1);
                    }
                    resources.add(c);
                }
            }
        }
    }
//過濾邏輯
      public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        if (response.isCommitted()) {
            return;
        }
        String uri = request.getRequestURI();
        String context = request.getContextPath();
        if (uri.endsWith("/favicon.ico")) {
            uri = "/favicon.ico";
        } else if (context != null && !"/".equals(context)) {
            uri = uri.substring(context.length());
        }
        if (!uri.startsWith("/")) {
            uri = "/" + uri;
        }
        //獲取資源的默認修改時間
        long lastModified = getLastModified(uri);
        long since = request.getDateHeader("If-Modified-Since");
        if (since >= lastModified) {
            response.sendError(HttpServletResponse.SC_NOT_MODIFIED);
            return;
        }
        byte[] data;
        //經過url沒有制定資源,就走servlet邏輯
        InputStream input = getInputStream(uri);
        if (input == null) {
            //沒有獲取到資源,經過過濾器往下走,到servlet層(***看這裏**)
            chain.doFilter(req, res);
            return;
        }
        //能獲取具體資源,直接返回資源
        try {
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            byte[] buffer = new byte[8192];
            int n = 0;
            while (-1 != (n = input.read(buffer))) {
                output.write(buffer, 0, n);
            }
            data = output.toByteArray();
        } finally {
            input.close();
        }
        //設置modified 時間
        response.setDateHeader("Last-Modified", lastModified);
        OutputStream output = response.getOutputStream();
        output.write(data);
        output.flush();
    }

PageServlet

//初始化
 public void init() throws ServletException {
        super.init();
        //維護自身實例的引用
        INSTANCE = this;
        String config = getServletConfig().getInitParameter("pages");
        Collection<String> names;
        //若是配置了pages 實現名稱,賦值給names 集合,能夠支持逗號分割多個pages
        if (config != null && config.length() > 0) {
            names = Arrays.asList(Constants.COMMA_SPLIT_PATTERN.split(config));
        } else {
            //沒有配置,默認獲取說有spi擴展的pages
            names = ExtensionLoader.getExtensionLoader(PageHandler.class).getSupportedExtensions();
        }
        for (String name : names) {
            PageHandler handler = ExtensionLoader.getExtensionLoader(PageHandler.class).getExtension(name);
            pages.put(ExtensionLoader.getExtensionLoader(PageHandler.class).getExtensionName(handler), handler);
            //若是實現類上有註解 Menu,收集到添加到menus列表中,用於顯示在頁面最上方的頂級菜單
            Menu menu = handler.getClass().getAnnotation(Menu.class);
            if (menu != null) {
                menus.add(handler);
            }
        }
        //對menus 經過自定義的MenuComparator菜單排序
        Collections.sort(menus, new MenuComparator());
    }
//處理請求過程
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        if (!response.isCommitted()) {
            PrintWriter writer = response.getWriter();
            String uri = request.getRequestURI();
            boolean isHtml = false;
            if (uri == null || uri.length() == 0 || "/".equals(uri)) {
                //默認用index PageHandler實現
                uri = "index";
                isHtml = true;
            } else {
                //uri 去頭,截尾
                if (uri.startsWith("/")) {
                    uri = uri.substring(1);
                }
                if (uri.endsWith(".html")) {
                    uri = uri.substring(0, uri.length() - ".html".length());
                    isHtml = true;
                }
            }
            if (uri.endsWith("favicon.ico")) {
                response.sendError(HttpServletResponse.SC_NOT_FOUND);
                return;
            }
            //到這裏 uri 就是某個pageHandler spi 實現名稱
            ExtensionLoader<PageHandler> pageHandlerLoader = ExtensionLoader.getExtensionLoader(PageHandler.class);
            PageHandler pageHandler = pageHandlerLoader.hasExtension(uri) ? pageHandlerLoader.getExtension(uri) : null;
            if (isHtml) {
                //拼接html代碼
                writer.println("<html><head><title>Dubbo</title>");
                writer.println("<style type=\"text/css\">html, body {margin: 10;padding: 0;background-color: #6D838C;font-family: Arial, Verdana;font-size: 12px;color: #FFFFFF;text-align: center;vertical-align: middle;word-break: break-all; } table {width: 90%; margin: 0px auto;border-collapse: collapse;border: 8px solid #FFFFFF; } thead tr {background-color: #253c46; } tbody tr {background-color: #8da5af; } th {padding-top: 4px;padding-bottom: 4px;font-size: 14px;height: 20px; } td {margin: 3px;padding: 3px;border: 2px solid #FFFFFF;font-size: 14px;height: 25px; } a {color: #FFFFFF;cursor: pointer;text-decoration: underline; } a:hover {text-decoration: none; }</style>");
                writer.println("</head><body>");
            }
            if (pageHandler != null) {
                Page page = null;
                try {
                    String query = request.getQueryString();
                    //把查詢條件做爲參數放入handle 參數URL
                    //經過pageHandler返回page 對象
                    page = pageHandler.handle(URL.valueOf(request.getRequestURL().toString()
                            + (query == null || query.length() == 0 ? "" : "?" + query)));
                } catch (Throwable t) {
                    logger.warn(t.getMessage(), t);
                    String msg = t.getMessage();
                    if (msg == null) {
                        msg = StringUtils.toString(t);
                    }
                    if (isHtml) {
                        writer.println("<table>");
                        writer.println("<thead>");
                        writer.println("    <tr>");
                        writer.println("        <th>Error</th>");
                        writer.println("    </tr>");
                        writer.println("</thead>");
                        writer.println("<tbody>");
                        writer.println("    <tr>");
                        writer.println("        <td>");
                        writer.println("            " + msg.replace("<", "&lt;").replace(">", "&lt;").replace("\n", "<br/>"));
                        writer.println("        </td>");
                        writer.println("    </tr>");
                        writer.println("</tbody>");
                        writer.println("</table>");
                        writer.println("<br/>");
                    } else {
                        writer.println(msg);
                    }
                }
                if (page != null) {
                    if (isHtml) {
                        //經過handler方法放回的page對象構造html
                        String nav = page.getNavigation();
                        if (nav == null || nav.length() == 0) {
                            nav = ExtensionLoader.getExtensionLoader(PageHandler.class).getExtensionName(pageHandler);
                            nav = nav.substring(0, 1).toUpperCase() + nav.substring(1);
                        }
                        if (!"index".equals(uri)) {
                            nav = "<a href=\"/\">Home</a> &gt; " + nav;
                        }
                        //繪製菜單部分
                        writeMenu(request, writer, nav);
                        //繪製表格部分
                        writeTable(writer, page.getTitle(), page.getColumns(),
                                page.getRows());
                    } else {
                        if (page.getRows().size() > 0 && page.getRows().get(0).size() > 0) {
                            writer.println(page.getRows().get(0).get(0));
                        }
                    }
                }
            } else {
                //沒有pageHanlder 實現提示 Not found
                if (isHtml) {
                    writer.println("<table>");
                    writer.println("<thead>");
                    writer.println("    <tr>");
                    writer.println("        <th>Error</th>");
                    writer.println("    </tr>");
                    writer.println("</thead>");
                    writer.println("<tbody>");
                    writer.println("    <tr>");
                    writer.println("        <td>");
                    writer.println("            Not found " + uri + " page. Please goto <a href=\"/\">Home</a> page.");
                    writer.println("        </td>");
                    writer.println("    </tr>");
                    writer.println("</tbody>");
                    writer.println("</table>");
                    writer.println("<br/>");
                } else {
                    writer.println("Not found " + uri + " page.");
                }
            }
            if (isHtml) {
                writer.println("</body></html>");
            }
            //寫到客戶端
            writer.flush();
        }
    }

經過上面的代碼能夠知道,serlvet是經過分析uri的請求路徑,動態加載相應的PageHandler並經過調用其hanlder方法,來獲取頁面要展現的數據的。

這裏看下PageHandler一個具體擴展實現ProvidersPageHandler,它的hanlder方法以下:

public Page handle(URL url) {
         //經過url獲取一些服務的基本信息
        String service = url.getParameter("service");
        String host = url.getParameter("host");
        String application = url.getParameter("application");
        if (service != null && service.length() > 0) {
            List<List<String>> rows = new ArrayList<List<String>>();
	    //重點在這,對以前訂閱信息的使用
            //經過 RegistryContainer.getInstance().getProvidersByService方法
            //獲取 RegistryContainer容器經過訂閱註冊中心獲取的 服務消費者和提供者信息
            List<URL> providers = RegistryContainer.getInstance().getProvidersByService(service);
            if (providers != null && providers.size() > 0) {
                for (URL u : providers) {
                    List<String> row = new ArrayList<String>();
                    String s = u.toFullString();
                    row.add(s.replace("&", "&amp;"));
                    row.add("<button onclick=\"if(confirm('Confirm unregister provider?')){window.location.href='unregister.html?service=" + service + "&provider=" + URL.encode(s) + "';}\">Unregister</button>");
                    rows.add(row);
                }
            }
	    //Page 對象中,主要是導航,標題,表格列,行信息,對應着咱們文章開頭的圖解說明
            return new Page("<a href=\"services.html\">Services</a> &gt; " + service
                    + " &gt; Providers | <a href=\"consumers.html?service=" + service
                    + "\">Consumers</a> | <a href=\"statistics.html?service=" + service
                    + "\">Statistics</a> | <a href=\"charts.html?service=" + service
                    + "\">Charts</a>", "Providers (" + rows.size() + ")",
                    new String[]{"Provider URL:", "Unregister"}, rows);
        } else if (host != null && host.length() > 0) {
            List<List<String>> rows = new ArrayList<List<String>>();
            List<URL> providers = RegistryContainer.getInstance().getProvidersByHost(host);
            if (providers != null && providers.size() > 0) {
                for (URL u : providers) {
                    List<String> row = new ArrayList<String>();
                    String s = u.toFullString();
                    row.add(s.replace("&", "&amp;"));
                    row.add("<button onclick=\"if(confirm('Confirm unregister provider?')){window.location.href='unregister.html?host=" + host + "&provider=" + URL.encode(s) + "';}\">Unregister</button>");
                    rows.add(row);
                }
            }
            return new Page("<a href=\"hosts.html\">Hosts</a> &gt; " + NetUtils.getHostName(host) + "/" + host + " &gt; Providers | <a href=\"consumers.html?host=" + host + "\">Consumers</a>", "Providers (" + rows.size() + ")",
                    new String[]{"Provider URL:", "Unregister"}, rows);
        } else if (application != null && application.length() > 0) {
            List<List<String>> rows = new ArrayList<List<String>>();
            List<URL> providers = RegistryContainer.getInstance().getProvidersByApplication(application);
            if (providers != null && providers.size() > 0) {
                for (URL u : providers) {
                    List<String> row = new ArrayList<String>();
                    String s = u.toFullString();
                    row.add(s.replace("&", "&amp;"));
                    row.add("<button onclick=\"if(confirm('Confirm unregister provider?')){window.location.href='unregister.html?application=" + application + "&provider=" + URL.encode(s) + "';}\">Unregister</button>");
                    rows.add(row);
                }
            }
            return new Page("<a href=\"applications.html\">Applications</a> &gt; " + application + " &gt; Providers | <a href=\"consumers.html?application=" + application + "\">Consumers</a> | <a href=\"dependencies.html?application=" + application + "\">Depends On</a> | <a href=\"dependencies.html?application=" + application + "&reverse=true\">Used By</a>", "Providers (" + rows.size() + ")",
                    new String[]{"Provider URL:", "Unregister"}, rows);
        } else {
            throw new IllegalArgumentException("Please input service or host or application parameter.");
        }
    }

最後

由消費者和服務提供者上報的動態調用信息,是以文件形式存在硬盤上的,包括圖表以png形式(由jfreechart工具生成),
存儲目錄是dubbo.jetty.directory配置指定的。這部分工做是由SimpleMonitorService實現的。

public SimpleMonitorService() {
        queue = new LinkedBlockingQueue<URL>(Integer.parseInt(ConfigUtils.getProperty("dubbo.monitor.queue", "100000")));
        //後臺守護線程
        writeThread = new Thread(new Runnable() {
            public void run() {
                while (running) {
                    try {
                        write(); // write statistics 寫統計文件
                    } catch (Throwable t) {
                        logger.error("Unexpected error occur at write stat log, cause: " + t.getMessage(), t);
                        try {
                            Thread.sleep(5000); // retry after 5 secs
                        } catch (Throwable t2) {
                        }
                    }
                }
            }
        });
        writeThread.setDaemon(true);
        writeThread.setName("DubboMonitorAsyncWriteLogThread");
        writeThread.start();
        //線程池
        chartFuture = scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
            public void run() {
                try {
                    draw(); // draw chart 畫圖
                } catch (Throwable t) {
                    logger.error("Unexpected error occur at draw stat chart, cause: " + t.getMessage(), t);
                }
            }
        }, 1, 300, TimeUnit.SECONDS);
        statisticsDirectory = ConfigUtils.getProperty("dubbo.statistics.directory");
        chartsDirectory = ConfigUtils.getProperty("dubbo.charts.directory");
    }

具體生成的文件這裏簡單切幾個圖,配置文件是存${user.home}/monitor目錄下

monitor目錄下有 charts 和statistics兩個目錄,分別存放圖片和統計數據文件的。

這裏在日期+接口名+方法名 格式的目錄下,有生成好的圖表文件,形如

下面兩個圖是statistics目錄下統計文件和文件具體內容

相關文章
相關標籤/搜索