上两章从点到点讲了,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="width: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>

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

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

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

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

加载请求流程图:

出入站处理流程:

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

应用监控CAT之cat-home源码阅读(三)的更多相关文章

  1. 25 BasicUsageEnvironment0基本使用环境基类——Live555源码阅读(三)UsageEnvironment

    25 BasicUsageEnvironment0基本使用环境基类——Live555源码阅读(三)UsageEnvironment 25 BasicUsageEnvironment0基本使用环境基类— ...

  2. 26 BasicUsageEnvironment基本使用环境——Live555源码阅读(三)UsageEnvironment

    26 BasicUsageEnvironment基本使用环境--Live555源码阅读(三)UsageEnvironment 26 BasicUsageEnvironment基本使用环境--Live5 ...

  3. 24 UsageEnvironment使用环境抽象基类——Live555源码阅读(三)UsageEnvironment

    24 UsageEnvironment使用环境抽象基类——Live555源码阅读(三)UsageEnvironment 24 UsageEnvironment使用环境抽象基类——Live555源码阅读 ...

  4. SparkSQL(源码阅读三)

    额,没忍住,想完全了解sparksql,毕竟一直在用嘛,想一次性搞清楚它,所以今天再多看点好了~ 曾几何时,有一个叫做shark的东西,它改了hive的源码...突然有一天,spark Sql突然出现 ...

  5. SpringMVC源码阅读(三)

    先理一下Bean的初始化路线 org.springframework.beans.factory.support.AbstractBeanDefinitionReader public int loa ...

  6. JDK源码阅读(三) Collection<T>接口,Iterable<T>接口

    package java.util; public interface Collection<E> extends Iterable<E> { //返回该集合中元素的数量 in ...

  7. 23 使用环境 UsageEnvironment——Live555源码阅读

    23 使用环境 UsageEnvironment——Live555源码阅读(三)UsageEnvironment 23 使用环境 UsageEnvironment——Live555源码阅读(三)Usa ...

  8. Struts2源码阅读(一)_Struts2框架流程概述

    1. Struts2架构图  当外部的httpservletrequest到来时 ,初始到了servlet容器(所以虽然Servlet和Action是解耦合的,但是Action依旧能够通过httpse ...

  9. 应用监控CAT之cat-client源码阅读(一)

    CAT 由大众点评开发的,基于 Java 的实时应用监控平台,包括实时应用监控,业务监控.对于及时发现线上问题非常有用.(不知道大家有没有在用) 应用自然是最初级的,用完之后,还想了解下其背后的原理, ...

  10. Pytorch版本yolov3源码阅读

    目录 Pytorch版本yolov3源码阅读 1. 阅读test.py 1.1 参数解读 1.2 data文件解析 1.3 cfg文件解析 1.4 根据cfg文件创建模块 1.5 YOLOLayer ...

随机推荐

  1. 7.Redis主线程阻塞原因

    7.Redis主线程阻塞原因7.1 发现阻塞7.2 内在原因7.2.1 API或数据结构使用不合理7.2.2 CPU饱和7.2.3 持久化阻塞7.3 外在原因7.3.1 CPU竞争7.3.2 内存交换 ...

  2. localhost换成127.0.0.1和本机IP打不开本地项目了的问题

    点击桌面右下角的小三角, iis express右键—>显示所有应用程序—>点击网站名称,配置文件路径,找到配置文件,以记事本打开, 按照configuration--system.app ...

  3. Bar 柱状图

    1.生成基本图形 向上向下分别生成12个数据,X为 0 到 11 的整数 ,Y是相应的均匀分布的随机数据. 使用的函数是plt.bar,参数为X和Y: import matplotlib.pyplot ...

  4. prefProvider.kt

    package com.gh0u1l5.wechatmagician.frontend import android.content.ContentProvider import android.co ...

  5. tomcat中如何禁止列目录下的文件

    tomcat中如何禁止列目录下的文件在{tomcat_home}/conf/web.xml中,把listings参数设置成false即可,如下: <servlet> <servlet ...

  6. Mybatis中的CDATA标签

    术语 CDATA 指的是不应由 XML 解析器进行解析的文本数据(Unparsed Character Data). 在 XML 元素中,"<" 和 "&& ...

  7. python3中 tkinter模块创建window窗体、添加按钮、事务处理、创建菜单等的使用

    开始接触桌面图形界面编程,你可以到安装路径  \lib\tkinter 打开__init__.py 文件了解tkinter 1    tkinter 模块创建窗体,代码如下截图: 运行结果,如有右图显 ...

  8. 02.02.03第3章 餐饮项目案例(Power BI商业智能分析)

    02.02.03第3章 餐饮项目案例 02.02.03.01餐饮数据理解与读入 00:06:12 02.02.03.02餐饮数据处理 00:29:57 处理生成的表为: 02.02.03.03餐饮数据 ...

  9. C# 自动程序 windows 无法启动 XXXX 服务 错误5 拒绝访问

    遇到过两次 这样的问题了,所以记录一下 原因可能是服务所在文件的目录权限不够 解决方法: 1是查看服务对应的程序所在的目录 2是设置目录的安全权限 右击–属性–安全–添加相应的帐号,给予除完全控制外的 ...

  10. Shell条件测试和流程控制-4