實現分佈式追蹤系統Jaeger 從php API Gateway 到go-micro微服務

分佈式調用鏈跟蹤系統,屬於監控系統的一類。系統架構逐步演進時,後期形態往往是一個平臺由很多不同的服務、組件構成,用戶請求過來後,可能會經過其中多個服務。
不過,出問題時往往很難排查,如整個請求變慢、偶爾報錯、不可用等,我們很難得知具體是由哪一個或哪些服務引起的,通常開發同學都會互相甩鍋,最後不得不花大量時間人肉 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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章