mingyuanyun/ggcp-http

基于 GuzzleHttp 封装的 HTTP 请求服务,可通过服务配置开启调链追踪分析、监控预警

v1.3.4 2021-11-01 07:22 UTC

README

云链公共产品组提供的跨应用调用链追踪扩展支持,底层基于 GuzzleHttp 扩展进行的相关接口调用封装,可以自动识别打通与 iLog 的数据关联

支持

  1. 自动寻找并利用 iLog 建立不同系统服务间调用的关联,无需业务手动设置每次服务调用的调用链数据
  2. 支持多线程并发调用。遇到多个乱序服务调用的情况,可以有效提升服务调用的性能
  3. 自动采集服务调用过程中的请求、响应以及网络传输过程的各种指标数据

性能测试

先放一份简单的性能分析对比,每种请求的测试场景均为发起 100 个请求,其中 CURL 的原生调用采用日常开发中常见的逻辑简单封装实现:

调用方式GgcpHttp 总耗时CURL 总耗时GgcpHttp 平均单个接口耗时CURL 平均单个接口耗时
同步发起 100 个 Get 请求2880.729ms10729.794ms28.807ms107.297ms
同步发起 100 个 Post 请求19008.264ms33206.679ms190.082ms332.066ms
异步发起 100 个 Get 请求1341.636ms-13.416ms-
异步发起 100 个 Post 请求1557.712ms-15.577ms-

配置 && 使用

准备数据采集器

服务调用所需采集的数据,需要通过采集器统一采集到指定的存储空间中,如未提供采集器配置,且通过默认配置无法初始化采集器的话,将无法正常采集所需的数据。目前仅支持数据统一采集存储到 MongoDB

# 在合适的位置做好采集器的初始化工作
$config = [
    'engine' => 'mongo',
    'options' => [
        'host' => 'mongodb://127.0.0.1:27017',
        'username' => 'root', // 默认无账号密码
        'password' => '123',
    ],
];
\GgcpHttp\Client::initCollection($config);

你也可以在需要发起服务调用的时候再提供采集器配置

$config = [
    'engine' => 'mongo',
    'options' => [
        'host' => 'mongodb://127.0.0.1:27017',
        'username' => 'root', // 默认无账号密码
        'password' => '123',
    ],
];
\GgcpHttp\Client::prepare(['collection' => $config])->getSender()->get('https://www.domain.com/api/get');

发起请求

$sender = \GgcpHttp\Client::prepare()->getSender();

$bodyData = ['id' => 1, 'type' => 2];

// 发起 GET 请求
$response = $sender->get('https://www.domain.com/api/get');
// 发起 POST 请求
$response = $sender->post('https://www.domain.com/api/post', $bodyData);
// 发起 POST 请求,传输 JSON 格式的数据
$response = $sender->postWithJson('https://www.domain.com/api/post', $bodyData);
// 发起 POST 请求,传输 FORM 格式的数据
$response = $sender->postWithForm('https://www.domain.com/api/post', $bodyData);

if (!$response->isSuccess(true)) {
    // 接口请求失败的处理
} else {
    $resData = $response->isJson() ? $response->toArray() : $response->toString();
}

异步发起单个请求

$sender = \GgcpHttp\Client::prepare()->getSender();

$sender->getAsync('https://www.domain.com/api/get', [], ['success' => function (\GgcpHttp\Response $response) {
    $resData = $response->toArray();
    # todo something
}]);

异步发起多个请求

$sender = \GgcpHttp\Client::prepare()->getSender();

$options = [
    'success' => function (\GgcpHttp\Response $response) {
        $resData = $response->toArray();
        # todo something
        return true;
    }
];

$promises = [
    // 该接口注册成功回调函数
    'api1' => $sender->getAsync('https://www.domain.com/api/get', [], $options),
    // 该接口不注册成功回调函数
    'api2' => $sender->getAsync('https://www.domain.com/api/get'),
];

