【CAT魔改】为cat-home Problem报表查询增加更多的查询项

0. 目录

      • 1. 背景
      • 2. 解决方案
      • 3. 实现原理
      • 4. 相关核心类
        • 4.1 `LongExecutionProblemHandler`类。
        • 4.2 `ProblemStatistics`类。
        • 4.3 其它
      • 5. 参考

1. 背景

正如笔者在自己的开源项目【CAT魔改】CAT-LOCAL项目的诞生中所介绍的:

CAT服务端展示层的慢请求过滤时长极限值为5秒钟,但在笔者所参与的部分业务逻辑中,其时间相对要长不少,这导致曾经项目组进行性能优化时,需要额外花费较多时间筛选出真正异常的请求。"

本文尝试突破以上约束,提供相应的解决方案,同时介绍一些相关的CAT底层实现原理。

样例图如下(在原有限度基础上我们新增了10,20,30秒时长的过滤;其它诸如long-sql等同理):
【CAT魔改】为cat-home Problem报表查询增加更多的查询项_第1张图片

2. 解决方案

能找到这里的基本都是奔着解决方案来的,所以按照惯例,我们先给出解决方案。

LongExecutionProblemHandler 类中给字段m_defaultLongUrlDuration添加额外的默认值(例如笔者这里新增的 10000(10秒) ):

public class LongExecutionProblemHandler extends ProblemHandler implements Initializable {
     
	public static final String ID = "long-execution";

	@Inject
	private ServerConfigManager m_configManager;

	// 对应cat-home中problem报表页面下的 long-service查询条件 (注意这里的service指代的是remoteService调用,而不是MVC里的Service层; 与下面的longCall呼应)
	private int[] m_defaultLongServiceDuration = {
      50, 100, 500, 1000, 3000, 5000 };
	// 对应long-sql查询条件
	private int[] m_defaultLongSqlDuration = {
      100, 500, 1000, 3000, 5000 };
	// ============================================= 下面这个数组就是我们唯一需要修改的位置
	// 对应long-url查询条件; 注意 10000是笔者自己加的
	private int[] m_defaultLongUrlDuration = {
      1000, 2000, 3000, 5000,10000 };
	// 对应long-call查询条件
	private int[] m_defalutLongCallDuration = {
      100, 500, 1000, 3000, 5000 };
	// 对应long-cache查询条件
	private int[] m_defaultLongCacheDuration = {
      10, 50, 100, 500 };

	.....
}

修改完成之后,编译项目重新部署即可。

3. 实现原理

我们先来个总结:

  1. 其实CAT里针对"过长时间执行的URL,RemoteCall"等的判断,**是在服务端接收到客户端推送来的日志时就将它们按照既定的Threshold规则进行了分类,(例如大于5秒的有哪些,大于3秒小于5秒的有哪些;例如默认的long-url分级为 { 1000, 2000, 3000, 5000 } 这几档。这些默认规则在LongExecutionProblemHandler中定义——m_defaultLongUrlDuration字段),而并非我们的第一反应里的"在用户实际查询时候做",毕竟后者意味着每次相关请求处理都得全量筛查一遍,这是不合理的。
  2. 上一步在服务端按照既定的规则将超时限的调用按时限规则进行了分类之后(例如 大于10秒的请求是被分配在了默认规则5秒的那个集合中),接下来在用户发起调用请求时,如果用户提供的 Threshold (例如 9000ms)大于 默认的Threshold规则(例如 long-url之5000ms), 则获取不到任何数据。这一步逻辑可以在源码ProblemStatistics.getDurationsByType(String type, Entity entity) 方法中找到端倪。(结合 problemStatics.jsp页面中的 ${model.allStatistics.status} 的取值层级, 以及上面的 com.dianping.cat.report.page.problem.Model 中字段的层级 : Model(model字段名) > ProblemStatistics(allStatistics字段名) > TypeStatistics(status字段名) > StatusStatistics( XXX ) 能更快理解)。

4. 相关核心类

接下来让我们更为近距离地观察一些核心类中的实现逻辑。

