技术标签: 链路追踪php
Tracker
Easyswoole提供了一个基础的追踪组件,方便用户实现基础的服务器状态监控,与调用链记录。
组件要求
php: >=7.1.0
ext-swoole: ^4.4.0
easyswoole/component: ^2.0
安装方法
composer require easyswoole/tracker
仓库地址
调用链
Easyswoole的调用链跟踪是一个以类似有序的树状链表的解构实现的,解构如下:
struct Point{
struct Point* nextPoint;
struct Point[] subPoints;
const END_SUCCESS = 'success';
const END_FAIL = 'fail';
const END_UNKNOWN = 'unknown';
int startTime;
mixed startArg;
int endTime;
string pointName;
string endStatus = self::END_UNKNOWN;
mixed endArg;
string pointId;
string parentId;
int depth = 0;
bool isNext
}
基本使用
use EasySwoole\Tracker\Point;
use EasySwoole\Component\WaitGroup;
use EasySwoole\Tracker\PointContext;
/*
* 假设我们的调用链是这样的
* onRequest ->> actionOne ->> actionOne call remote Api(1,2) ->> afterAction
*/
go(function (){
/*
* 创建入口
*/
$onRequest = new Point('onRequest');
//记录请求参数,并模拟access log
\co::sleep(0.01);
$onRequest->setStartArg([
'requestArg' => 'requestArgxxxxxxxx',
'accessLogId'=>'logIdxxxxxxxxxx'
]);
//onRequest完成
$onRequest->end();
//进入 next actionOne
$actionOne = $onRequest->next('actionOne');
//action one 进入子环节调用
$waitGroup = new WaitGroup();
//sub pointOne
$waitGroup->add();
$subOne = $actionOne->appendChild('subOne');
go(function ()use($subOne,$waitGroup){
\co::sleep(0.1);
$subOne->end();
$waitGroup->done();
});
//sub pointTwo,并假设失败
$waitGroup->add();
$subTwo = $actionOne->appendChild('subTwo');
go(function ()use($subTwo,$waitGroup){
\co::sleep(1);
$subTwo->end($subTwo::END_FAIL,['failMsg'=>'timeout']);
$waitGroup->done();
});
$waitGroup->wait();
$actionOne->end();
//actionOne结束,进入afterAction
$afterAction = $actionOne->next('afterAction');
//模拟响应记录
\co::sleep(0.01);
$afterAction->end($afterAction::END_SUCCESS,['log'=>'success']);
/*
* 从入口开始打印调用链
*/
echo Point::toString($onRequest);
});
//以上代码等价于如下
go(function (){
PointContext::getInstance()->createStart('onRequest')->next('actionOne')->next('afterAction');
//记录请求参数,并模拟access log
\co::sleep(0.01);
PointContext::getInstance()->find('onRequest')->setStartArg([
'requestArg' => 'requestArgxxxxxxxx',
'accessLogId'=>'logIdxxxxxxxxxx'
])->end();
$subOne = PointContext::getInstance()->find('actionOne')->appendChild('subOne');
$subTwo = PointContext::getInstance()->find('actionOne')->appendChild('subTwo');
$waitGroup = new WaitGroup();
$waitGroup->add();
go(function ()use($subOne,$waitGroup){
\co::sleep(0.1);
$subOne->end();
$waitGroup->done();
});
//sub pointTwo,并假设失败
$waitGroup->add();
go(function ()use($subTwo,$waitGroup){
\co::sleep(1);
$subTwo->end($subTwo::END_FAIL,['failMsg'=>'timeout']);
$waitGroup->done();
});
$waitGroup->wait();
PointContext::getInstance()->find('actionOne')->end();
//模拟响应记录
\co::sleep(0.01);
PointContext::getInstance()->find('afterAction')->end(Point::END_SUCCESS,['log'=>'success']);
/*
* 从入口开始打印调用链
*/
echo Point::toString(PointContext::getInstance()->startPoint());
});
以上代码输出结果:
#
PointName:onRequest
Status:success
PointId:AoRVFMgrsbNwukBZc7
Depth:0
IsNext:false
Start:1561736477.2808
StartArg:{"requestArg":"requestArgxxxxxxxx","accessLogId":"logIdxxxxxxxxxx"}
End:1561736477.2939
EndArg:null
ChildCount:0
Children:None
NextPoint:
#
PointName:actionOne
Status:success
PointId:2zOWG1SvMbyBcnRmje
Depth:0
IsNext:true
Start:1561736477.2809
StartArg:null
End:1561736478.2993
EndArg:null
ChildCount:2
Children:
#
PointName:subOne
Status:success
PointId:0wU31l8brpfCnXdTxH
Depth:1
IsNext:false
Start:1561736477.2939
StartArg:null
End:1561736477.4006
EndArg:null
ChildCount:0
Children:None
NextPoint:None
#
PointName:subTwo
Status:fail
PointId:Jphr6RD8KSHmYbt70A
Depth:1
IsNext:false
Start:1561736477.2939
StartArg:null
End:1561736478.2993
EndArg:{"failMsg":"timeout"}
ChildCount:0
Children:None
NextPoint:None
NextPoint:
#
PointName:afterAction
Status:success
PointId:oPnGNrkj6qwb381BQl
Depth:0
IsNext:true
Start:1561736477.2809
StartArg:null
End:1561736478.3119
EndArg:{"log":"success"}
ChildCount:0
Children:None
NextPoint:None
如果想以自己的格式记录到数据库,可以具体查看Point实现的方法,每个Point都有自己的Id
进阶使用
HTTP API请求追踪
EasySwooleEvent.php
namespace EasySwoole\EasySwoole;
use EasySwoole\EasySwoole\Swoole\EventRegister;
use EasySwoole\EasySwoole\AbstractInterface\Event;
use EasySwoole\Http\Request;
use EasySwoole\Http\Response;
use EasySwoole\Tracker\Point;
use EasySwoole\Tracker\PointContext;
class EasySwooleEvent implements Event
{
public static function initialize()
{
// TODO: Implement initialize() method.
date_default_timezone_set('Asia/Shanghai');
}
public static function mainServerCreate(EventRegister $register)
{
}
public static function onRequest(Request $request, Response $response): bool
{
$point = PointContext::getInstance()->createStart('onRequest');
$point->setStartArg([
'uri'=>$request->getUri()->__toString(),
'get'=>$request->getQueryParams()
]);
return true;
}
public static function afterRequest(Request $request, Response $response): void
{
$point = PointContext::getInstance()->startPoint();
$point->end();
echo Point::toString($point);
$array = Point::toArray($point);
}
}
Index.php
namespace App\HttpController;
use EasySwoole\Component\WaitGroup;
use EasySwoole\Http\AbstractInterface\Controller;
use EasySwoole\Tracker\PointContext;
class Index extends Controller
{
protected function onRequest(?string $action): ?bool
{
/*
* 调用关系 HttpRequest->OnRequest
*/
$point = PointContext::getInstance()->next('ControllerOnRequest');
//假设这里进行了权限验证,并模拟数据库耗时
\co::sleep(0.01);
$point->setEndArg([
'userId'=>'xxxxxxxxxxx'
]);
$point->end();
return true;
}
function index()
{
//模拟调用第三方Api,调用关系 OnRequest->sub(subApi1,subApi2)
$actionPoint = PointContext::getInstance()->next('indexAction');
$wait = new WaitGroup();
$subApi = $actionPoint->appendChild('subOne');
$wait->add();
go(function ()use($wait,$subApi){
\co::sleep(1);
$subApi->end();
$wait->done();
});
$subApi = $actionPoint->appendChild('subTwo');
$wait->add();
go(function ()use($wait,$subApi){
\co::sleep(0.3);
$subApi->end($subApi::END_FAIL);
$wait->done();
});
$wait->wait();
$actionPoint->end();
$this->response()->write('hello world');
}
}
以上每次请求会输出如下格式:
#
PointName:onRequest
Status:success
PointId:1561743038GyV4lnus
ParentId:
Depth:0
IsNext:false
Start:1561743038.7011
StartArg:{"uri":"http://127.0.0.1:9501/","get":[]}
End:1561743039.7152
EndArg:null
ChildCount:0
Children:None
NextPoint:
#
PointName:ControllerOnRequest
Status:success
PointId:15617430386f0OQDsS
ParentId:1561743038GyV4lnus
Depth:0
IsNext:true
Start:1561743038.7025
StartArg:null
End:1561743038.713
EndArg:null
ChildCount:0
Children:None
NextPoint:
#
PointName:indexAction
Status:success
PointId:1561743038XEmF0M49
ParentId:15617430386f0OQDsS
Depth:0
IsNext:true
Start:1561743038.7131
StartArg:null
End:1561743039.7151
EndArg:null
ChildCount:2
Children:
#
PointName:subOne
Status:success
PointId:1561743038uIkzYgcS
ParentId:1561743038XEmF0M49
Depth:1
IsNext:false
Start:1561743038.7135
StartArg:null
End:1561743039.7151
EndArg:null
ChildCount:0
Children:None
NextPoint:None
#
PointName:subTwo
Status:fail
PointId:1561743038PslVSY4n
ParentId:1561743038XEmF0M49
Depth:1
IsNext:false
Start:1561743038.7136
StartArg:null
End:1561743039.0149
EndArg:null
ChildCount:0
Children:None
NextPoint:None
NextPoint:None
Api调用链记录
$array = Point::toArray($point);
可以把一个入口点转为一个数组。例如我们可以在MYSQL数据库中存储以下关键结构:
CREATE TABLE `api_tracker_point_list` (
`pointd` varchar(18) NOT NULL,
`pointName` varchar(45) DEFAULT NULL,
`parentId` varchar(18) DEFAULT NULL,
`depth` int(11) NOT NULL DEFAULT '0',
`isNext` int(11) NOT NULL DEFAULT '0',
`startTime` varchar(14) NOT NULL,
`endTime` varchar(14) DEFAULT NULL,
`status` varchar(10) NOT NULL,
PRIMARY KEY (`pointd`),
UNIQUE KEY `trackerId_UNIQUE` (`pointd`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
其余请求参数可以自己记录。
核心字段在pointId,parentId与isNext,status 这四个个字段,例如,我想得到哪次调用链超时,那么就是直接
where status = fail
如果想看哪次调用耗时多少,那么可以
where spendTime > 3
spendTime 是用startTime和endTime计算
相关仓库
EasySwoole之链路追踪 简单demo
文章浏览阅读875次,点赞10次,收藏2次。1、函数的功能:(1)代码的一种组织形式;(2)一个函数一般完成一项特定的功能。2、函数使用(1)函数需要先定义(2)使用函数,俗称调用3、函数定义的一般规则(1)def 关键字,后跟一个空格;(2)函数名,自己定义,起名需要遵循便令命名规则,约定俗成,大驼峰命名方法;(3)后面括号和冒号不能省,括号内可以由参数;(4)函数内所有代码缩进。..._python中函数返回一个可能不存在的变量该怎么写
文章浏览阅读323次,点赞5次,收藏8次。探索高效数据结构与算法:elarity/data-structure-php-clanguage项目地址:https://gitcode.com/elarity/data-structure-php-clanguage在这个数字化的时代,对数据结构和算法的理解是每个开发者不可或缺的技能。今天我们要推荐的是一个开源项目——elarity/data-structure-php-clanguage,...
文章浏览阅读3k次,点赞3次,收藏6次。计算机应用,计算机信息管理, 计算机网络, 计算机软件, 计算机科学与技术毕业设计网主要向广大计算机专业的各类本、专科毕业生(包括全日制正规高校、自考、各级电大、夜大、函授、成人教育等类型的计算机应用,计算机信息管理, 计算机网络, 计算机软件, 计算机科学与技术,电子商务等方向),提供计算机毕业设计论文参考资料(包括计算机论文、计算机毕业论文、计算机毕业设计)、免费的计算_计算机毕业设计下载
文章浏览阅读4.6k次,点赞8次,收藏66次。1. 计算机网络向用户提供的最重要的两大功能:连通和共享。2. 计算机网络就是为数据交换提供服务的,是作为提供数据发送、传输、接收服务的基础设施。3. 定义了通信实体之间交换的报文的格式和传输顺序,以及在报文发送和/或接收或者其他事件方面所采取的行动(响应)。协议的基本要素:语法、语义和同步4. 网络按照其位置和提供的功能,可以划分为两个大的部分:位于网路边缘的资源子网,和网络核心通信子网。5. ETF 组织发布网络标准化文档的形式是 RFC 文档。6. 端到_幕布 计算机网络
文章浏览阅读973次,点赞27次,收藏29次。深入理解es对象间的关联关系_xcontentfactory.jsonbuilder().startobject() .startobject 对象类型
文章浏览阅读7.1k次。1.应用目录下新建config目录,在config目录下新建template.php文件2.return [ 'tplreplacestring' => [ 'STATIC'=>'/static', ],];3.将姿态资源文件放入public 目录下即可..._tp6访问静态资源
文章浏览阅读420次。所有练习题,来自这个仓库:https://github.com/darkprinx/100-plus-Python-programming-exercises-extended习题汇总 Day 1- Question 1-3 Day 2- Question 4-9 Day 3- Question 10-13 Day 4- Question 14-15 Day 5- Question 16-17 Day 6- Question 18-19 Day 7- Que._python practice
文章浏览阅读90次。斐波那契数列定义:斐波那契数列指的是这样一个数列 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368........,这个数列从第3项开始,每一项都等于前两项之和。斐波那契数列又称黄金分割数列、因数学家列昂纳多·斐波那契(Le..._适用于递归实现的算法有
文章浏览阅读300次。很多网站登录都需要输入验证码,如果要实现自动登录就不可避免的要识别验证码。最近学习了一下图像处理相关的知识,并用Python实现了基于KNN的验证码识别。准备工作这里我们使用opencv做图像处理,所以需要安装下面两个库pip3 install opencv-pythonpip3 install numpy识别原理我们采取一种有监督式学习的方法来识别验证码,包含以下几个步骤图片处理 - 对图片进..._python 高斯噪音识别
文章浏览阅读92次。进阶版的序列DP 从一道题的优化开始ModricWang的序列问题题目描述:给定一个序列,求出这个序列中的最长上升子序列的长度。这道题的本质还是求解一个最长上升子序列的问题相对与之前提到过的O(n^2)的算法 我们可以重新整理思路用O(nlogn)的思路来写,用贪心和二分优化之前的算法我们设置新的DP数组//dp[i]代表的是当前长度为i的上升子序列的末尾元素的大...
文章浏览阅读3.4k次。XSS攻击听说过,没见过,后来通过查资料了解一点,这篇文章中,主要是针对XSS攻击做一个基本的防御,我也不知道能不能防的住,防不住在加规则,中国式解决问题:哪疼医那。哈哈由于公司用的是 SpringMVC,因此,这次就主要基于 SpringMVC 来解决这些漏洞。当然,其实这些解决方案都是大同小异,对于什么环境来说根本无所谓。了解了原理,什么环境、什么语言都可以运用自如了。废话就不多说了,直接上解..._xxs全防1.0免费
文章浏览阅读1.9k次。2019独角兽企业重金招聘Python工程师标准>>> ..._图片转为tif格式怎么附加坐标