Tracker

Easyswoole提供了一个基础的追踪组件,方便用户实现基础的服务器状态监控,与调用链记录。

组件要求

  • php: >=7.1.0
  • ext-swoole: ^4.4.0
  • easyswoole/component: ^2.0

安装方法

composer require easyswoole/tracker

仓库地址

easyswoole/tracker

调用链

Easyswoole的调用链跟踪是一个以类似有序的树状链表的解构实现的,解构如下:

  1. struct Point{
  2. struct Point* nextPoint;
  3. struct Point[] subPoints;
  4. const END_SUCCESS = 'success';
  5. const END_FAIL = 'fail';
  6. const END_UNKNOWN = 'unknown';
  7. int startTime;
  8. mixed startArg;
  9. int endTime;
  10. string pointName;
  11. string endStatus = self::END_UNKNOWN;
  12. mixed endArg;
  13. string pointId;
  14. string parentId;
  15. int depth = 0;
  16. bool isNext
  17. }

基本使用

  1. use EasySwoole\Tracker\Point;
  2. use EasySwoole\Component\WaitGroup;
  3. use EasySwoole\Tracker\PointContext;
  4. /*
  5. * 假设我们的调用链是这样的
  6. * onRequest ->> actionOne ->> actionOne call remote Api(1,2) ->> afterAction
  7. */
  8. go(function (){
  9. /*
  10. * 创建入口
  11. */
  12. $onRequest = new Point('onRequest');
  13. //记录请求参数,并模拟access log
  14. \co::sleep(0.01);
  15. $onRequest->setStartArg([
  16. 'requestArg' => 'requestArgxxxxxxxx',
  17. 'accessLogId'=>'logIdxxxxxxxxxx'
  18. ]);
  19. //onRequest完成
  20. $onRequest->end();
  21. //进入 next actionOne
  22. $actionOne = $onRequest->next('actionOne');
  23. //action one 进入子环节调用
  24. $waitGroup = new WaitGroup();
  25. //sub pointOne
  26. $waitGroup->add();
  27. $subOne = $actionOne->appendChild('subOne');
  28. go(function ()use($subOne,$waitGroup){
  29. \co::sleep(0.1);
  30. $subOne->end();
  31. $waitGroup->done();
  32. });
  33. //sub pointTwo,并假设失败
  34. $waitGroup->add();
  35. $subTwo = $actionOne->appendChild('subTwo');
  36. go(function ()use($subTwo,$waitGroup){
  37. \co::sleep(1);
  38. $subTwo->end($subTwo::END_FAIL,['failMsg'=>'timeout']);
  39. $waitGroup->done();
  40. });
  41. $waitGroup->wait();
  42. $actionOne->end();
  43. //actionOne结束,进入afterAction
  44. $afterAction = $actionOne->next('afterAction');
  45. //模拟响应记录
  46. \co::sleep(0.01);
  47. $afterAction->end($afterAction::END_SUCCESS,['log'=>'success']);
  48. /*
  49. * 从入口开始打印调用链
  50. */
  51. echo Point::toString($onRequest);
  52. });
  53. //以上代码等价于如下
  54. go(function (){
  55. PointContext::getInstance()->createStart('onRequest')->next('actionOne')->next('afterAction');
  56. //记录请求参数,并模拟access log
  57. \co::sleep(0.01);
  58. PointContext::getInstance()->find('onRequest')->setStartArg([
  59. 'requestArg' => 'requestArgxxxxxxxx',
  60. 'accessLogId'=>'logIdxxxxxxxxxx'
  61. ])->end();
  62. $subOne = PointContext::getInstance()->find('actionOne')->appendChild('subOne');
  63. $subTwo = PointContext::getInstance()->find('actionOne')->appendChild('subTwo');
  64. $waitGroup = new WaitGroup();
  65. $waitGroup->add();
  66. go(function ()use($subOne,$waitGroup){
  67. \co::sleep(0.1);
  68. $subOne->end();
  69. $waitGroup->done();
  70. });
  71. //sub pointTwo,并假设失败
  72. $waitGroup->add();
  73. go(function ()use($subTwo,$waitGroup){
  74. \co::sleep(1);
  75. $subTwo->end($subTwo::END_FAIL,['failMsg'=>'timeout']);
  76. $waitGroup->done();
  77. });
  78. $waitGroup->wait();
  79. PointContext::getInstance()->find('actionOne')->end();
  80. //模拟响应记录
  81. \co::sleep(0.01);
  82. PointContext::getInstance()->find('afterAction')->end(Point::END_SUCCESS,['log'=>'success']);
  83. /*
  84. * 从入口开始打印调用链
  85. */
  86. echo Point::toString(PointContext::getInstance()->startPoint());
  87. });

