本篇文章是针对CodeIgniter框架提出了一个性能剖析的工具。可以帮助使用CI的开发者清晰的关注到代码执行过程中详细的耗时信息,以便于进一步排查问题,或者分析系统瓶颈来进行优化。
性能剖析
性能剖析(profile)这个词,我是了解与《高性能MySQL》一书。放到CI框架中,其实就是剖析框架代码在执行过程中的各个操作耗时,这些操作可能是查询数据库、查询缓存、RPC调用或者是自定义的某段代码执行过程。了解了这些操作的耗时,不仅可以排查问题(例如接口查不到数据,就可以将sql很方便的打印出来,看是不是参数有问题),更重要的是可以帮助我们优化程序(例如查找耗时瓶颈并优化、优化sql等等)。
工具原理
CI_profiling的工作过程主要为:
- 扩展CI_profiling类库,autoload中初始化该Library;
- 在不同的位置打点记录数据(例如框架初始化完成、执行sql之前后);
- 在输出内容之前整理打点数据,生成剖析结果并设置到response header中;
- 在chrome中接收response header,并打印在console中(通过chrome扩展完成);
工具详解
首先看下Library:
<?php
// 定义类型常量
defined('PROFILING_DB') or define('PROFILING_DB', 1);
defined('PROFILING_CACHE') or define('PROFILING_CACHE', 2);
defined('PROFILING_LOG') or define('PROFILING_LOG', 3);
defined('PROFILING_HTTP') or define('PROFILING_HTTP', 4);
defined('PROFILING_CUSTOMIZED') or define('PROFILING_CUSTOMIZED', 6);
defined('PROFILING_TOTAL') or define('PROFILING_TOTAL', 100);
// 定义时间点常量
defined('PROFILING_START') or define('PROFILING_START', 1);
defined('PROFILING_END') or define('PROFILING_END', 2);
class CiProfiling
{
// 运行环境,只有在此环境下,才会进行性能剖析
private $env;
// 是否输出详细信息
private $output_detail;
// 存放db操作相关的数据
private $db_profiling;
// 存放缓存操作相关的数据
private $cache_profiling;
// 存放日志操作相关的数据
private $log_profiling;
// 存放http操作相关的数据
private $http_profiling;
// 存放自定义执行区间的数据
private $customized_profiling;
// 存放总耗时数据
private $total_profiling;
// 是否已输出
private $outputed;
// 记录时间点
public function record($profiling_type, $record_type, $data) {}
// 检查运行环境
private function checkEnv($env) {}
// 剖析运算,得出结果
public function calculate() {}
// 将性能剖析结果放入header
public function setHeader() {}
}
在类库里,我们两个配置:运行环境和是否输出详细内容。运行环境决定是否进行性能分析,而输出详细内容则是输出具体的sql、cache key等信息。
除此之外,定义了一些列数据来存储打点信息。
方法方面,最主要是两个方法:record和calculate。我们会在不同的地方去record数据,例如在DB_Query_Builder里面,我们可以这样写:
#start profiling
$sql = $this->_compile_select();
$CI = &get_instance();
$CI->ciprofiling->record(PROFILING_DB, PROFILING_START, $sql);
$result = $this->query($sql);
#end profiling
$CI->ciprofiling->record(PROFILING_DB, PROFILING_END, $sql);
这样,在每条sql查询前后,我们就会记录时间点信息,指定记录类型(PROFILING_DB
),指定时间点类型(PROFILING_START
orPROFILING_END
),以及将这条sql作为key。接着在输出之前,调用calculate来计算这条sql的耗时:
foreach ($this->profiling_types as $profiling_type) {
if (!empty($this->$profiling_type)) {
foreach ($this->$profiling_type as &$item) {
if (empty($item['time1']) || empty($item['time2'])) {
// log
continue;
} else {
$item['time'] = round(($item['time2'] - $item['time1']), 2);
unset($item['time1']);
unset($item['time2']);
}
}
}
}
接着将剖析好的数据放入header:
header('CI-Profiling:' . json_encode(array(
'db' => $this->db_profiling,
'cache' => $this->cache_profiling,
'log' => $this->log_profiling,
'http' => $this->http_profiling,
'total' => $this->total_profiling,
'customized' => $this->customized_profiling,
'url' => $_SERVER['REQUEST_URI']
)));
而当response到达客户端时,我编写的chrome插件,会将header中的剖析数据提取出来,展示到console中,如下所示(在基础CI框架之上,写了一条sql用来测试):
大功告成~