餓了麼物流移動端業務可用性監控體系建設

做者簡介java

錦洋,負責餓了麼蜂鳥APP的架構、研發等工做。目前關注的技術方向爲移動端監控、移動端架構、移動端性能優化等方向。git

在這個重視穩定性的年代,不少公司在移動端性能監控上花了很大的力氣,對業務可用性監控的投入不足,可是移動端可用是由性能可用和業務可用共同組成,缺一不可,由於業 界性能監控已經比較成熟,有不少第三方的平臺,因此避開性能監控不談,下面介紹一下餓了麼物流移動端在業務可用性監控體系建設上的一些探索。github

餓了麼物流移動端做爲騎手直接使用的配送工具,須要天天承載千萬量級的配送單量,騎手app具有如下三個特色:時效要求高,網絡環境複雜,重度使用。騎手須要在30分鐘內將訂單配送到用戶手中,中間實施屢次訂單操做,可謂爭分奪秒,若是遇到網絡或者定位異常均可能致使有損操做,爲了保證騎手的操做順暢,咱們須要將騎手整個配送過程歸入可用性監控體系建設中,通過長期的探索,咱們創建一套本身的移動端業務監控體系。數據庫

整個監控體系就像一個數據漏斗:後端

第一層 E-Monitor:全局業務監控,縱觀全局,掌握業務大盤趨勢;api

第二層 TimeBomb:異常事件監控,定點插針,實時報警;安全

第三層 Dogger:單點日誌監控,全量日誌,還原現場;性能優化

第四層 EDW:離線大數據,T+1報表,大數據分析業務健康度;bash

下面給你們詳細介紹一下這四層監控網絡

E-Monitor

你們都知道做爲移動端自己不須要對接口監控敏感,由於後端有各類維度的API服務監控,可是App做爲上層應用,接口的成功失敗,並不能徹底替代用戶的感知,這是有調用方的特徵決定的。一次業務請求包含:準備請求數據->發送請求->網絡鏈路->請求回調->解析->渲染,任何一個環節的失敗均可能表明着用戶的一次交互失敗,因此要想徹底掌控線上大盤的核心功能使用曲線,徹底依賴後端接口監控是不行的,必需要梳理調用鏈路,搭建客戶端業務監控。

做爲全局業務監控只看單個用戶的數據是沒有什麼意義的,須要將全部用戶的數據採集,存儲,可視化。這裏數據的採集咱們使用餓了麼MT部門提供的Skynet做爲採集和上傳通道,它具有編譯期AOP插入,序列化存儲,針對移動端優化的對齊上傳等特性,保證了數據採集上傳的可靠,穩定。

而在服務端的存儲和可視化咱們採用LinDB+E-Monitor的監控架構,LinDB是一款優秀的時間序列數據庫,適合存儲設備性能、日誌等帶時間戳的數據。能輕鬆處理高寫入和高查詢負載。配合E-Monitor強大的可視化能力,能夠完美展示騎手訂單操做主流程的穩定狀況,異常報警。

報警的策略有如下幾種:移動端經常使用的是閾值模型和趨勢模型配合同環比

最終生成一個大盤的監控面板,這裏由於銘感數據只放出了部分脫敏面板

TimeBomb

TimeBomb-定時炸彈,從名字就能夠猜到它和異常有關,它專門負責監控在規定時間和次數限定下沒有達成用戶交互結果的邏輯,TimeBomb做爲全局業務監控的補充,在排查異常中立下汗馬功勞。它的設計初衷是經過簡單的代碼插入,由計數,時間間隔等條件觸發異常事件上傳, 適用於:

  1. 登陸屢次登陸不上

  2. 屢次點擊確認送達,都失敗

  3. 定位一直報錯

  4. 定位上傳屢次失敗

  5. 任何接⼝口屢次報錯

等等...

總結來講,TimeBomb能夠隨意定義異常的監控力度,而且能夠靈活的遠端配置次數,時間,採樣率。