// 两个接口均请求完成后才返回结果
$results = GuzzleHttp\Promise\Utils\unwrap($promises);

$ok = $results['api1']; // return true

$response = $results['api2'];
if (!$response->isSuccess(true)) {
    // 接口请求失败的处理
} else {
    $resData = $response->isJson() ? $response->toArray() : $response->toString();
}

发起请求时对 Header 的处理

为了能够让每一个发起的请求,都能在某个调用链上找到。我们会往即将发起的请求的 Request Header 中加入总共三个请求头参数:X-Trace-Id, X-Span-Id, X-Sub-Span-Id

  1. X-Trace-Id: 一条完整的调用链中,唯一的 ID 标识。如果从调用方的 Request Header 中能找到 X-Trace-Id 的数据,则接下来发起的所有子请求中,都会带上相同值的 TraceId
  2. X-Span-Id: 调用方自己的 SpanId,用于告诉子请求是哪一个父请求发起的调用
  3. X-Sub-Span-Id: 调用方分配给子请求的 SpanId,主要用于调用方自身做采集数据收集时的数据整理。调用方可根据自己的情况选择是否作为自己唯一的 SpanId,正常情况下内部业务接口以 iLog 产生的 subLogId 作为自身唯一的 SpanId

数据采集器说明

数据采集器主要采集以下的信息:

  • 请求开始/结束时间
  • 请求处理过程中每个环节的耗时(域名解析耗时、TCP Connect 耗时等)
  • 请求的 Request、Response 信息
  • 请求失败时的异常信息

一个完整的采集信息格式如下:

{
  "traceId": "ilog_60307f6be15f1",
  "spanId": "ilog_60307f6bf1ac1",
  "parentId": "ilog_60307f6bf1ea0",
  "parentLogId": "ilog_60307f6bf1ea0",
  "parentUrl": "https://www.domain1.com/v2/api",
  "requestUrl": "https://www.domain2.com/v1/api",
  "responseLogId": "ilog_60307f6bf1ac1",
  "duration": {
    "startTime": "2021-02-20 11:18:03.966709",
    "endTime": "2021-02-20 11:18:04.130406"
  },
  "transferStat": {
    "totalTime": 0.149956,
    "namelookupTime": 0.004178,
    "connectTime": 0.00784,
    "sslVerifyTime": 0.00791,
    "waitTime": 0.149081,
    "uploadSize": 295,
    "uploadSpeed": 1979,
    "downloadSize": 56,
    "downloadSpeed": 375
  },
  "requestStat": {
    "url": "https://www.domain2.com/v1/api",
    "query": "",
    "fullUrl": "https://www.domain2.com/v1/api?accessToken=123",
    "method": "POST",
    "body": "请求体数据",
    "header": {
      "Content-Length": "295",
      "X-Trace-Id": "ilog_60307f6be15f1",
      "X-Parent-Log-Id": "ilog_60307f6be162e",
      "User-Agent": "GuzzleHttp/6.5.3 curl/7.66.0 PHP/7.1.33",
      "Content-Type": "application/x-www-form-urlencoded",
      "Host": "www.domain.com",
      "X-Span-Id": "ggcphttp_60307f6bec018"
    },
    "protocVersion": "1.1"
  },
  "responseStat": {
    "statusCode": 200,
    "header": {
      "Server": "openresty/1.17.8.2",
      "Date": "Sat, 20 Feb 2021 03:18:04 GMT",
      "Content-Type": "application/json; charset=UTF-8",
      "Transfer-Encoding": "chunked",
      "Connection": "keep-alive",
      "Vary": "Accept-Encoding",
      "X-Powered-By": "PHP/7.0.8",
      "X-Aop": "",
      "X-Log-Id": "ilog_60307f6bf1ea0"
    },
    "content": "{\"code\":0,\"data\":\"\"}"
  }
}

调用链追踪

具体的接入使用说明,可联系 huangyj01@mingyuan.com 咨询获取