4.1 LongExecutionProblemHandler类。

该类就是负责我们上面原理总结里提到的"接收到客户端推送来的日志信息时对其进行超时限请求的分类"。

该类隶属于ProblemAnalyzer(作为MessageAnalyzer的子类,它负责对请求日志进行Problem维度的分析)。

private void processLongUrl(Machine machine, Transaction transaction, MessageTree tree) {
     
	long duration = (transaction).getDurationInMillis();
	String domain = tree.getDomain();
	
	// 这是关键性的一步
	long nomarizeDuration = computeLongDuration(duration, domain, m_defaultLongUrlDuration, m_longUrlThresholds);
	if (nomarizeDuration > 0) {
     
		String type = ProblemType.LONG_URL.getName();
		String status = transaction.getName();
		Entity problem = findOrCreateEntity(machine, type, status);

		updateEntity(tree, problem, (int) nomarizeDuration);
	}
}

public int computeLongDuration(long duration, String domain, int[] defaultLongDuration,
						Map<String, Integer> longThresholds) {
     
	int[] messageDuration = defaultLongDuration;
	// 优先进行既定规则的分析;将默认规则进行倒序地比较,判断当前请求日志耗时落在哪个区间
	//   以long-url为例,如果是 9000则落在5000这个区间; 4500则落在3000这个区间,以此类推。
	for (int i = messageDuration.length - 1; i >= 0; i--) {
     
		if (duration >= messageDuration[i]) {
     
			return messageDuration[i];
		}
	}
	// 如果本次请求日志耗时没有落在上面的规则里(注意:此时就只可能是当前请求日志耗时比上面数组里的最小值还要小,这也是为啥笔者选择修改源码的主要原因)
	// longThresholds字段值的来源,其实是在 http://context:port/cat/s/config?op=serverConfigUpdate 下的:
	//	
	//		
	//			
	//			
	//		
	//		
	Integer value = longThresholds.get(domain);

	if (value != null && duration >= value) {
     
		return value;
	} else {
     
		return -1;
	}
}

4.2 ProblemStatistics类。

这个类负责我们上面原理总结里提到的"对应用户查询请求,将大于指定时限的那几类日志统计出来",举个例子就是,如果用户要求查询大于3秒的请求,则返回 3秒,5秒两个类别的日志(这个分类在 LongExecutionProblemHandler中已经做完了);而如果用户要求查询大于6秒的请求,则返回空。(因为默认情况下CAT是没有"大于6秒"这一分类)

	private List<Duration> getDurationsByType(String type, Entity entity) {
     
		List<Duration> durations = new ArrayList<Duration>();
		if (ProblemType.LONG_URL.getName().equals(type)) {
     
			// 注意这里Integer类型的key, 以long-url为例,就是上面的{ 1000, 2000, 3000, 5000 } 中的一个;这也是为什么你直接在cat-home前端修改 urlThreshold 的传参是无效的;因为key的最大值只到5;除非修改源码,让CAT一开始就对日志进行单独分类
			for (java.util.Map.Entry<Integer, Duration> temp : entity.getDurations().entrySet()) {
     
				if (temp.getKey() >= m_longConfig.getUrlThreshold()) {
     
					durations.add(temp.getValue());
				}
			}
		} else {
     
			durations.add(entity.getDurations().get(0));
		}
		return durations;
	}

4.3 其它

该package下对应的就是Problem这一报表功能的后端实现:

  1. com.dianping.cat.report.page.problem.ProblemReport。我们看到的Problem报表里的信息,对应到内存中就是一个 ProblemReport实例。
  2. com.dianping.cat.report.page.problem.Payload 接收Problem报表前端页面向后端发起请求时携带的参数。例如其中的 m_urlThreshold字段正是我们所寻找的时长过滤
  3. /jsp/report/problem/problemStatics.jsp 这是 Problem前端展示页面。

5. 参考

  1. 【CAT魔改】为cat-home添加链路追踪查询
  2. 【CAT魔改】CAT-LOCAL项目的诞生

你可能感兴趣的:(CAT,CAT,报表,监控)