Hyperf下免费版Swoole Tracker使用
公司使用hyperf框架搭建的微服务平台,内存泄露问题十分严重,在排查期间也使用了
gc_mem_caches
回收内存,但是作用不大,所以后面还是采用免费版的Swoole Tracker
作为内存检测工具,帮助排查内存问题;Hyperf的官方文档给的也不是很详细,安装过程也是踩了一些坑;
1. 首先要从官网获取最新的swoole_tracker
包
* 地址 https://business.swoole.com/SwooleTracker/apply
* 注意使用和php环境版本一致的 swoole_tracker.so 文件
2. Hyperf
一般使用docker
作为开发容器,这里需要把swoole_tracker相关的扩展以及ini配置打包进dockerfile
;
[swoole_tracker]
;Tracker从v3.3.0版本开始修改为了Zend扩展
zend_extension=swoole_tracker.so
;打开总开关 tracker.enable和tracker.enable_malloc_hook不能同时开启
tracker.enable=0
;采样率 例如:100%
tracker.sampling_rate=100
;开启内存泄漏检测时添加 默认0 关闭状态
tracker.enable_memcheck=1
;Leak检测开关
tracker.enable_malloc_hook=1
# DockerFile需要增加的内容
ADD ./swoole_tracker80.so /tmp
RUN cd /tmp \
&& cp /tmp/swoole_tracker80.so /usr/lib/php8/modules/swoole_tracker.so \
&& echo "zend_extension=swoole_tracker.so" >> /etc/php8/conf.d/swoole_tracker.ini \
&& echo "tracker.enable=0" >> /etc/php8/conf.d/swoole_tracker.ini \
&& echo "tracker.sampling_rate=100" >> /etc/php8/conf.d/swoole_tracker.ini \
&& echo "tracker.enable_memcheck=1" >> /etc/php8/conf.d/swoole_tracker.ini \
&& echo "tracker.enable_malloc_hook=1" >> /etc/php8/conf.d/swoole_tracker.ini \
下载好的文件直接放在项目根目录
值得注意的是hyperf官方文档
dockerfile
配置中有一个swoole-tracker-install.sh
文件,免费版用不到这个文件,所以我们忽略掉即可;
3. 依赖组件
composer
安装
composer require hyperf/swoole-tracker
在 config/autoload/aspects.php
配置中间件
return [
'http' => [
// 这个HttpServerMiddleware就是一个坑,引入后会造成一个报错,在这浪费了很多时间。希望有大佬解答下,是什么地方的问题。在此,我们不引入这个扩展
// Hyperf\SwooleTracker\Middleware\HttpServerMiddleware::class,
/* 如果需要在 Hyperf 中检测 HTTP Server 中的内存泄漏,就需要此全局中间件 */
Hyperf\SwooleTracker\Middleware\HookMallocMiddleware::class
],
];
ps:引入
Hyperf\SwooleTracker\Middleware\HttpServerMiddleware::class
后会产生的错误
[INFO] HTTP Server listening at 0.0.0.0:9501
zend_mm_heap corrupted
[2023-03-03 13:46:39 $67.0] WARNING Server::check_worker_exit_status(): worker(pid=68, id=0) abnormal exit, status=1, signal=0
如果创建目录有以下报错 就需要手动创建目录或者在dockerfile中尝试命令创建
PHP Fatal error: PHP Startup: swoole_tracker extension ERROR: mkdir /opt/swoole/var/run/swoole_tracker/ error,make sure that start the agent first or start your php with root permission(No such file or directory)// 创建目录命令
mkdir -p /opt/swoole/var/run/swoole_tracker/
4.测试
- 在
app/Controller/IndexController.php
文件中写一个内存泄漏函数,并调用
/**
* 内存泄露测试
*/
public function demo()
{
$parallel = new Parallel();
$parallel->add(function () {
$this->foo();
sleep(1);
return Coroutine::id();
});
$parallel->add(function () {
$this->foo();
sleep(1);
return Coroutine::id();
});
try{
return $parallel->wait();
} catch(ParallelExecutionException $e){
print_r($e->getResults()); // $e->getResults() 获取协程中的返回值。
print_r($e->getThrowables());// $e->getThrowables() 获取协程中出现的异常。
return "exception";
}
}
/**
* 内存泄露函数
*/
public function foo()
{
//如果不需要全局检测 就需要在主函数加上 trackerHookMalloc 用来测试
//上面的中间件配置的有全局中间件 Hyperf\SwooleTracker\Middleware\HookMallocMiddleware::class 所以此处不需要单独标记主函数
//trackerHookMalloc(); //标记主函数,开始hook malloc
static $arr = [];
$arr[] = str_repeat("big string", 1024);
$GLOBAL['g_arr'][] = str_repeat("big string", 1024);
}
- 运行项目访问
demo
方法。http://127.0.0.1:9501/index/demo
- 查看泄露结果。
Cli
命令行调用trackerAnalyzeLeak()
函数即可分析泄漏日志,生成泄漏报告;可以直接php -r "trackerAnalyzeLeak();"
Cli
命令行调用trackerCleanLeak()
函数即可可以清除泄漏日志,重新开始;可以直接php -r "trackerCleanLeak();"
5.结果分析
泄漏的信息默认在 /tmp/trackerleak
日志里面
下面是泄漏报告的格式:
- 没有内存泄漏的情况:
[217 (Loop 5)] ✅ Nice!! No Leak Were Detected In This Loop
其中217
表示进程 id
,Loop 5
表示第 5
次调用主函数生成的泄漏信息
- 有确定的内存泄漏的情况:
[217 (Loop 6)] /data/hyperf-skeleton/vendor/hyperf/di/src/ClosureDefinitionCollector.php:23 => [256]
[217 (Loop 6)] /data/hyperf-skeleton/runtime/container/proxy/App_Controller_IndexController.proxy.php:64 => [24576]
[217 (Loop 6)] ❌ This Loop TotalLeak: [24832]
表示第6
次调用ClosureDefinitionCollector.php
的23
行,泄露了256
字节内存;调用IndexController.php
的 64
行,泄漏了24576
字节内存,总共泄漏了 24832
字节内存。
跨 loop 分析:有时本次 Loop 的泄漏会在下次释放掉,Leak工具会跨相邻 2 个Loop 进行分析,自动对冲泄漏信息,如果是跨多个 Loop 的释放,会以如下格式输出:
[195 (Loop 7)] /opt/www/vendor/hyperf/load-balancer/src/AbstractLoadBalancer.php:76 => [-2640]
Free Pre (Loop 5) : /opt/www/vendor/hyperf/utils/src/Codec/Json.php:49 => [360]
Free Pre (Loop 5) : /opt/www/vendor/hyperf/rpc-client/src/AbstractServiceClient.php:210 => [2280]
上述信息表示 Loop 7
释放了 Loop 5
的 360
+2280
字节内存,如果只算这里,也是没有内存泄漏。
注意事项
- 前几次 Loop 的泄漏信息不用管,因为大部分项目都有一些初始化的缓存是不释放的,所以可以先清空下记录。
- 检测期间尽量不要有并发。
- 由于开启泄漏检测后性能会非常差,不要在
php.ini
中开启apm.enable_malloc_hook = 1
压测。 - 和 Swoole Tracker2.x 的检查泄漏原理不一样,不能一起用。
- 一个进程只能有一个地方调用
trackerHookMalloc()
函数。 - Swoole4.5.3由于底层 api 有问题,Leak工具无法正常工作,请升级到最新版Swoole或者降级Swoole版本。
一些相关命令
查找PHP扩展目录的位置,
php -i | grep extension_dir
查看swoole和swoole_track版本
php --ri swoole
php --ri swoole_tracker
查询基础环境
uname -a
查询php版本
php -v
cp拷贝命令
cp 源头文件 目标文件
apache的ab压测
ab -n 500 -c 2000 http://10.2.1.28:9501/index/index
docker build
docker build -t swoole_admin_docker:v0.1 .
发表评论 取消回复