TimeBomb的數據採集和展現是經過咱們自研的服務,主要包括兩個功能:

  1. 根據選擇的Tag和時間顯示異常曲線
  2. 選擇節點以後就能夠查看異常明細列表 在Parameter中查看異常上報的次數和時間配置還有上報的緣由,點擊日誌拉取就能夠拉取到用戶的詳細日誌數據。

騎手App經過TimeBomb完成了不少異常問題的上報,修復,觀測,優化,再觀測,這是一個異常問題解決的正向循環,並且特別適合一些須要多輪驗證的極端case的排查觀測。

Dogger

Dogger包含兩個部分:

  1. Trojan日誌寫入上傳SDK

  2. Dogger-Service 日誌解析服務

Trojan是一個面對高性能,極致體驗要求下,產出的輕量級,高效率的移動端日誌監控方案,它就像一隻聽話的狗狗,他在客戶端默默的記錄着用戶的各項操做日誌和技術性能埋點,最終在須要的時候,把日誌拋上來,交給Dogger-Service解析,經過完善的埋點,咱們能夠很快的還原騎手的操做現場,藉助對特定日誌的橫向分析,能夠幫咱們快速定位問題。

Trojan具有如下四個特色:

  • 第一點:開發透明,使用者不須要關心日誌文件的讀寫。
  • 第二點:高效收集,一.採用AOP技術植入到埋點自動收集日誌,二.高效的文件讀寫方式,毫秒級別的耗時。
  • 第三點:敏感加密,對於用戶相關的敏感信息,爲保證日誌的安全性,咱們提供加密方式,好比說DES、AES。
  • 第四點:流量開銷低,日誌寫在本地,指定用戶壓縮上傳。

Trojan的架構圖:

Trojan 用C的方式經過mmap(內存映射)的方案寫入日誌,對比java api的寫入方式性能提升了一倍,低CPU,低內存消耗。

在性能監控這種大數據量寫入的場景上知足了咱們的需求,再配合文件的gzip壓縮能夠將日誌這種多重複字母的文件達到50倍的壓縮效果,實測一個43M的文件,壓縮上傳只要860kb。1M之內的文件上傳對移動端來講也是一個能夠接受的大小,這也對將來trojan除了完成邏輯回放,提供了可能。

通過三個版本的迭代,Trojan已經涵蓋了用戶的點擊事件,頁面生命週期,請求監控,流量,電量,內存,線程等方方面面, 文件的寫入和上傳都完成了,那一個幾十M的文件該如何分析尼?下面就介紹一下咱們的Trojan配套解析服務Dogger-Service。

Dogger-Service 主要的功能分爲三個部分:

  1. ActionChart-全局展現騎手一天的頁面跳轉,電量,內存,網絡切換,定位頻率,特定請求頻率等

經過ActionChart咱們能夠直觀的看出騎手在時空座標系下的操做和資源使用狀況,能夠方便協助咱們觀察某個時間點出現某個問題的環境,這種全局掌控用戶操做多是行業內第一次達到。

  1. Origin -能夠輕鬆的完成百兆之內的文件解析和展現,按時間查找,全局高亮搜索等功能

經過對原始數據解析,咱們能夠拖拽時間滑片,直接定位到某個時間段查看騎手的日誌明細,也能夠選擇某個關注的Tag,或者直接經過關鍵字搜索高亮查找,Origin模塊讓咱們能夠靈活的查找問題的蛛絲馬跡,給定位問題的root cause提供了保障。

這樣就夠了嗎?

  1. Statistics - 統計模塊是基於特定的Tag數據,數據挖掘分析和展現。

當前實現了對電量,網絡,流量,卡頓,請求,生命週期,內存,定位的數據分析。好比下面的內存分析,咱們能夠經過最長間隔,知道騎手有哪些時間段app是關閉着的,內存的峯值和低谷,平均內存各是多少,內存波動比較大的時間段是哪幾個,波動大表明着資源開銷可能異常,是須要仔細排查的點。

能夠Loc Tag查看騎手的定位軌跡,分析是否有定位漂移或者定位失敗狀況

還能夠經過PunchLoc Tag查看定位上傳的失敗佔比,分析失敗的緣由是否和當時的網絡狀態有關

