zoukankan      html  css  js  c++  java
  • 应用监控CAT之cat-home源码阅读(三)

      上两章从点到点讲了,cat-client  到  cat-consumer 的请求处理过程,但是怎么样让我们监控给人看到呢?那么就需要一个展示的后台了,也就是本章要讲的 cat-home 模块 ! 带你一起走进cat-home。

      作为观察监控的平台,为所需要的人提供着可视化的稳健服务!那是必须的!

      作为web展现层,在java中,自然是以servlet为接收方法了。

      以tomcat作为web容器,进行运行cat-home服务。

    servlet 以处理 uri 为基础,因此,让我们先看一下都有些什么样的路由。也就是说总体服务能力就是这些。

    @OutboundActionMeta(name = "home")
    @OutboundActionMeta(name = "app")
    @OutboundActionMeta(name = "cdn")
    @OutboundActionMeta(name = "top")
    @OutboundActionMeta(name = "web")
    @OutboundActionMeta(name = "home")
    @OutboundActionMeta(name = "alert")
    @OutboundActionMeta(name = "cache")
    @OutboundActionMeta(name = CrossAnalyzer.ID)
    @OutboundActionMeta(name = "e")
    @OutboundActionMeta(name = "model")
    @OutboundActionMeta(name = StateAnalyzer.ID)
    @OutboundActionMeta(name = MatrixAnalyzer.ID)
    @OutboundActionMeta(name = MetricAnalyzer.ID)
    @OutboundActionMeta(name = "system")
    @OutboundActionMeta(name = "m")
    @OutboundActionMeta(name = "monitor")
    @OutboundActionMeta(name = "network")
    @OutboundActionMeta(name = "p")
    @OutboundActionMeta(name = "storage")
    @OutboundActionMeta(name = "activity")
    @OutboundActionMeta(name = "database")
    @OutboundActionMeta(name = "overload")
    @OutboundActionMeta(name = "dashboard")
    @OutboundActionMeta(name = "h")
    @OutboundActionMeta(name = "alteration")
    @OutboundActionMeta(name = DependencyAnalyzer.ID)
    @OutboundActionMeta(name = "statistics")
    @OutboundActionMeta(name = "t")
    @OutboundActionMeta(name = "login")
    @OutboundActionMeta(name = "config")
    @OutboundActionMeta(name = "plugin")
    @OutboundActionMeta(name = "router")

    目录结构为 xxx/Handler.java,也算是比较难以理解的结构了。不过学习还是可以的!!

    既然是web服务,第一个自然要看一下 web.xml 了:

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
        version="2.5">
        <!-- 初始化一些cat需要的参数 -->
        <filter>
            <filter-name>cat-filter</filter-name>
            <filter-class>com.dianping.cat.servlet.CatFilter</filter-class>
        </filter>
        <!-- 设置响应用的cookie信息 -->
        <filter>
            <filter-name>domain-filter</filter-name>
            <filter-class>com.dianping.cat.report.view.DomainFilter</filter-class>
        </filter>
        <!-- 以 cat-servlet 作为第一个启动的servlet -->
        <servlet>
            <servlet-name>cat-servlet</servlet-name>
            <servlet-class>com.dianping.cat.servlet.CatServlet</servlet-class>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <!-- 以 mvc-servlet 作为第二个启动的servlet -->
        <servlet>
            <servlet-name>mvc-servlet</servlet-name>
            <servlet-class>org.unidal.web.MVC</servlet-class>
            <init-param>
                <param-name>cat-client-xml</param-name>
                <param-value>client.xml</param-value>
            </init-param>
            <init-param>
                <param-name>init-modules</param-name>
                <param-value>false</param-value>
            </init-param>
            <load-on-startup>2</load-on-startup>
        </servlet>
        <!-- 定义过滤器的使用场景: REQUEST -->
        <filter-mapping>
            <filter-name>cat-filter</filter-name>
            <url-pattern>/r/*</url-pattern>
            <dispatcher>REQUEST</dispatcher>
        </filter-mapping>
        <filter-mapping>
            <filter-name>domain-filter</filter-name>
            <url-pattern>/r/*</url-pattern>
            <dispatcher>REQUEST</dispatcher>
        </filter-mapping>
        <filter-mapping>
            <filter-name>cat-filter</filter-name>
            <url-pattern>/s/*</url-pattern>
            <dispatcher>REQUEST</dispatcher>
        </filter-mapping>
        <filter-mapping>
            <filter-name>cat-filter</filter-name>
            <url-pattern>/jsp/*</url-pattern>
            <dispatcher>FORWARD</dispatcher>
        </filter-mapping>
        <!-- servlet 请求映射,主要转给 mvc-servlet,如果匹配不到再交给 -->
        <servlet-mapping>
            <servlet-name>mvc-servlet</servlet-name>
            <url-pattern>/r/*</url-pattern>
        </servlet-mapping>
        <servlet-mapping>
            <servlet-name>mvc-servlet</servlet-name>
            <url-pattern>/s/*</url-pattern>
        </servlet-mapping>
        <jsp-config>
            <taglib>
                <taglib-uri>/WEB-INF/app.tld</taglib-uri>
                <taglib-location>/WEB-INF/app.tld</taglib-location>
            </taglib>
        </jsp-config>
    </web-app>

    // 其中 Cat-Servlet 主要用于初始化cat相关的程序,不作具体的请求接收功能

    // 主要为调用如下 initComponents() 方法
        @Override
        protected void initComponents(ServletConfig servletConfig) throws ServletException {
            try {
                ModuleContext ctx = new DefaultModuleContext(getContainer());
                ModuleInitializer initializer = ctx.lookup(ModuleInitializer.class);
                File clientXmlFile = getConfigFile(servletConfig, "cat-client-xml", "client.xml");
                File serverXmlFile = getConfigFile(servletConfig, "cat-server-xml", "server.xml");
    
                ctx.setAttribute("cat-client-config-file", clientXmlFile);
                ctx.setAttribute("cat-server-config-file", serverXmlFile);
                initializer.execute(ctx);
            } catch (Exception e) {
                m_exception = e;
                System.err.println(e);
                throw new ServletException(e);
            }
        }
        // DefaultModuleInitializer.execute() 运行,
    public class DefaultModuleInitializer implements ModuleInitializer {
       @Inject
       private ModuleManager m_manager;
    
       @InjectAttribute
       private boolean m_verbose;
    
       private int m_index = 1;
    
       // 调入,获取topModules,进行加载
       @Override
       public void execute(ModuleContext ctx) {
          Module[] modules = m_manager.getTopLevelModules();
    
          execute(ctx, modules);
       }
    
       @Override
       public void execute(ModuleContext ctx, Module... modules) {
          Set<Module> all = new LinkedHashSet<Module>();
    
          info(ctx, "Initializing top level modules:");
    
          for (Module module : modules) {
             info(ctx, "   " + module.getClass().getName());
          }
    
          try {
            // 先调用 setup() 方法
             expandAll(ctx, modules, all);
    
             for (Module module : all) {
                if (!module.isInitialized()) {
                    // 初始化具体的类的初始化方法
                   executeModule(ctx, module, m_index++);
                }
             }
          } catch (Exception e) {
             throw new RuntimeException("Error when initializing modules! Exception: " + e, e);
          }
       }
    
       private synchronized void executeModule(ModuleContext ctx, Module module, int index) throws Exception {
          long start = System.currentTimeMillis();
    
          // set flat to avoid re-entrance
          module.setInitialized(true);
    
          info(ctx, index + " ------ " + module.getClass().getName());
    
          // execute itself after its dependencies
          module.initialize(ctx);
    
          long end = System.currentTimeMillis();
          info(ctx, index + " ------ " + module.getClass().getName() + " DONE in " + (end - start) + " ms.");
       }
    
       private void expandAll(ModuleContext ctx, Module[] modules, Set<Module> all) throws Exception {
          if (modules != null) {
             for (Module module : modules) {
                expandAll(ctx, module.getDependencies(ctx), all);
    
                if (!all.contains(module)) {
                   if (module instanceof AbstractModule) {
                      ((AbstractModule) module).setup(ctx);
                   }
    
                   all.add(module);
                }
             }
          }
       }
    
    }

    // 主要接收页面请求的mvc-servlet, 

        // 初始化mvc
       @Override
       protected void initComponents(ServletConfig config) throws Exception {
          String contextPath = config.getServletContext().getContextPath();
          String path = contextPath == null || contextPath.length() == 0 ? "/" : contextPath;
    
          getLogger().info("MVC is starting at " + path);
    
          // 初始化cat
          initializeCat(config);
          // 初始化模块
          initializeModules(config);
    
          // 获取所有role为mvc的handler,如: r:t, r:home, r:model
          m_handler = lookup(RequestLifecycle.class, "mvc");
          m_handler.setServletContext(config.getServletContext());
    
          config.getServletContext().setAttribute(ID, this);
          getLogger().info("MVC started at " + path);
       }
        // MVC, 接收请求,交由handler处理
       @Override
       protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
          if (request.getCharacterEncoding() == null) {
             request.setCharacterEncoding("UTF-8");
          }
    
          response.setContentType("text/html;charset=UTF-8");
    
          try {
            // 转到handler处理
             m_handler.handle(request, response);
          } catch (Throwable t) {
             String message = "Error occured when handling uri: " + request.getRequestURI();
    
             getLogger().error(message, t);
    
             if (!response.isCommitted()) {
                response.sendError(500, message);
             }
          }
       }
       // DefaultRequestLifecycle.handle(), 接管请求第一棒
       public void handle(final HttpServletRequest request, final HttpServletResponse response) throws IOException {
          RequestContext context = m_builder.build(request);
    
          try {
             handleRequest(request, response, context);
          } finally {
             m_builder.reset(context);
          }
       }
       // DefaultRequestContextBuilder.build(), 构建请求数据, 分解 module,action , 找到对应的处理handler
       @Override
       public RequestContext build(HttpServletRequest request) {
          ParameterProvider provider = buildParameterProvider(request);
          String requestModuleName = provider.getModuleName();
          ActionResolver actionResolver = (ActionResolver) m_modelManager.getActionResolver(requestModuleName);
    
          if (actionResolver == null) {
             return null;
          }
    
          UrlMapping urlMapping = actionResolver.parseUrl(provider);
          String action = urlMapping.getAction();
          // 获取 action
          InboundActionModel inboundAction = m_modelManager.getInboundAction(requestModuleName, action);
    
          if (inboundAction == null) {
             return null;
          }
    
          RequestContext context = new RequestContext();
          // 获取 module
          ModuleModel module = m_modelManager.getModule(requestModuleName, action);
    
          urlMapping.setModule(module.getModuleName());
          // 设置配置参数及请求参数到上下文
          context.setActionResolver(actionResolver);
          context.setParameterProvider(provider);
          context.setUrlMapping(urlMapping);
          context.setModule(module);
          context.setInboundAction(inboundAction);
          context.setTransition(module.findTransition(inboundAction.getTransitionName()));
          context.setError(module.findError(inboundAction.getErrorActionName()));
    
          return context;
       }
       // 处理请求,先处理入站,再处理出站
       private void handleRequest(final HttpServletRequest request, final HttpServletResponse response,
             RequestContext requestContext) throws IOException {
          if (requestContext == null) {
             showPageNotFound(request, response);
             return;
          }
    
          ModuleModel module = requestContext.getModule();
          InboundActionModel inboundAction = requestContext.getInboundAction();
          ActionContext<?> actionContext = createActionContext(request, response, requestContext, inboundAction);
          Transaction t = Cat.getManager().getPeekTransaction();
    
          if (t == null) { // in case of no CatFilter is configured
             t = NullMessage.TRANSACTION;
          }
    
          request.setAttribute(CatConstants.CAT_PAGE_URI,
                actionContext.getRequestContext().getActionUri(inboundAction.getActionName()));
    
          try {
             InboundActionHandler handler = m_actionHandlerManager.getInboundActionHandler(module, inboundAction);
    
             // 调用前置方法,为类似于拦截器一类的方法生效
             handler.preparePayload(actionContext);
             
             if (!handlePreActions(request, response, module, requestContext, inboundAction, actionContext)) {
                return;
             }
    
             // 正式开始处理进入请求
             handleInboundAction(module, actionContext);
    
             t.addData("module", module.getModuleName());
             t.addData("in", actionContext.getInboundAction());
    
             if (actionContext.isProcessStopped()) {
                t.addData("processStopped=true");
                return;
             }
    
             handleTransition(module, actionContext);
    
             t.addData("out", actionContext.getOutboundAction());
             // 开始处理出站操作
             handleOutboundAction(module, actionContext);
          } catch (Throwable e) {
             handleException(request, e, actionContext);
          }
       }
       // 正式处理入站请求
       private void handleInboundAction(ModuleModel module, ActionContext<?> actionContext) throws ActionException {
          InboundActionModel inboundAction = actionContext.getRequestContext().getInboundAction();
          InboundActionHandler inboundActionHandler = m_actionHandlerManager.getInboundActionHandler(module, inboundAction);
          // 调用获取到的handler的handle方法完成
          inboundActionHandler.handle(actionContext);
       }
       // DefaultActionHandlerManager.getInboundActionHandler(), 获取入站处理handler, 以 r:home 样例为格式获取
       public InboundActionHandler getInboundActionHandler(ModuleModel module, InboundActionModel inboundAction) {
          String key = module.getModuleName() + ":" + inboundAction.getActionName();
          InboundActionHandler actionHandler = m_inboundActionHandlers.get(key);
    
          // 双重锁的应用
          if (actionHandler == null) {
             synchronized (m_inboundActionHandlers) {
                actionHandler = m_inboundActionHandlers.get(key);
    
                if (actionHandler == null) {
                   actionHandler = lookup(InboundActionHandler.class);
                   actionHandler.initialize(inboundAction);
                   m_inboundActionHandlers.put(key, actionHandler);
                }
             }
          }
    
          return actionHandler;
       }
       // DefaultInboundActionHandler.handle(), 类似于 around 式的切面
       public void handle(ActionContext ctx) throws ActionException {
          Transaction t = m_cat.newTransaction("MVC", "InboundPhase");
    
          try {
             for (Validator<ActionContext<?>> validator : m_preValidators) {
                validator.validate(ctx);
             }
    
             if (ctx.getPayload() == null) {
                preparePayload(ctx);
             }
    
             for (Validator<ActionContext<?>> validator : m_validators) {
                validator.validate(ctx);
             }
    
             // 静态调用 ReflectUtils.invokeMethod(), 进行最后的也是最开始的业务逻辑处理
             invokeMethod(m_inboundAction.getActionMethod(), m_inboundAction.getModuleInstance(), ctx);
    
             for (Validator<ActionContext<?>> validator : m_postValidators) {
                validator.validate(ctx);
             }
    
             t.setStatus(Transaction.SUCCESS);
          } catch (Exception e) {
             String actionName = m_inboundAction.getActionName();
    
             m_cat.logError(e);
             t.setStatus(e);
             throw new ActionException("Error occured during handling inbound action(" + actionName + ")!", e);
          } finally {
             t.complete();
          }
       }

    // 以上,就是整个的入站调用过程

    // 接下来,就是出站的调用过程了

        // t.addData("out", actionContext.getOutboundAction()); handleOutboundAction(module, actionContext); 进入出站处理
       private void handleOutboundAction(ModuleModel module, ActionContext<?> actionContext) throws ActionException {
          String outboundActionName = actionContext.getOutboundAction();
          OutboundActionModel outboundAction = module.getOutbounds().get(outboundActionName);
    
          if (outboundAction == null) {
             throw new ActionException("No method annotated by @" + OutboundActionMeta.class.getSimpleName() + "("
                   + outboundActionName + ") found in " + module.getModuleClass());
          } else {
             OutboundActionHandler outboundActionHandler = m_actionHandlerManager.getOutboundActionHandler(module,
                   outboundAction);
    
             outboundActionHandler.handle(actionContext);
          }
       }
       // 获取出站路由信息
       public OutboundActionHandler getOutboundActionHandler(ModuleModel module, OutboundActionModel outboundAction) {
          String key = module.getModuleName() + ":" + outboundAction.getActionName();
          OutboundActionHandler actionHandler = m_outboundActionHandlers.get(key);
    
          if (actionHandler == null) {
             synchronized (m_outboundActionHandlers) {
                actionHandler = m_outboundActionHandlers.get(key);
    
                if (actionHandler == null) {
                   actionHandler = lookup(OutboundActionHandler.class);
                   actionHandler.initialize(outboundAction);
                   m_outboundActionHandlers.put(key, actionHandler);
                }
             }
          }
    
          return actionHandler;
       }
       // DefaultOutboundActionHandler.handle(), 记录操作,调用action
       public void handle(ActionContext<?> context) throws ActionException {
          Transaction t = m_cat.newTransaction("MVC", "OutboundPhase");
    
          try {
             invokeMethod(m_outboundAction.getMethod(), m_outboundAction.getModuleInstance(), context);
             t.setStatus(Transaction.SUCCESS);
          } catch (RuntimeException e) {
             String actionName = m_outboundAction.getActionName();
    
             m_cat.logError(e);
             t.setStatus(e);
             throw new ActionException("Error occured during handling outbound action(" + actionName + ")", e);
          } finally {
             t.complete();
          }
       }

    // 以上,出入站的流程就讲完了

    // 接下来,看一下具体的几个处理讲求的实例

    // home/Handler (由 /cat 或 /cat/r 转入), 首页展现逻辑最简单,默认无数据操作
        // 入站不处理
        @Override
        @PayloadMeta(Payload.class)
        @InboundActionMeta(name = "home")
        public void handleInbound(Context ctx) throws ServletException, IOException {
        }
    
        // 出站显示首页页面
        @Override
        @OutboundActionMeta(name = "home")
        public void handleOutbound(Context ctx) throws ServletException, IOException {
            Model model = new Model(ctx);
            Payload payload = ctx.getPayload();
    
            switch (payload.getAction()) {
            case THREAD_DUMP:
                showThreadDump(model, payload);
                break;
            case VIEW:
                break;
            case CHECKPOINT:
                m_receiver.destory();
                m_realtimeConsumer.doCheckpoint();
                break;
            default:
                break;
            }
    
            model.setAction(payload.getAction());
            model.setPage(ReportPage.HOME);
            model.setDomain(payload.getDomain());
            model.setDate(payload.getDate());
            m_jspViewer.view(ctx, model);
        }
        
    // transaction/Handler, transaction 页面呈现
        // 入站不处理
        @Override
        @PayloadMeta(Payload.class)
        @InboundActionMeta(name = "t")
        public void handleInbound(Context ctx) throws ServletException, IOException {
            // display only, no action here
        }
    
        // 出站进行数据复合
        @Override
        @OutboundActionMeta(name = "t")
        public void handleOutbound(Context ctx) throws ServletException, IOException {
            Model model = new Model(ctx);
            Payload payload = ctx.getPayload();
    
            normalize(model, payload);
            String domain = payload.getDomain();
            Action action = payload.getAction();
            String ipAddress = payload.getIpAddress();
            String group = payload.getGroup();
            String type = payload.getType();
            String name = payload.getName();
            String ip = payload.getIpAddress();
    
            if (StringUtils.isEmpty(group)) {
                group = m_configManager.queryDefaultGroup(domain);
                payload.setGroup(group);
            }
            model.setGroupIps(m_configManager.queryIpByDomainAndGroup(domain, group));
            model.setGroups(m_configManager.queryDomainGroup(payload.getDomain()));
            switch (action) {
            case HOURLY_REPORT:
                // 当前小时数据
                TransactionReport report = getHourlyReport(payload);
    
                if (report != null) {
                    report = m_mergeHelper.mergeAllMachines(report, ipAddress);
    
                    model.setReport(report);
                    buildTransactionMetaInfo(model, payload, report);
                }
                break;
            case HISTORY_REPORT:
                report = m_reportService.queryReport(domain, payload.getHistoryStartDate(), payload.getHistoryEndDate());
    
                if (report != null) {
                    model.setReport(report);
                    buildTransactionMetaInfo(model, payload, report);
                }
                break;
            case HISTORY_GRAPH:
                if (Constants.ALL.equalsIgnoreCase(ipAddress)) {
                    report = m_reportService.queryReport(domain, payload.getHistoryStartDate(), payload.getHistoryEndDate());
    
                    buildDistributionInfo(model, type, name, report);
                }
    
                m_historyGraph.buildTrendGraph(model, payload);
                break;
            case GRAPHS:
                report = getHourlyGraphReport(model, payload);
    
                if (Constants.ALL.equalsIgnoreCase(ipAddress)) {
                    buildDistributionInfo(model, type, name, report);
                }
                if (name == null || name.length() == 0) {
                    name = Constants.ALL;
                }
    
                report = m_mergeHelper.mergeAllNames(report, ip, name);
    
                model.setReport(report);
                buildTransactionNameGraph(model, report, type, name, ip);
                break;
            case HOURLY_GROUP_REPORT:
                report = getHourlyReport(payload);
                report = filterReportByGroup(report, domain, group);
                report = m_mergeHelper.mergeAllMachines(report, ipAddress);
    
                if (report != null) {
                    model.setReport(report);
    
                    buildTransactionMetaInfo(model, payload, report);
                }
                break;
            case HISTORY_GROUP_REPORT:
                report = m_reportService.queryReport(domain, payload.getHistoryStartDate(), payload.getHistoryEndDate());
                report = filterReportByGroup(report, domain, group);
                report = m_mergeHelper.mergeAllMachines(report, ipAddress);
    
                if (report != null) {
                    model.setReport(report);
                    buildTransactionMetaInfo(model, payload, report);
                }
                break;
            case GROUP_GRAPHS:
                report = getHourlyGraphReport(model, payload);
                report = filterReportByGroup(report, domain, group);
                buildDistributionInfo(model, type, name, report);
    
                if (name == null || name.length() == 0) {
                    name = Constants.ALL;
                }
                report = m_mergeHelper.mergeAllNames(report, ip, name);
    
                model.setReport(report);
                buildTransactionNameGraph(model, report, type, name, ip);
                break;
            case HISTORY_GROUP_GRAPH:
                report = m_reportService.queryReport(domain, payload.getHistoryStartDate(), payload.getHistoryEndDate());
                report = filterReportByGroup(report, domain, group);
    
                buildDistributionInfo(model, type, name, report);
                List<String> ips = m_configManager.queryIpByDomainAndGroup(domain, group);
    
                m_historyGraph.buildGroupTrendGraph(model, payload, ips);
                break;
            }
    
            if (payload.isXml()) {
                m_xmlViewer.view(ctx, model);
            } else {
                m_jspViewer.view(ctx, model);
            }
        }
        // 获取小时报告实例
        private TransactionReport getHourlyReport(Payload payload) {
            String domain = payload.getDomain();
            String ipAddress = payload.getIpAddress();
            ModelRequest request = new ModelRequest(domain, payload.getDate()).setProperty("type", payload.getType())
                  .setProperty("ip", ipAddress);
    
            if (m_service.isEligable(request)) {
                // invoke service
                ModelResponse<TransactionReport> response = m_service.invoke(request);
                TransactionReport report = response.getModel();
    
                return report;
            } else {
                throw new RuntimeException("Internal error: no eligable transaction service registered for " + request + "!");
            }
        }
        // BaseCompersiteModelService.invoke(), 
        
        @Override
        public ModelResponse<T> invoke(final ModelRequest request) {
            int requireSize = 0;
            final List<ModelResponse<T>> responses = Collections.synchronizedList(new ArrayList<ModelResponse<T>>());
            // 使用信号量进行加锁
            final Semaphore semaphore = new Semaphore(0);
            final Transaction t = Cat.getProducer().newTransaction("ModelService", getClass().getSimpleName());
            int count = 0;
    
            t.setStatus(Message.SUCCESS);
            t.addData("request", request);
            t.addData("thread", Thread.currentThread());
    
            for (final ModelService<T> service : m_allServices) {
                if (!service.isEligable(request)) {
                    continue;
                }
                
                // save current transaction so that child thread can access it
                if (service instanceof ModelServiceWithCalSupport) {
                    ((ModelServiceWithCalSupport) service).setParentTransaction(t);
                }
                requireSize++;
                
                s_threadPool.submit(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            ModelResponse<T> response = service.invoke(request);
    
                            if (response.getException() != null) {
                                logError(response.getException());
                            }
                            if (response != null && response.getModel() != null) {
                                responses.add(response);
                            }
                        } catch (Exception e) {
                            logError(e);
                            t.setStatus(e);
                        } finally {
                            semaphore.release();
                        }
                    }
                });
    
                count++;
            }
    
            try {
                semaphore.tryAcquire(count, 10000, TimeUnit.MILLISECONDS); // 10 seconds timeout
            } catch (InterruptedException e) {
                // ignore it
                t.setStatus(e);
            } finally {
                t.complete();
            }
    
            String requireAll = request.getProperty("requireAll");
    
            if (requireAll != null && responses.size() != requireSize) {
                String data = "require:" + requireSize + " actual:" + responses.size();
                Cat.logEvent("FetchReportError:" + this.getClass().getSimpleName(), request.getDomain(), Event.SUCCESS, data);
    
                return null;
            }
            ModelResponse<T> aggregated = new ModelResponse<T>();
            T report = merge(request, responses);
    
            aggregated.setModel(report);
            return aggregated;
        }
        
        // TransactionMergeHelper
        
    public class TransactionMergeHelper {
    
        public TransactionReport mergeAllMachines(TransactionReport report, String ipAddress) {
            if (StringUtils.isEmpty(ipAddress) || Constants.ALL.equalsIgnoreCase(ipAddress)) {
                AllMachineMerger all = new AllMachineMerger();
    
                all.visitTransactionReport(report);
                report = all.getReport();
            }
            return report;
        }
    
        public TransactionReport mergeAllNames(TransactionReport report, String allName) {
            if (StringUtils.isEmpty(allName) || Constants.ALL.equalsIgnoreCase(allName)) {
                AllNameMerger all = new AllNameMerger();
    
                all.visitTransactionReport(report);
                report = all.getReport();
            }
            return report;
        }
    
        public TransactionReport mergeAllNames(TransactionReport report, String ipAddress, String allName) {
            TransactionReport temp = mergeAllMachines(report, ipAddress);
    
            return mergeAllNames(temp, allName);
        }
    
    }
        // AllMerchineMerger.visitTransactionReport()
        @Override
        public void visitTransactionReport(TransactionReport transactionReport) {
            m_report = new TransactionReport(transactionReport.getDomain());
            m_report.setStartTime(transactionReport.getStartTime());
            m_report.setEndTime(transactionReport.getEndTime());
            m_report.getDomainNames().addAll(transactionReport.getDomainNames());
            m_report.getIps().addAll(transactionReport.getIps());
    
            super.visitTransactionReport(transactionReport);
        }
        // 调用父类的方法 BaseVisitor.visitTransactionReport() 进行循环调用集群机器小时数据
        
       @Override
       public void visitTransactionReport(TransactionReport transactionReport) {
          for (Machine machine : transactionReport.getMachines().values()) {
             visitMachine(machine);
          }
       }
    
        @Override
        public void visitMachine(Machine machine) {
            m_report.findOrCreateMachine(Constants.ALL);
            super.visitMachine(machine);
        }
    
        @Override
        public void visitType(TransactionType type) {
            m_currentType = type.getId();
            TransactionType temp = m_report.findOrCreateMachine(Constants.ALL).findOrCreateType(m_currentType);
    
            m_merger.mergeType(temp, type);
            super.visitType(type);
        }
        
        // TransactionReportMerger.mergeType(), 进行数据合并
        
        @Override
        public void mergeType(TransactionType old, TransactionType other) {
            long totalCountSum = old.getTotalCount() + other.getTotalCount();
            if (totalCountSum > 0) {
                // 95、99线相加/总数
                double line95Values = old.getLine95Value() * old.getTotalCount() + other.getLine95Value()
                      * other.getTotalCount();
                double line99Values = old.getLine99Value() * old.getTotalCount() + other.getLine99Value()
                      * other.getTotalCount();
    
                old.setLine95Value(line95Values / totalCountSum);
                old.setLine99Value(line99Values / totalCountSum);
            }
    
            // 取总数,取最大最小值
            old.setTotalCount(totalCountSum);
            old.setFailCount(old.getFailCount() + other.getFailCount());
            old.setTps(old.getTps() + other.getTps());
    
            if (other.getMin() < old.getMin()) {
                old.setMin(other.getMin());
            }
    
            if (other.getMax() > old.getMax()) {
                old.setMax(other.getMax());
            }
    
            old.setSum(old.getSum() + other.getSum());
            old.setSum2(old.getSum2() + other.getSum2());
    
            if (old.getTotalCount() > 0) {
                old.setFailPercent(old.getFailCount() * 100.0 / old.getTotalCount());
                old.setAvg(old.getSum() / old.getTotalCount());
                old.setStd(std(old.getTotalCount(), old.getAvg(), old.getSum2(), old.getMax()));
            }
    
            if (old.getSuccessMessageUrl() == null) {
                old.setSuccessMessageUrl(other.getSuccessMessageUrl());
            }
    
            if (old.getFailMessageUrl() == null) {
                old.setFailMessageUrl(other.getFailMessageUrl());
            }
        }
    
        // visitType
       @Override
       public void visitType(TransactionType type) {
          for (TransactionName name : type.getNames().values()) {
             visitName(name);
          }
    
          for (Range2 range2 : type.getRange2s().values()) {
             visitRange2(range2);
          }
    
          for (AllDuration allDuration : type.getAllDurations().values()) {
             visitAllDuration(allDuration);
          }
       }
       
       // 消息merge完后,回到Handler, 设置概要信息
        private void buildTransactionMetaInfo(Model model, Payload payload, TransactionReport report) {
            String type = payload.getType();
            String sorted = payload.getSortBy();
            String queryName = payload.getQueryName();
            String ip = payload.getIpAddress();
    
            if (!StringUtils.isEmpty(type)) {
                DisplayNames displayNames = new DisplayNames();
    
                model.setDisplayNameReport(displayNames.display(sorted, type, ip, report, queryName));
                // 创建 pie 饼图
                buildTransactionNamePieChart(displayNames.getResults(), model);
            } else {
                model.setDisplayTypeReport(new DisplayTypes().display(sorted, ip, report));
            }
        }

    // m_jspViewer.view(ctx, model);  jsp 模板渲染,输出页面内容:

    // m_jspViewer.view(ctx, model); 找到对应模板jsp, 转发
       public void view(S ctx, T model) throws ServletException, IOException {
          HttpServletRequest req = ctx.getHttpServletRequest();
          HttpServletResponse res = ctx.getHttpServletResponse();
    
          req.setAttribute("ctx", ctx);
          req.setAttribute("payload", ctx.getPayload());
          req.setAttribute("model", model);
    
          if (m_modelHandler != null) {
             m_modelHandler.handle(req, res);
          }
    
          if (!ctx.isProcessStopped()) {
             try {
                // 找到各自配置的模板文件(其实很麻烦了)
                String path = getJspFilePath(ctx, model);
                // 转发
                req.getRequestDispatcher(path).forward(req, res);
             } catch (EOFException e) {
                // Caused by: java.net.SocketException: Broken pipe
                // ignore it
                System.out.println(String.format("[%s] HTTP request(%s) stopped by client(%s) explicitly!", new Date(),
                      req.getRequestURI(), req.getRemoteAddr()));
             }
          }
       }

    // 模板jsp示例: /jsp/report/transaction/transaction.jsp

    <%@ page session="false" language="java" pageEncoding="UTF-8" %>
    <%@ page contentType="text/html; charset=utf-8"%>
    <%@ taglib prefix="a" uri="/WEB-INF/app.tld"%>
    <%@ taglib prefix="w" uri="http://www.unidal.org/web/core"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
    <%@ taglib prefix="res" uri="http://www.unidal.org/webres"%>
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
    <jsp:useBean id="ctx" type="com.dianping.cat.report.page.transaction.Context" scope="request" />
    <jsp:useBean id="payload" type="com.dianping.cat.report.page.transaction.Payload" scope="request" />
    <jsp:useBean id="model"    type="com.dianping.cat.report.page.transaction.Model" scope="request" />
    <c:set var="report" value="${model.report}"/>
    
    <a:report title="Transaction Report${empty payload.type ? '' : ' :: '}<a href='?domain=${model.domain}&date=${model.date}&type=${payload.encodedType}'>${payload.type}</a>" navUrlPrefix="ip=${model.ipAddress}&queryname=${model.queryName}&domain=${model.domain}${empty payload.type ? '' : '&type='}${payload.encodedType}" timestamp="${w:format(model.creatTime,'yyyy-MM-dd HH:mm:ss')}">
    <jsp:attribute name="subtitle">${w:format(report.startTime,'yyyy-MM-dd HH:mm:ss')} to ${w:format(report.endTime,'yyyy-MM-dd HH:mm:ss')}</jsp:attribute>
    <jsp:body>
    <res:useJs value="${res.js.local['baseGraph.js']}" target="head-js"/>
    
    <table class="machines">
        <tr class="left">
            <th>&nbsp;[&nbsp; <c:choose>
                    <c:when test="${model.ipAddress eq 'All'}">
                        <a href="?domain=${model.domain}&date=${model.date}&type=${payload.encodedType}&queryname=${model.queryName}"
                            class="current">All</a>
                    </c:when>
                    <c:otherwise>
                        <a href="?domain=${model.domain}&date=${model.date}&type=${payload.encodedType}&queryname=${model.queryName}">All</a>
                    </c:otherwise>
                </c:choose> &nbsp;]&nbsp; <c:forEach var="ip" items="${model.ips}">
                     &nbsp;[&nbsp;
                     <c:choose>
                        <c:when test="${model.ipAddress eq ip}">
                            <a href="?domain=${model.domain}&ip=${ip}&date=${model.date}&type=${payload.encodedType}&queryname=${model.queryName}"
                                class="current">${ip}</a>
                        </c:when>
                        <c:otherwise>
                            <a href="?domain=${model.domain}&ip=${ip}&date=${model.date}&type=${payload.encodedType}&queryname=${model.queryName}">${ip}</a>
                        </c:otherwise>
                    </c:choose>
                    &nbsp;]&nbsp;
                 </c:forEach>
            </th>
        </tr>
    </table>
    <script type="text/javascript" src="/cat/js/appendHostname.js"></script>
    <script type="text/javascript">
        $(document).ready(function() {
            appendHostname(${model.ipToHostnameStr});
        });
    </script>
    <table class="groups">
        <tr class="left">
            <th> 
                <c:forEach var="group" items="${model.groups}">
                         &nbsp;[&nbsp;
                             <a href="?op=groupReport&domain=${model.domain}&date=${model.date}&group=${group}">${group}</a>
                        &nbsp;]&nbsp;
                 </c:forEach>
            </th>
        </tr>
    </table>
    <table class='table table-striped table-condensed table-hover '  style="100%;">
        <c:choose>
            <c:when test="${empty payload.type}">
                <tr><th class="left"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&sort=type">Type</a></th>
                    <th  class="right"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&sort=total">Total</a></th>
                    <th class="right"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&sort=failure">Failure</a></th>
                    <th class="right"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&sort=failurePercent">Failure%</a></th>
                    <th class="right">Sample Link</th>
                    <th class="right"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&sort=min">Min</a>(ms)</th>
                    <th class="right"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&sort=max">Max</a>(ms)</th>
                    <th class="right"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&sort=avg">Avg</a>(ms)</th>
                    <th class="right"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&sort=95line">95Line</a>(ms)</th>
                    <th class="right"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&sort=99line">99.9Line</a>(ms)</th>
                    <th class="right"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&sort=std">Std</a>(ms)</th>
                    <th class="right"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&sort=total">QPS</a></th>
                </tr>
                <c:forEach var="item" items="${model.displayTypeReport.results}" varStatus="status">
                    <c:set var="e" value="${item.detail}"/>
                    <c:set var="lastIndex" value="${status.index}"/>
                    <tr class=" right">
                        <td class="left"><a href="?op=graphs&domain=${report.domain}&date=${model.date}&ip=${model.ipAddress}&type=${item.type}" class="graph_link" data-status="${status.index}">[:: show ::]</a>
                        &nbsp;&nbsp;<a href="?domain=${report.domain}&date=${model.date}&ip=${model.ipAddress}&type=${item.type}"> ${item.detail.id}</a></td>
                        <td>${w:format(e.totalCount,'#,###,###,###,##0')}</td>
                        <td>${w:format(e.failCount,'#,###,###,###,##0')}</td>
                        <td>&nbsp;${w:format(e.failPercent/100,'0.0000%')}</td>
                        <td><a href="/cat/r/m/${empty e.failMessageUrl ? e.successMessageUrl : e.failMessageUrl}?domain=${model.domain}">Log View</a></td>
                        <td>${w:format(e.min,'###,##0.#')}</td>
                        <td>${w:format(e.max,'###,##0.#')}</td>
                        <td>${w:format(e.avg,'###,##0.0')}</td>
                        <td>${w:format(e.line95Value,'###,##0.0')}</td>
                        <td>${w:format(e.line99Value,'###,##0.0')}</td>
                        <td>${w:format(e.std,'###,##0.0')}</td>
                        <td>${w:format(e.tps,'###,##0.0')}</td>
                    </tr>
                    <tr class="graphs"><td colspan="13" style="display:none"><div id="${status.index}" style="display:none"></div></td></tr>
                    <tr style="display:none"></tr>
                </c:forEach>
            </c:when>
            <c:otherwise>
                <tr><th class="left" colspan="13"><input type="text" name="queryname" id="queryname" size="40" value="${model.queryName}">
                <input  class="btn btn-primary  btn-sm"  value="Filter" onclick="selectByName('${model.date}','${model.domain}','${model.ipAddress}','${payload.type}')" type="submit">
                支持多个字符串查询,例如sql|url|task,查询结果为包含任一sql、url、task的列。
                </th></tr>
                <tr>
                <th  style="text-align: left;"><a href="?op=graphs&domain=${report.domain}&date=${model.date}&ip=${model.ipAddress}&type=${payload.encodedType}" class="graph_link" data-status="-1">[:: show ::]</a>
                <a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&type=${payload.encodedType}&sort=type&queryname=${model.queryName}">Name</a></th>
                <th class="right"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&type=${payload.encodedType}&sort=total&queryname=${model.queryName}">Total</a></th>
                <th class="right"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&type=${payload.encodedType}&sort=failure&queryname=${model.queryName}">Failure</a></th>
                <th class="right"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&type=${payload.encodedType}&sort=failurePercent&queryname=${model.queryName}">Failure%</a></th>
                <th class="right">Sample Link</th>
                <th class="right"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&type=${payload.encodedType}&sort=min&queryname=${model.queryName}">Min</a>(ms)</th>
                <th class="right"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&type=${payload.encodedType}&sort=max&queryname=${model.queryName}">Max</a>(ms)</th>
                <th class="right"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&type=${payload.encodedType}&sort=avg&queryname=${model.queryName}">Avg</a>(ms)</th>
                <th class="right"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&type=${payload.encodedType}&sort=95line&queryname=${model.queryName}">95Line</a>(ms)</th>
                <th class="right"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&type=${payload.encodedType}&sort=99line&queryname=${model.queryName}">99.9Line</a>(ms)</th>
                <th class="right"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&type=${payload.encodedType}&sort=std&queryname=${model.queryName}">Std</a>(ms)</th>
                <th class="right"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&type=${payload.encodedType}&sort=total&queryname=${model.queryName}">QPS</a></th>
                <th class="right"><a href="?domain=${model.domain}&date=${model.date}&ip=${model.ipAddress}&type=${payload.encodedType}&sort=total&queryname=${model.queryName}">Percent%</a></th></tr>
                <tr class="graphs"><td colspan="13" style="display:none"><div id="-1" style="display:none"></div></td></tr>
                <c:forEach var="item" items="${model.displayNameReport.results}" varStatus="status">
                    <c:set var="e" value="${item.detail}"/>
                    <c:set var="lastIndex" value="${status.index}"/>
                    <tr class=" right">
                        <c:choose>
                            <c:when test="${status.index > 0}">
                                <td class="left longText" style="white-space:normal">
                                <a href="?op=graphs&domain=${report.domain}&date=${model.date}&ip=${model.ipAddress}&type=${payload.encodedType}&name=${item.name}" class="graph_link" data-status="${status.index}">[:: show ::]</a> 
                                &nbsp;&nbsp;${w:shorten(e.id, 120)}</td>
                            </c:when>
                            <c:otherwise>
                                <td class="center" style="white-space:normal">${w:shorten(e.id, 120)}</td>
                            </c:otherwise>
                        </c:choose>
                        <td>${w:format(e.totalCount,'#,###,###,###,##0')}</td>
                        <td>${w:format(e.failCount,'#,###,###,###,##0')}</td>
                        <td>&nbsp;${w:format(e.failPercent/100,'0.0000%')}</td>
                        <td class="center"><a href="/cat/r/m/${empty e.failMessageUrl ? e.successMessageUrl : e.failMessageUrl}?domain=${model.domain}">Log View</a></td>
                        <td>${w:format(e.min,'###,##0.#')}</td>
                        <td>${w:format(e.max,'###,##0.#')}</td>
                        <td>${w:format(e.avg,'###,##0.0')}</td>
                        <c:choose>
                            <c:when test="${status.index > 0}">
                                <td>${w:format(e.line95Value,'###,##0.0')}</td>
                                <td>${w:format(e.line99Value,'###,##0.0')}</td>
                            </c:when>
                            <c:otherwise>
                                <td class="center">-</td>
                                <td class="center">-</td>
                            </c:otherwise>
                        </c:choose>
                        <td>${w:format(e.std,'###,##0.0')}</td>
                        <td>${w:format(e.tps,'###,##0.0')}</td>
                        <td>${w:format(e.totalPercent,'0.00%')}</td>
                    </tr>
                    <tr class="    "><td colspan="13" style="display:none"><div id="${status.index}" style="display:none"></div></td></tr>
                    <tr></tr>
                </c:forEach>
            </c:otherwise>
        </c:choose>
    </table>
    <font color="white">${lastIndex}</font>
    <res:useJs value="${res.js.local.transaction_js}" target="bottom-js" />
    <c:choose>
        <c:when test="${not empty payload.type}">
            <table>
                <tr>
                    <td><div id="transactionGraph" class="pieChart"></div>
                    </td>
                </tr>
            </table>
            <script type="text/javascript">
                var data = ${model.pieChart};
                graphPieChart(document.getElementById('transactionGraph'), data);
            </script>
        </c:when>
    </c:choose>
    </jsp:body>
    </a:report>
    View Code

    // 如上过程,简单或复杂的页面业务已经ok了

      其他业务逻辑看代码自然一目了然了!

      如有必要再添加几个有意义的 方法。

      具体的业务展现逻辑一般都比较简单的,这里就不赘述了,主要在于熟悉业务,熟悉表结构。

    加载请求流程图:

     出入站处理流程:

      cat本身是好几年前的产物,技术自然算不上新,要去深究意义也不大,重在学习吧。

  • 相关阅读:
    数据分析之Anaconda安装
    算法作业三
    算法作业二
    html
    qingdao
    hdu 123
    排序作业
    hdu 5614
    hdu 456
    poj 3140 树形去边差异最小
  • 原文地址:https://www.cnblogs.com/yougewe/p/9508400.html
Copyright © 2011-2022 走看看