以上代码输出结果:

  1. #
  2. PointName:onRequest
  3. Status:success
  4. PointId:AoRVFMgrsbNwukBZc7
  5. Depth:0
  6. IsNext:false
  7. Start:1561736477.2808
  8. StartArg:{"requestArg":"requestArgxxxxxxxx","accessLogId":"logIdxxxxxxxxxx"}
  9. End:1561736477.2939
  10. EndArg:null
  11. ChildCount:0
  12. Children:None
  13. NextPoint:
  14. #
  15. PointName:actionOne
  16. Status:success
  17. PointId:2zOWG1SvMbyBcnRmje
  18. Depth:0
  19. IsNext:true
  20. Start:1561736477.2809
  21. StartArg:null
  22. End:1561736478.2993
  23. EndArg:null
  24. ChildCount:2
  25. Children:
  26. #
  27. PointName:subOne
  28. Status:success
  29. PointId:0wU31l8brpfCnXdTxH
  30. Depth:1
  31. IsNext:false
  32. Start:1561736477.2939
  33. StartArg:null
  34. End:1561736477.4006
  35. EndArg:null
  36. ChildCount:0
  37. Children:None
  38. NextPoint:None
  39. #
  40. PointName:subTwo
  41. Status:fail
  42. PointId:Jphr6RD8KSHmYbt70A
  43. Depth:1
  44. IsNext:false
  45. Start:1561736477.2939
  46. StartArg:null
  47. End:1561736478.2993
  48. EndArg:{"failMsg":"timeout"}
  49. ChildCount:0
  50. Children:None
  51. NextPoint:None
  52. NextPoint:
  53. #
  54. PointName:afterAction
  55. Status:success
  56. PointId:oPnGNrkj6qwb381BQl
  57. Depth:0
  58. IsNext:true
  59. Start:1561736477.2809
  60. StartArg:null
  61. End:1561736478.3119
  62. EndArg:{"log":"success"}
  63. ChildCount:0
  64. Children:None
  65. NextPoint:None

如果想以自己的格式记录到数据库,可以具体查看Point实现的方法,每个Point都有自己的Id

进阶使用

HTTP API请求追踪

EasySwooleEvent.php

  1. namespace EasySwoole\EasySwoole;
  2. use EasySwoole\EasySwoole\Swoole\EventRegister;
  3. use EasySwoole\EasySwoole\AbstractInterface\Event;
  4. use EasySwoole\Http\Request;
  5. use EasySwoole\Http\Response;
  6. use EasySwoole\Tracker\Point;
  7. use EasySwoole\Tracker\PointContext;
  8. class EasySwooleEvent implements Event
  9. {
  10. public static function initialize()
  11. {
  12. // TODO: Implement initialize() method.
  13. date_default_timezone_set('Asia/Shanghai');
  14. }
  15. public static function mainServerCreate(EventRegister $register)
  16. {
  17. }
  18. public static function onRequest(Request $request, Response $response): bool
  19. {
  20. $point = PointContext::getInstance()->createStart('onRequest');
  21. $point->setStartArg([
  22. 'uri'=>$request->getUri()->__toString(),
  23. 'get'=>$request->getQueryParams()
  24. ]);
  25. return true;
  26. }
  27. public static function afterRequest(Request $request, Response $response): void
  28. {
  29. $point = PointContext::getInstance()->startPoint();
  30. $point->end();
  31. echo Point::toString($point);
  32. $array = Point::toArray($point);
  33. }
  34. }

