1.背景
目前公司采用的是服务化设计,(什么是服务化?),服务化带来便利的同时也带了了一个问题。那就是请求链路长,通常一个api访问会访问到多个服务。这样就导致查询一个访问需要查询好几个服务。链路分析就是解决的就是把这些访问的信息都串联起来。
2.日志设置
以上要考虑的问题有两个:
- 如何把这些不同服务器上的日志串联起来
- 如何保证这个接口的访问顺序
一个接口访问到了A服务,A服务访问了B服务,B服务访问了C服务。返回结果后A服务有访问了D服务,D服务又访问了B服务。
怎么记录他的链路来整体做分析?
解决1的办法,用一个唯一的request_id来串联起来所有的服务。
解决2的办法就是设置一个rpc_version.
rpc_version的设置规则:
- 当前服务的(server)的版本规则 “rpc_version.rpc_server_version”
- 当前服务请求其他服务(此时该请求作为了client)的版本规则 “rpc_version => rpc_server_version”
此时请求的日志规则
A服务器日报
2018-07-10 17:45:26 [INFO] {"input":{"get":{"_url":"/abc/efg","id":"7336"},"post":[]},"client_ip":"10.33.142.56","request_id":"2018071017452642453","rpc_version":"1","consume":0.214,"response": {"errno":0,"error":"","request_id":"2018071017452642453"}}
2018-07-10 17:45:30 [INFO] {"path":"/abc/efg","get":"","post":{"id":7336},"request_id":"2018071017453065890","rpc_version":"1=>1.1","consume":0.191,"client_ip":"8.8.8.8"}
2018-07-10 17:45:30 [INFO] {"path":"/abc/efg","get":"","post":{"id":7336},"request_id":"2018071017453065890","rpc_version":"1=>1.2","consume":0.191,"client_ip":"8.8.8.8"}
B服务器日报
2018-07-10 17:45:26 [INFO] {"input":{"get":{"_url":"/abc/efg","id":"7336"},"post":[],"client_ip":"9.9.9.9","request_id":"2018071017452642453","rpc_version":"1.1","consume":0.214,"response":{"errno":0,"error":"","request_id":"2018071017452642453"}}
2018-07-10 17:45:30 [INFO] {"path":"/abc/efg","get":"","post":{"id":7336},"request_id":"2018071017453065890","rpc_version":"1.1=>1.1.1","consume":0.191,"client_ip":"8.8.8.8"}
C服务器日志
2018-07-10 17:45:26 [INFO] {"input":{"get":{"_url":"/abc/efg","id":"7336"},"post":[]},"client_ip":"9.9.9.9","request_id":"2018071017452642453","rpc_version":"1.1.1","consume":0.214,"response":{"errno":0,"error":"","request_id":"2018071017452642453"}}
D服务器日志
2018-07-10 17:45:26 [INFO] {"input":{"get":{"_url":"/abc/efg","id":"7336"},"post":[]},"client_ip":"9.9.9.9","request_id":"2018071017452642453","rpc_version":"1.2","consume":0.214,"response":{"errno":0,"error":"","request_id":"2018071017452642453"}}
通过上面的日志我们可以得到
通过request_id=2018071017452642453 可以找到所有的日志
然后通过rpc_version来把他们顺序串联起来。
例如上述日志
服务器A发起请求此时版本号1
发起了对服务器B的请求,A服务器上的版本信息 “rpc_version”:”1=>1.1”
B服务器上打印的日志为 “rpc_version”:”1.1”,同时发起了对C服务器的请求。
B服务器上打印的日志为 “rpc_version”:”1.1=>1.1.1”,同时发起了对C服务器的请求。
C服务器上打印到的日志 “rpc_version”:”1.1.1”
返回A服务器后 A服务器发起了对D服务器的请求”rpc_version”:”1=>1.2”
D服务器打印日志 “rpc_version”:”1.2”
这样访问路径就串联起来了
主要代码如下:
<?php
class Context
{
private static $request_id = null;
private static $rpc_version = null;
private static $rpc_client_version = 0; // 1.1这种形式
private static $rpc_server_version = null; // 1=>1.1这种形式
//初始化操作
public static function init()
{
self::setRequestId();
self::setRpcVersion();
}
//设置request_id
public static function setRequestId()
{
$request_id = $_GET['request_id'];
if (empty($request_id)){
self::$request_id = date("YmdHis") . rand(10000, 99999);
} else {
self::$request_id = $request_id;
}
}
//获取全局的request_id
public static function getRequestId()
{
return self::request_id;
}
//设置RPC版本
public static function setRpcVersion()
{
$rpc_version = $_GET['rpc_version'];
if (empty($rpc_version)){
self::$rpc_version = 1;
} else {
self::$rpc_version = $rpc_version;
}
}
//获取rpc版本
public static function getRpcServerVersion()
{
self::$rpc_server_version++;
return sprintf("d%.d%", self::$rpc_version, self::$rpc_server_version);
}
//获取服务端rpc版本
public static function getRpcClientVersion()
{
$rpc_server_version = self::getRpcServerVersion();
self::$rpc_client_version = sprintf("s%=>s%", self::$rpc_version, self::$rpc_server_version);
}
}
//初始化
Context::init();
$rpc_version = Context::getRpcClientVersion();
$request_id = Context::getRequestId();
//一个服务在调用另一个服务的时候
//对URL做拼接
$url = $url."?rpc_version=".$rpc_version."&request_id=".$request_id;
3.链路分析
以上日志rpc_version规则的设定好后,接下来分析就是靠数据组装了。
- 通过request_id从ES中获取相关的日志信息。
- 获取日志信息后根据rpc_version吧日志串联起来,至于怎么串联就根据自己的需求去处理了。