經過THttpReq能夠查看網絡請求的Host和Path佔比狀況,方便優化請求流量

Trojan和Dogger-Service組成了Dogger這個有機的總體,日誌和解析配合,可讓咱們在排查單個case的時候,對用戶的行爲了如指掌,豐富的埋點數據能夠爲咱們的排查提供數據支撐。

目前Dogger服務中的日誌寫入sdk Trojan已經開源,歡迎交流學習

當咱們有了實時的全局大盤和異常監控,還有單個用戶的全週期日誌數據,就夠了嗎?

大盤的曲線正常,異常的毛刺消除只能表明業務大盤穩定,可是業務功能真實的質量還不能一律而論,這時咱們須要對數據漏斗的終點---離線數據池進行大數據挖掘分析來作最後的監控兜底。

下面介紹一下最後一層監控EDW

EDW:

離線報表監控做爲全局大盤的另外一種視角,E-Monitor屬於實時大盤監控,只能觀察實時曲線趨勢和昨天作對比,判斷粗粒度的業務是否異常,可是離線數據能夠挖掘分析完整一天的數據,細粒度的判斷每個訂單的健康程度,聚合定位失敗的緣由佔比,獲取複雜條件篩選出的各類比例,讓咱們從上帝視角觀察整個業務線,評估線上業務健康度,分析趨勢,表徵產出,是移動端監控體系中不可或缺的利器。

公司大數據平臺部自研的edw爲咱們提供了優質的離線大數據服務,它融合了即時查詢、數據抽取、數據計算、數據推送、元數據管理、數據監控等多種數據服務的平臺型產品。

當前咱們在流量,定位質量,騎手多設備使用,離線送達,推送質量,訂單異常等關鍵業務場景都有完備的離線報表。好比上圖的流量報表,能夠知曉線上流量消耗Top 100的騎手device_id 和流量數據,而排行第一的騎手response數據遠大於request數據,經過Dogger拉日誌後發現,騎手有屢次下載app的行爲。第二幅圖則是線上主流程的偏向業務的流轉時長監控,由於數據敏感因此打碼了。這些報表能夠說明線上業務的真實健康度,這一點可以讓咱們對全局的把控更有自信。基於離線數據的聚合分析,能夠發現優化點,爲改善方案提供依據。

實戰

這裏記錄一下最近發生的一次網絡層問題的排查過程,讓你們直觀感覺這幾層監控的做用。

第一步:咱們的gafana的監控發現Android騎手的訂單相關請求平均成功率下降到了98.69%,而正常請求成功率應該在99%以上

上面說到grafana屬於可用性全局監控,若是這邊的數據異常,將會影響全盤,因此咱們不敢怠慢,立馬着手排查。

首先咱們懷疑是DNS解析問題,咱們經過EDW拉取了出現問題騎手的id,而後配置了Dogger的騎手日誌拉取,通過分析發現,DNS失敗的場景多發生在斷網等弱網環境,屬於正常狀況,並且咱們發現日誌上出問題的請求的requestID在後端的trace系統上都查不到,查看了skynet網絡監控攔截器的代碼

apmNetInterceptor插在最後一個,數據沒有傳上去,說明請求在發送前就已經拋了錯,因此咱們開始排查請求發送前的邏輯。 第二步:經過EDW抽取出現問題騎手,對他們的請求失敗緣由聚合,獲得了ioException異常佔比最大

第三步:因爲請求前的日誌數據過少,因此咱們升級了okhttp到3.11,使用EventListener來獲取請求生命週期埋點,針對上報問題的騎手發了內測版本,但願得到出問題請求的鏈路明細。

完整的鏈路大體以下:

再次撈出有問題騎手的日誌,發現有些時候網絡狀態是良好的,可是在 responseHeaderStart以後會直接拋錯或者是 timeout:

因而咱們擼了多遍okhttp的源碼,以爲應該是鏈接池複用的問題,複用了已經失效的鏈接. 咱們又加入 IOException 的 stacktrace日誌.發現一個奇怪的問題:

