作为观察监控的平台,为所需要的人提供着可视化的稳健服务!
作为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);
}
}
// 设置概要信息
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));
}
}
// 如上过程,简单或复杂的页面业务已经ok了
其他业务逻辑看代码自然一目了然了!
如有必要再添加几个有意义的 方法。
具体的业务展现逻辑一般都比较简单的,这里就不赘述了,主要在于熟悉业务,熟悉表结构。
流程图就暂不显示了,请自行脑补吧。
cat本身是好几年前的产物,技术自然算不上新,要去深究意义也不大,重在学习吧。