分布式调用链跟踪系统,属于监控系统的一类。系统架构逐步演进时,后期形态往往是一个平台由很多不同的服务、组件构成,用户请求过来后,可能会经过其中多个服务。
不过,出问题时往往很难排查,如整个请求变慢、偶尔报错、不可用等,我们很难得知具体是由哪一个或哪些服务引起的,通常开发同学都会互相甩锅,最后不得不花大量时间人肉 tracing

Jaeger是Uber Technologies用GO语言开发的分布式跟踪系统,现已开源。它用于监视和排除基于微服务的分布式系统,包括:
分布式上下文传播
分布式事务监控
根本原因分析
服务依赖性分析
性能/延迟优化

架构图
两个核心概念
TraceId:
用于标识一次完整请求 trace,会从头到尾贯穿在各个服务中,通常在请求入口时生成。
SpanId:
用于标识某个调用跨度 span,一个 span 可以有多个 子span, 通常一个完整的 trace 由很多个 span 组成。

PHP使用并传递uber-trace-id到下一层链路中!在http请求时调用jaege注入请求,会生成唯一的uber-trace-id 与spanid,获取到这俩个id,组装数据Metadata ,通过grpc调用服务方法时把Metadata传递过去,而在go-micro中通过context.Context就可以获取到,如果需要服务之间继续调用,则继续传递context。这样就把一次完整的链路请求记录到Jaeger中。

基本流程

请求入口生成 trace

在方法(或服务)调用前,生成 span,记录时间

调用时,携带 TraceId SpanId (如,在 http header 或 grpc meta data 里)

调用完后关联到 trace

统一上报到 Jaeger存储。

代码示例

 public function Login(Request $request)
    {
        $jaegerClien = new jaegerClien();
        $jaegerClien->setSvcName();
        $metadata = $jaegerClien->Hprose('admin/login'); //请求链路的metadata
        $phone = $request->input('phone');
        $password = $request->input('password');
        $consul = new Consul("xyd.vip.srv");
        $host = $consul->getHost();
        $client = new Vipuser($host,[
            'credentials' => \Grpc\ChannelCredentials::createInsecure(),
        ]);
        $req = new vipuserReq();
        $req->setMobile($phone);
        $req->setPwd($password);
        list($rsp, $status) = $client->Login($req,$metadata)->wait();
        $code = $rsp->getCode();
        $msg = $rsp->getMsg();
        $data = $rsp->getData();
        $token = $rsp->getToken();
        if ($code == 200){
            $user = json_decode($data);
            $request->session()->put('token',$token);
            $request->session()->put('user', $user);
            $jaegerClien->setDisabled(); //关闭jaeger
            return $this->_returnJosn($code,$user,$msg,0,$token);
        }else{
            return $this->_returnJosn($code,array(),$msg);
        }
    }
    
public function Hprose($operationName){
        $this->jaegercfg = JaegerConfig::getInstance();
        $tracer = $this->jaegercfg->initTracer($this->svcName, $this->jaegerUrl);
        $spanContext = $tracer->extract(Formats\TEXT_MAP, $_SERVER);
        $serverSpan = $tracer->startSpan($operationName, ['child_of' => $spanContext]);
        $tracer->inject($serverSpan->getContext(), Formats\TEXT_MAP, $_SERVER);
        //init server span end
        $serverSpan->finish();
        $this->jaegercfgFlush();
        $spanContext = (array)$serverSpan->getContext();
        $meta = new Metadata();
        $meta->set('uber-trace-id', $_SERVER['UBER-TRACE-ID']);
        $meta->set('X-B3-ParentSpanId', $spanContext['parentId']);
        $meta->set('X-B3-SpanId', $spanContext['spanId']);
        $meta->set('X-B3-Sampled', true);

        return $this->getZipKinMetadata($meta);
    }
func (a *User) Login(ctx context.Context, req *user.Request, rsp *user.Response) error {
	mobile := req.Mobile
	pwd := req.Pwd
	code, msg, user := UserService.Login(mobile, pwd)
	jsons := "{}"
	token := ""
	if user != nil {
		jsonsby, _ := json.Marshal(user)
		jsons = string(jsonsby)
		token = UserService.MakeAccessToken(ctx, user.Id, user.Mobile)
	}
	response.UserReturnRes(code, msg, jsons, token, rsp)
	return nil
}

//使用上下文传递 span
func (a *AuthClient) MakeAccessToken(ctx context.Context, userid int64, phone string) string {
	info, err := a.client.MakeAccessToken(ctx, &authSvc.Request{
		Userid: userid,
		Phone:  phone,
	})
	fmt.Println("info:", info)
	fmt.Println("err:", err)
	if err != nil {
		return ""
	}
	//span.Finish()
	return info.Token
}

实际效果图
在这里插入图片描述在这里插入图片描述
可以看到整个链路的耗时,点击进去可以看到每个span的详细信息

实践准备工作
安装Jaeger
docker安装测试版 所有数据都是存储到内存中 只能作为测试

docker run -d --name jaeger   -e COLLECTOR_ZIPKIN_HTTP_PORT=9411   -p 5775:5775/udp   -p 6831:6831/udp   -p 6832:6832/udp   -p 5778:5778   -p 16686:16686   -p 14268:14268   -p 9411:9411   jaegertracing/all-in-one:latest

Docker部署jaeger并使用elasticsearch作为存储引擎

docker + collector安装

docker run -d --name jaeger-collector --restart=always --link elasticsearch -e SPAN_STORAGE_TYPE=elasticsearch -e ES_SERVER_URLS=http://172.26.155.215:9200 -e ES_USERNAME=elastic -p 14267:14267 -p 14268:14268 -p 9411:9411 jaegertracing/jaeger-collector

注意:
​ --link elasticsearch,代表docker 关联,该名字必须和你安装elasticsearch —name的名字相同
​ --SPAN_STORAGE_TYPE=elasticsearch 代表安装jaeger选择elasticsearch作为存储
-e ES_SERVER_URLS=http://elasticsearch:9200次条目代表你选择容器安装的elasticsearch的9200端口
-e ES_USERNAME elasticsearch的用户名:默认elastic,下同
-e ES_PASSWORD elasticsearch的密码  没有设置密码不用填

docker + query安装

docker run -d --name jaeger-query --restart=always --link elasticsearch -e SPAN_STORAGE_TYPE=elasticsearch -e ES_SERVER_URLS=http://172.26.155.215:9200 -p 16686:16686/tcp jaegertracing/jaeger-query

注意,ES_USERNAME、ES_PASSWORD这两个环境变量,当你的elasticsearch未设置账号密码时,你可以不填,也可以填上默认值,elasticsearch的默认ES_USERNAME=elastic,ES_PASSWORD=changeme

部署完成query之后,根据你暴露的端口号(-p 16686:16686/tcp),浏览器输入以下地址(将localhost换成你部署query的地址):
http://localhost:16686

docker + agent安装

docker run -d  --name jaeger-agent --restart=always -p 5775:5775/udp -p 6831:6831/udp -p 6832:6832/udp -p 5778:5778/tcp jaegertracing/jaeger-agent --collector.host-port=172.26.155.215:14267
Logo

开源、云原生的融合云平台

更多推荐