線上的請求走的居然是http/2的協議,仔細閱讀Okhttp 握手相關的代碼發現,Okhttp 在 https的狀況下會判斷服務端是否支持 http/2,若是支持則會走 http/2的協議,相關代碼參見RealConnection.java的establishProtocol方法。

private void establishProtocol(ConnectionSpecSelector connectionSpecSelector,
      int pingIntervalMillis, Call call, EventListener eventListener) throws IOException {
    if (route.address().sslSocketFactory() == null) {
      if (route.address().protocols().contains(Protocol.H2_PRIOR_KNOWLEDGE)) {
        socket = rawSocket;
        protocol = Protocol.H2_PRIOR_KNOWLEDGE;
        startHttp2(pingIntervalMillis);
        return;
      }
      socket = rawSocket;
      protocol = Protocol.HTTP_1_1;
      return;
    }
    eventListener.secureConnectStart(call);
    connectTls(connectionSpecSelector);
    eventListener.secureConnectEnd(call, handshake);
    if (protocol == Protocol.HTTP_2) {
      startHttp2(pingIntervalMillis);
    }
  }

複製代碼

最終發現的確是 Okhttp在 http/2上對鏈接池的複用問題存在 bug ,在StreamAllocation.java上

public void streamFailed(IOException e) {
    boolean noNewStreams = false;
    synchronized (connectionPool) {
      if (e instanceof StreamResetException) {
      } else if (connection != null
          && (!connection.isMultiplexed() || e instanceof ConnectionShutdownException)) {
        noNewStreams = true;
      }
      socket = deallocate(noNewStreams, false, true);
      }
  }

複製代碼

當協議是http/2的時候,noNewStreams爲false 而在ConnectionPool.java的connectionBecameIdle就不會將這個connection從ConnectionPool中移除

boolean connectionBecameIdle(RealConnection connection) {
    if (connection.noNewStreams || maxIdleConnections == 0) {
      connections.remove(connection);
      return true;
    } else {
    }
  }
複製代碼

結合前段時間,後端將路由層切換了公司的SoPush服務上,而SoPush是支持Http/2的,切換的時間和曲線異常時間吻合,能夠肯定問題就在這裏。

線上使用的Okhttp版本仍是3.8.4, 在Okhttp 3.10.0版本以後,加入了對http2的鏈接池中的鏈接作了嚴格的ping驗證, 下面是 changelog

能夠看到 http/2 纔剛加Ping機制,因此OKhttp對Http/2支持有問題的版本是<3.10,可是即便使用了3.11的最新版本依舊有必定機率發生這個問題.因而咱們以爲先強制指定Android版本的協議爲http/1.1,後面接入集團的網絡庫再支持Http/2。修改完再次發佈內測版本,曲線恢復正常,問題解決。

此次網絡層的排查,咱們使用E-Monitor監控和分析問題嚴重程度,EDW離線數據過濾出問題騎手ID,Dogger單點日誌靈活埋點驗證修復方案,就這樣一次由第三方變更引發的客戶端可用性異常就這樣解決了,業務可用性監控功不可沒。

總結:

上面就是餓了麼物流移動當前在業務可用性監控領域作出的一些探索,咱們按照數據漏斗,從全量埋點,異常監控,單用戶日誌,離線數據,由面到點再到面,每個切面都插入了不一樣緯度的監控,但願能作到全面覆蓋業務,穩定性和質量兼顧,由於咱們深信,只有作好監控,才能作到可報警,可排查,可優化。可是移動端可用性監控是一個長期建設的工程,須要不斷的優化迭代,當前咱們也面臨數據冗餘和性能監控衝突,監控自己帶來的性能損耗等問題,將來咱們也將在這些問題上作進一步探索、實踐和分享。




閱讀博客還不過癮?

歡迎你們掃二維碼經過添加羣助手,加入交流羣,討論和博客有關的技術問題,還能夠和博主有更多互動

博客轉載、線下活動及合做等問題請郵件至 shadowfly_zyl@hotmail.com 進行溝通

相關文章
相關標籤/搜索