链路追踪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

智能推荐

Hadoop集群如何启动_hadoop集群启动命令-程序员宅基地

文章浏览阅读3.4k次。辅助namenode上的resourcemanager通过start-all.sh可能启动不了,需要进入辅namenode上手动启动(/opt/app/hadoop/sbin/hadoop-daemon.sh startresourcemanager)/opt/app/zookeeper-3.4.6/bin/zkServer.sh start(启动)/opt/app/hadoop/sbin/start-all.sh(启动)/opt/app/hadoop/sbin/start-all.sh(停止)_hadoop集群启动命令

打开别人的工程提示IAR版本不兼容问题_win7 iar 8.4无法加载工程-程序员宅基地

文章浏览阅读718次。打开别人的工程提示IAR版本不兼容问题IAR 版本不兼容问题IAR 版本不兼容问题我使用的是 STM32CUBEMX 生成的IAR工程,我自己的是 IAR 版本 8.4,打开别人的工程 8.3版本,但是提示我打开的工程版本更高,可能是IAR的一个bug吧,把cube 生成的 IAR 工程删除 然后再重新生成一个工程就好了。..._win7 iar 8.4无法加载工程

使用VMware安装Ubuntu虚拟机 - 完整教程-程序员宅基地

文章浏览阅读4.8w次,点赞89次,收藏590次。使用 VMware Workstation 16 Pro 安装 Ubuntu 虚拟机 - 完整教程_vmware安装ubuntu

商业WIFI,移动互联网的一大超级入口-程序员宅基地

文章浏览阅读116次。在2016中国互联网大会之中国WiFi应用创新大会上,中国互联网协会开发者核心价值小组秘书长于立娟发表了精彩的开幕致辞,她指出随着移动上网终端的多元化,WiFi应用已经成为移动互联网时代继浏览器、超级APP之后的又一大超级入口。 于立娟表示,“WiFi如果仅仅停留在底层入口,就会像3G...

前端常用网站-程序员宅基地

文章浏览阅读756次。MDNhttps://developer.mozilla.org/zh-CN/vue3https://v3.cn.vuejs.org/api/tshttps://www.typescriptlang.org/docs/handbook/2/keyof-types.htmlvitehttps://staging-cn.vitejs.dev/微信小程序开发文档https://developers.weixin.qq.com/miniprogram/dev/framework/微信公众平台h

php中比rbac更好的权限认证的方式auth认证类_php 节点认证-程序员宅基地

文章浏览阅读295次。RBAC是按节点进行认证的,如果要控制比节点更细的权限就有点困难了,比如页面上面的操作按钮, 我想判断用户权限来显示这个按钮, 如果没有权限就不会显示这个按钮; 再比如我想按积分进行权限认证, 积分在0-100时能干什么, 在101-200时能干什么。 这些权限认证用RABC都很困难。 下面介绍 Auth权限认证, 它几乎是全能的, 除了能进行节点认证, 上面说的RABC很难认证的两种情况,它都能_php 节点认证

随便推点

深入理解Java中的HashMap:工作原理、实现方式与使用案例分析_java中hashmap-程序员宅基地

文章浏览阅读1.6k次,点赞3次,收藏9次。HashMap是一种非常常见和实用的数据结构,它被广泛应用于Java编程中。在本文中,我们将深入探讨HashMap的工作原理、实现方式和使用案例,以帮助读者更好地理解和应用这一数据结构。_java中hashmap

HotSpot 垃圾回收-程序员宅基地

文章浏览阅读591次,点赞26次,收藏8次。主要概括了hotspot的gc机制

病毒分析之撒旦(Satan)勒索病毒分析解密(AES256 ECB算法)_撒旦查询恶意-程序员宅基地

文章浏览阅读2.9k次,点赞3次,收藏10次。病毒分析之撒旦(Satan)勒索病毒分析解密(AES256 ECB算法)0x0病毒概况撒旦勒索病毒主要是针对企业服务器用户进行感染加密其服务器上的文件并勒索用户的一种病毒。撒旦病毒通过大量漏洞利用工具,扫描入侵主机。成功入侵后加密重要文件。并生成文件用中英韩三种语言提示索要比特币赎金解密文件。0x1恶意行为1.主模块会释放大量攻击模块并执行,也会连接恶意IP下载病毒文件..._撒旦查询恶意

在Ubuntu20.04配置PX4环境_ubuntu 24.04 ppeizhi-程序员宅基地

文章浏览阅读932次,点赞7次,收藏8次。pwd=5npl 提取码:5npl。找到“软件和更新”,选择aliyun服务器,——>【关闭】——>【重新载入】,等待进度条完毕就更管成功。出现“Configuring incomplete, errors occurred!",这是由于没有将软件源改为国内软件源,如果意外退出下载,第二次进入下载的时候缓存会被锁住。在主目录下打开终端,运行以下命令,_ubuntu 24.04 ppeizhi

关于报错“! [rejected]master -> master (non-fast-forward)”的解决方法_efs/heads/master:refs/heads/master[rejected] (non--程序员宅基地

文章浏览阅读3.2w次,点赞18次,收藏58次。在遇到上述错误自己无解决时,于是开始了网上大搜索:git push -u origin master 上面命令将本地的master分支推送到origin主机,同时指定origin为默认主机,后面就可以不加任何参数使用git push了首先尝试了 命令git push -u origin master -f,然后惊喜地出现了下面错误,不能强制推送到受保护的分支随后就又找到了一个方法:先使用..._efs/heads/master:refs/heads/master[rejected] (non-fast-forward)

C# 多线程详细讲解-程序员宅基地

文章浏览阅读2.4w次,点赞68次,收藏446次。C#多线程一、基本概念1、进程首先打开任务管理器,查看当前运行的进程:从任务管理器里面可以看到当前所有正在运行的进程。那么究竟什么是进程呢?进程(Process)是Windows系统中的一个基本概念,它包含着一个运行程序所需要的资源。一个正在运行的应用程序在操作系统中被视为一个进程,进程可以包括一个或多个线程。线程是操作系统分配处理器时间的基本单元,在进程中可以有多个线程同时执行代码。进程之间是相对独立的,一个进程无法访问另一个进程的数据(除非利用分布式计算方式),一个进程运_c# 多线程

推荐文章

热门文章

相关标签