链路追踪php,easyswoole链路追踪-程序员宅基地

技术标签: 链路追踪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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_33755379/article/details/116184153

智能推荐

Python 函数定义及调用(1)_python中函数返回一个可能不存在的变量该怎么写-程序员宅基地

文章浏览阅读875次,点赞10次,收藏2次。1、函数的功能:(1)代码的一种组织形式;(2)一个函数一般完成一项特定的功能。2、函数使用(1)函数需要先定义(2)使用函数,俗称调用3、函数定义的一般规则(1)def 关键字,后跟一个空格;(2)函数名,自己定义,起名需要遵循便令命名规则,约定俗成,大驼峰命名方法;(3)后面括号和冒号不能省,括号内可以由参数;(4)函数内所有代码缩进。..._python中函数返回一个可能不存在的变量该怎么写

探索高效数据结构与算法:elarity/data-structure-php-clanguage-程序员宅基地

文章浏览阅读323次,点赞5次,收藏8次。探索高效数据结构与算法:elarity/data-structure-php-clanguage项目地址:https://gitcode.com/elarity/data-structure-php-clanguage在这个数字化的时代,对数据结构和算法的理解是每个开发者不可或缺的技能。今天我们要推荐的是一个开源项目——elarity/data-structure-php-clanguage,...

2008最新全套计算机毕业设计_计算机毕业设计下载-程序员宅基地

文章浏览阅读3k次,点赞3次,收藏6次。计算机应用,计算机信息管理, 计算机网络, 计算机软件, 计算机科学与技术毕业设计网主要向广大计算机专业的各类本、专科毕业生(包括全日制正规高校、自考、各级电大、夜大、函授、成人教育等类型的计算机应用,计算机信息管理, 计算机网络, 计算机软件, 计算机科学与技术,电子商务等方向),提供计算机毕业设计论文参考资料(包括计算机论文、计算机毕业论文、计算机毕业设计)、免费的计算_计算机毕业设计下载

计算机网络---考前最后一背_幕布 计算机网络-程序员宅基地

文章浏览阅读4.6k次,点赞8次,收藏66次。1. 计算机网络向用户提供的最重要的两大功能:连通和共享。2. 计算机网络就是为数据交换提供服务的,是作为提供数据发送、传输、接收服务的基础设施。3. 定义了通信实体之间交换的报文的格式和传输顺序,以及在报文发送和/或接收或者其他事件方面所采取的行动(响应)。协议的基本要素:语法、语义和同步4. 网络按照其位置和提供的功能,可以划分为两个大的部分:位于网路边缘的资源子网,和网络核心通信子网。5. ETF 组织发布网络标准化文档的形式是 RFC 文档。6. 端到_幕布 计算机网络

【ElasticSearch系列-08】ElasticSearch处理对象间的关联关系_xcontentfactory.jsonbuilder().startobject() .start-程序员宅基地

文章浏览阅读973次,点赞27次,收藏29次。深入理解es对象间的关联关系_xcontentfactory.jsonbuilder().startobject() .startobject 对象类型

tp6加载静态资源文件_tp6访问静态资源-程序员宅基地

文章浏览阅读7.1k次。1.应用目录下新建config目录,在config目录下新建template.php文件2.return [ 'tplreplacestring' => [ 'STATIC'=>'/static', ],];3.将姿态资源文件放入public 目录下即可..._tp6访问静态资源

随便推点

【Python Practice】汇总-程序员宅基地

文章浏览阅读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..._适用于递归实现的算法有

python高斯噪声怎么去除_用Python识别验证码-程序员宅基地

文章浏览阅读300次。很多网站登录都需要输入验证码,如果要实现自动登录就不可避免的要识别验证码。最近学习了一下图像处理相关的知识,并用Python实现了基于KNN的验证码识别。准备工作这里我们使用opencv做图像处理,所以需要安装下面两个库pip3 install opencv-pythonpip3 install numpy识别原理我们采取一种有监督式学习的方法来识别验证码,包含以下几个步骤图片处理 - 对图片进..._python 高斯噪音识别

2017级算法模拟上机准备篇(序列DP 进阶_1)-程序员宅基地

文章浏览阅读92次。进阶版的序列DP 从一道题的优化开始ModricWang的序列问题题目描述:给定一个序列,求出这个序列中的最长上升子序列的长度。这道题的本质还是求解一个最长上升子序列的问题相对与之前提到过的O(n^2)的算法 我们可以重新整理思路用O(nlogn)的思路来写,用贪心和二分优化之前的算法我们设置新的DP数组//dp[i]代表的是当前长度为i的上升子序列的末尾元素的大...

XSS攻击基础防御_xxs全防1.0免费-程序员宅基地

文章浏览阅读3.4k次。XSS攻击听说过,没见过,后来通过查资料了解一点,这篇文章中,主要是针对XSS攻击做一个基本的防御,我也不知道能不能防的住,防不住在加规则,中国式解决问题:哪疼医那。哈哈由于公司用的是 SpringMVC,因此,这次就主要基于 SpringMVC 来解决这些漏洞。当然,其实这些解决方案都是大同小异,对于什么环境来说根本无所谓。了解了原理,什么环境、什么语言都可以运用自如了。废话就不多说了,直接上解..._xxs全防1.0免费

如何将JPG格式的图片转化为带地理坐标的TIFF格式-程序员宅基地

文章浏览阅读1.9k次。2019独角兽企业重金招聘Python工程师标准>>> ..._图片转为tif格式怎么附加坐标