Index.php

  1. namespace App\HttpController;
  2. use EasySwoole\Component\WaitGroup;
  3. use EasySwoole\Http\AbstractInterface\Controller;
  4. use EasySwoole\Tracker\PointContext;
  5. class Index extends Controller
  6. {
  7. protected function onRequest(?string $action): ?bool
  8. {
  9. /*
  10. * 调用关系 HttpRequest->OnRequest
  11. */
  12. $point = PointContext::getInstance()->next('ControllerOnRequest');
  13. //假设这里进行了权限验证,并模拟数据库耗时
  14. \co::sleep(0.01);
  15. $point->setEndArg([
  16. 'userId'=>'xxxxxxxxxxx'
  17. ]);
  18. $point->end();
  19. return true;
  20. }
  21. function index()
  22. {
  23. //模拟调用第三方Api,调用关系 OnRequest->sub(subApi1,subApi2)
  24. $actionPoint = PointContext::getInstance()->next('indexAction');
  25. $wait = new WaitGroup();
  26. $subApi = $actionPoint->appendChild('subOne');
  27. $wait->add();
  28. go(function ()use($wait,$subApi){
  29. \co::sleep(1);
  30. $subApi->end();
  31. $wait->done();
  32. });
  33. $subApi = $actionPoint->appendChild('subTwo');
  34. $wait->add();
  35. go(function ()use($wait,$subApi){
  36. \co::sleep(0.3);
  37. $subApi->end($subApi::END_FAIL);
  38. $wait->done();
  39. });
  40. $wait->wait();
  41. $actionPoint->end();
  42. $this->response()->write('hello world');
  43. }
  44. }

以上每次请求会输出如下格式:

  1. #
  2. PointName:onRequest
  3. Status:success
  4. PointId:1561743038GyV4lnus
  5. ParentId:
  6. Depth:0
  7. IsNext:false
  8. Start:1561743038.7011
  9. StartArg:{"uri":"http://127.0.0.1:9501/","get":[]}
  10. End:1561743039.7152
  11. EndArg:null
  12. ChildCount:0
  13. Children:None
  14. NextPoint:
  15. #
  16. PointName:ControllerOnRequest
  17. Status:success
  18. PointId:15617430386f0OQDsS
  19. ParentId:1561743038GyV4lnus
  20. Depth:0
  21. IsNext:true
  22. Start:1561743038.7025
  23. StartArg:null
  24. End:1561743038.713
  25. EndArg:null
  26. ChildCount:0
  27. Children:None
  28. NextPoint:
  29. #
  30. PointName:indexAction
  31. Status:success
  32. PointId:1561743038XEmF0M49
  33. ParentId:15617430386f0OQDsS
  34. Depth:0
  35. IsNext:true
  36. Start:1561743038.7131
  37. StartArg:null
  38. End:1561743039.7151
  39. EndArg:null
  40. ChildCount:2
  41. Children:
  42. #
  43. PointName:subOne
  44. Status:success
  45. PointId:1561743038uIkzYgcS
  46. ParentId:1561743038XEmF0M49
  47. Depth:1
  48. IsNext:false
  49. Start:1561743038.7135
  50. StartArg:null
  51. End:1561743039.7151
  52. EndArg:null
  53. ChildCount:0
  54. Children:None
  55. NextPoint:None
  56. #
  57. PointName:subTwo
  58. Status:fail
  59. PointId:1561743038PslVSY4n
  60. ParentId:1561743038XEmF0M49
  61. Depth:1
  62. IsNext:false
  63. Start:1561743038.7136
  64. StartArg:null
  65. End:1561743039.0149
  66. EndArg:null
  67. ChildCount:0
  68. Children:None
  69. NextPoint:None
  70. NextPoint:None

Api调用链记录

  1. $array = Point::toArray($point);

可以把一个入口点转为一个数组。例如我们可以在MYSQL数据库中存储以下关键结构:

  1. CREATE TABLE `api_tracker_point_list` (
  2. `pointd` varchar(18) NOT NULL,
  3. `pointName` varchar(45) DEFAULT NULL,
  4. `parentId` varchar(18) DEFAULT NULL,
  5. `depth` int(11) NOT NULL DEFAULT '0',
  6. `isNext` int(11) NOT NULL DEFAULT '0',
  7. `startTime` varchar(14) NOT NULL,
  8. `endTime` varchar(14) DEFAULT NULL,
  9. `status` varchar(10) NOT NULL,
  10. PRIMARY KEY (`pointd`),
  11. UNIQUE KEY `trackerId_UNIQUE` (`pointd`)
  12. ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

其余请求参数可以自己记录。

核心字段在pointId,parentId与isNext,status 这四个个字段,例如,我想得到哪次调用链超时,那么就是直接

  1. where status = fail

如果想看哪次调用耗时多少,那么可以

  1. where spendTime > 3

spendTime 是用startTime和endTime计算

相关仓库

EasySwoole之链路追踪 简单demo