精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

手撕APM系統(tǒng),痛快!

安全 應用安全
本文來說說什么是 APM 系統(tǒng),也就是大家平時說的監(jiān)控系統(tǒng),以及怎么實現(xiàn)一個 APM 系統(tǒng)。因為一些特殊的原因,我在文中會使用 Dog 作為我們的系統(tǒng)名稱進行介紹。

 [[421642]]

圖片來自 包圖網(wǎng)

我們?yōu)?Dog 規(guī)劃的目標是接入公司的大部分應用,預計每秒處理 500MB-1000MB 的數(shù)據(jù),單機每秒 100MB 左右,使用多臺普通的 AWS EC2。

因為本文的很多讀者供職的公司不一定有比較全面的 APM 系統(tǒng),所以我盡量照顧更多讀者的閱讀感受,會在有些內(nèi)容上啰嗦一些,希望大家可以理解。

我會在文中提到 Prometheus、Grafana、Cat、Pinpoint、Skywalking、Zipkin 等一系列工具,如果你沒有用過也不要緊,我會充分考慮到這一點。

本文預設的一些背景:Java 語言、Web 服務、每個應用有多個實例、以微服務方式部署。

另外,從文章的可閱讀性上考慮,我假設每個應用的不同實例分布在不同的 IP 上,可能你的應用場景不一定是這樣的。

APM 簡介

APM 通常認為是 Application Performance Management 的簡寫,它主要有三個方面的內(nèi)容,分別是 Logs(日志)、Traces(鏈路追蹤)和 Metrics(報表統(tǒng)計)。

以后大家接觸任何一個 APM 系統(tǒng)的時候,都可以從這三個方面去分析它到底是什么樣的一個系統(tǒng)。

有些場景中,APM 特指上面三個中的 Metrics,我們這里不去討論這個概念。

這節(jié)我們先對這 3 個方面進行介紹,同時介紹一下這 3 個領域里面一些常用的工具。

①首先 Logs 最好理解,就是對各個應用中打印的 log 進行收集和提供查詢能力。

Logs 系統(tǒng)的重要性不言而喻,通常我們在排查特定的請求的時候,是非常依賴于上下文的日志的。

以前我們都是通過 terminal 登錄到機器里面去查 log(我好幾年都是這樣過來的),但是由于集群化和微服務化的原因,繼續(xù)使用這種方式工作效率會比較低。

因為你可能需要登錄好幾臺機器搜索日志才能找到需要的信息,所以需要有一個地方中心化存儲日志,并且提供日志查詢。

Logs 的典型實現(xiàn)是 ELK(ElasticSearch、Logstash、Kibana),三個項目都是由 Elastic 開源,其中最核心的就是 ES 的儲存和查詢的性能得到了大家的認可,經(jīng)受了非常多公司的業(yè)務考驗。

Logstash 負責收集日志,然后解析并存儲到 ES。通常有兩種比較主流的日志采集方式:

  • 一種是通過一個客戶端程序 FileBeat,收集每個應用打印到本地磁盤的日志,發(fā)送給 Logstash。
  • 另一種則是每個應用不需要將日志存儲到磁盤,而是直接發(fā)送到 Kafka 集群中,由 Logstash 來消費。

Kibana 是一個非常好用的工具,用于對 ES 的數(shù)據(jù)進行可視化,簡單來說,它就是 ES 的客戶端。

kibana-discover

我們回過頭來分析 Logs 系統(tǒng),Logs 系統(tǒng)的數(shù)據(jù)來自于應用中打印的日志,它的特點是數(shù)據(jù)量可能很大,取決于應用開發(fā)者怎么打日志,Logs 系統(tǒng)需要存儲全量數(shù)據(jù),通常都要支持至少 1 周的儲存。

每條日志包含 ip、thread、class、timestamp、traceId、message 等信息,它涉及到的技術(shù)點非常容易理解,就是日志的存儲和查詢。

使用也非常簡單,排查問題時,通常先通過關鍵字搜到一條日志,然后通過它的 traceId 來搜索整個鏈路的日志。

題外話,Elastic 其實除了 Logs 以外,也提供了 Metrics 和 Traces 的解決方案,不過目前國內(nèi)用戶主要是使用它的 Logs 功能。

②我們再來看看 Traces 系統(tǒng),它用于記錄整個調(diào)用鏈路。

前面介紹的 Logs 系統(tǒng)使用的是開發(fā)者打印的日志,所以它是最貼近業(yè)務的。

而 Traces 系統(tǒng)就離業(yè)務更遠一些了,它關注的是一個請求進來以后,經(jīng)過了哪些應用、哪些方法,分別在各個節(jié)點耗費了多少時間,在哪個地方拋出的異常等,用來快速定位問題。

經(jīng)過多年的發(fā)展,Traces 系統(tǒng)雖然在服務端的設計很多樣,但是客戶端的設計慢慢地趨于統(tǒng)一,所以有了 OpenTracing 項目,我們可以簡單理解為它是一個規(guī)范,它定義了一套 API,把客戶端的模型固化下來。

當前比較主流的 Traces 系統(tǒng)中,Jaeger、SkyWalking 是使用這個規(guī)范的,而 Zipkin、Pinpoint 沒有使用該規(guī)范。限于篇幅,本文不對 OpenTracing 展開介紹。

下面這張圖是我畫的一個請求的時序圖:

trace

從上面這個圖中,可以非常方便地看出,這個請求經(jīng)過了 3 個應用,通過線的長短可以非常容易看出各個節(jié)點的耗時情況。

通常點擊某個節(jié)點,我們可以有更多的信息展示,比如點擊 HttpClient 節(jié)點我們可能有 request 和 response 的數(shù)據(jù)。

下面這張圖是 Skywalking 的圖,它的 UI 也是蠻好的:

skywalking-trace

SkyWalking 在國內(nèi)應該比較多公司使用,是一個比較優(yōu)秀的由國人發(fā)起的開源項目,已進入 Apache 基金會。

另一個比較好的開源 Traces 系統(tǒng)是由韓國人開源的 Pinpoint,它的打點數(shù)據(jù)非常豐富,這里有官方提供的 Live Demo,大家可以去玩一玩。

pinpoint

最近比較火的是由 CNCF(Cloud Native Computing Foundation)基金會管理的 Jeager:

jaeger

當然也有很多人使用的是 Zipkin,算是 Traces 系統(tǒng)中開源項目的老前輩了:

zipkin

上面介紹的是目前比較主流的 Traces 系統(tǒng),在排查具體問題的時候它們非常有用,通過鏈路分析,很容易就可以看出來這個請求經(jīng)過了哪些節(jié)點、在每個節(jié)點的耗時、是否在某個節(jié)點執(zhí)行異常等。

雖然這里介紹的幾個 Traces 系統(tǒng)的 UI 不一樣,大家可能有所偏好,但是具體說起來,表達的都是一個東西,那就是一顆調(diào)用樹,所以我們要來說說每個項目除了 UI 以外不一樣的地方。

首先肯定是數(shù)據(jù)的豐富度,你往上拉看 Pinpoint 的樹,你會發(fā)現(xiàn)它的埋點非常豐富,真的實現(xiàn)了一個請求經(jīng)過哪些方法一目了然。

但是這真的是一個好事嗎?值得大家去思考一下。兩個方面,一個是對客戶端的性能影響,另一個是服務端的壓力。

其次,Traces 系統(tǒng)因為有系統(tǒng)間調(diào)用的數(shù)據(jù),所以很多 Traces 系統(tǒng)會使用這個數(shù)據(jù)做系統(tǒng)間的調(diào)用統(tǒng)計,比如下面這個圖其實也蠻有用的:

trace-statistics

另外,前面說的是某個請求的完整鏈路分析,那么就引出另一個問題,我們怎么獲取這個“某個請求”,這也是每個 Traces 系統(tǒng)的不同之處。

比如上圖,它是 Pinpoint 的圖,我們看到前面兩個節(jié)點的圓圈是不完美的,點擊前面這個圓圈,就可以看出來原因了:

pinpoint-dashboard

圖中右邊的兩個紅圈是我加的。我們可以看到在 Shopping-api 調(diào)用 Shopping-order 的請求中,有 1 個失敗的請求。

我們用鼠標在散點圖中把這個紅點框出來,就可以進入到 trace 視圖,查看具體的調(diào)用鏈路了。限于篇幅,我這里就不去演示其他 Traces 系統(tǒng)的入口了。

還是看上面這個圖,我們看右下角的兩個統(tǒng)計圖,我們可以看出來在最近 5 分鐘內(nèi) Shopping-api 調(diào)用 Shopping-order 的所有請求的耗時情況,以及時間分布。在發(fā)生異常的情況,比如流量突發(fā),這些圖的作用就出來了。

對于 Traces 系統(tǒng)來說,最有用的就是這些東西了,當然大家在使用過程中,可能也發(fā)現(xiàn)了 Traces 系統(tǒng)有很多的統(tǒng)計功能或者機器健康情況的監(jiān)控,這些是每個 Traces 系統(tǒng)的差異化功能,我們就不去具體分析了。

③最后,我們再來討論 Metrics,它側(cè)重于各種報表數(shù)據(jù)的收集和展示。

在 Metrics 方面做得比較好的開源系統(tǒng),是大眾點評開源的 Cat,下面這個圖是 Cat 中的 transaction 視圖,它展示了很多的我們經(jīng)常需要關心的統(tǒng)計數(shù)據(jù):

cat-transaction

下圖是 Cat 的 problem 視圖,對我們開發(fā)者來說就太有用了,應用開發(fā)者的目標就是讓這個視圖中的數(shù)據(jù)越少越好。

cat-problem

本文之后的內(nèi)容主要都是圍繞著 Metrics 展開的,所以這里就不再展開更多的內(nèi)容了。

另外,說到 APM 或系統(tǒng)監(jiān)控,就不得不提 Prometheus+Grafana 這對組合,它們對機器健康情況、URL 訪問統(tǒng)計、QPS、P90、P99 等等這些需求,支持得非常好,它們用來做監(jiān)控大屏是非常合適的。

但是通常不能幫助我們排查問題,它看到的是系統(tǒng)壓力高了、系統(tǒng)不行了,但不能一下子看出來為啥高了、為啥不行了。

科普:Prometheus 是一個使用內(nèi)存進行存儲和計算的服務,每個機器/應用通過 Prometheus 的接口上報數(shù)據(jù),它的特點是快,但是機器宕機或重啟會丟失所有數(shù)據(jù)。

Grafana 是一個好玩的東西,它通過各種插件來可視化各種系統(tǒng)數(shù)據(jù),比如查詢 Prometheus、ElasticSearch、ClickHouse、MySQL 等等,它的特點就是酷炫,用來做監(jiān)控大屏再好不過了。

Metrics 和 Traces

因為本文之后要介紹的我們開發(fā)的 Dog 系統(tǒng)從分類來說,側(cè)重于 Metrics,同時我們也提供 tracing 功能,所以這里單獨寫一小節(jié),分析一下 Metrics 和 Traces 系統(tǒng)之間的聯(lián)系和區(qū)別。

使用上的區(qū)別很好理解,Metrics 做的是數(shù)據(jù)統(tǒng)計,比如某個 URL 或 DB 訪問被請求多少次,P90 是多少毫秒,錯誤數(shù)是多少等這種問題。

而 Traces 是用來分析某次請求,它經(jīng)過了哪些鏈路,比如進入 A 應用后,調(diào)用了哪些方法,之后可能又請求了 B 應用,在 B 應用里面又調(diào)用了哪些方法,或者整個鏈路在哪個地方出錯等這些問題。

不過在前面介紹 Traces 的時候,我們也發(fā)現(xiàn)這類系統(tǒng)也會做很多的統(tǒng)計工作,它也覆蓋了很多的 Metrics 的內(nèi)容。

所以大家先要有個概念,Metrics 和 Traces 之間的聯(lián)系是非常緊密的,它們的數(shù)據(jù)結(jié)構(gòu)都是一顆調(diào)用樹,區(qū)別在于這顆樹的枝干和葉子多不多。

在 Traces 系統(tǒng)中,一個請求所經(jīng)過的鏈路數(shù)據(jù)是非常全的,這樣對排查問題的時候非常有用,但是如果要對 Traces 中的所有節(jié)點的數(shù)據(jù)做報表統(tǒng)計,將會非常地耗費資源,性價比太低。

而 Metrics 系統(tǒng)就是面向數(shù)據(jù)統(tǒng)計而生的,所以樹上的每個節(jié)點我們都會進行統(tǒng)計,所以這棵樹不能太“茂盛”。

我們關心的其實是,哪些數(shù)據(jù)值得統(tǒng)計?首先是入口,其次是耗時比較大的地方,比如 db 訪問、http 請求、redis 請求、跨服務調(diào)用等。

當我們有了這些關鍵節(jié)點的統(tǒng)計數(shù)據(jù)以后,對于系統(tǒng)的健康監(jiān)控就非常容易了。

我這里不再具體去介紹他們的區(qū)別,大家看完本文介紹的 Metrics 系統(tǒng)實現(xiàn)以后,再回來思考這個問題會比較好。

Dog 在設計上,主要是做一個 Metrics 系統(tǒng),統(tǒng)計關鍵節(jié)點的數(shù)據(jù),另外也提供 trace 的能力,不過因為我們的樹不是很”茂盛“,所以鏈路上可能是斷斷續(xù)續(xù)的,中間會有很多缺失的地帶,當然應用開發(fā)者也可以加入手動埋點來彌補。

Dog 因為是公司內(nèi)部的監(jiān)控系統(tǒng),所以對于公司內(nèi)部大家會使用到的中間件相對是比較確定的,不需要像開源的 APM 一樣需要打很多點。

我們主要實現(xiàn)了以下節(jié)點的自動打點:

  • HTTP 入口:通過實現(xiàn)一個 Filter 來攔截所有的請求。
  • MySQL:通過 Mybatis Interceptor 的方式。
  • Redis:通過 javassist 增強 RedisTemplate 的方式。
  • 跨應用調(diào)用:通過代理 feign client 的方式,dubbo、grpc 等方式可能需要通過攔截器。
  • HTTP 調(diào)用:通過 javassist 為 HttpClient 和 OkHttp 增加 interceptor 的方式。
  • Log 打點:通過 plugin 的方式,將 log 中打印的 error 上報上來。

打點的技術(shù)細節(jié),就不在這里展開了,主要還是用了各個框架提供的一些接口,另外就是用到了 javassist 做字節(jié)碼增強。

這些打點數(shù)據(jù)就是我們需要做統(tǒng)計的,當然因為打點有限,我們的 tracing 功能相對于專業(yè)的 Traces 系統(tǒng)來說單薄了很多。

Dog 簡介

下面是 DOG 的架構(gòu)圖,客戶端將消息投遞給 Kafka,由 dog-server 來消費消息,存儲用到了 Cassandra 和 ClickHouse,后面再介紹具體存哪些數(shù)據(jù)。

architecture

也有 APM 系統(tǒng)是不通過消息中間件的,比如 Cat 就是客戶端通過 Netty 連接到服務端來發(fā)送消息的。

Server 端使用了 Lambda 架構(gòu)模式,Dog UI 上查詢的數(shù)據(jù),由每一個 Dog-server 的內(nèi)存數(shù)據(jù)和下游儲存的數(shù)據(jù)聚合而來。

下面,我們簡單介紹下 Dog UI 上一些比較重要的功能,我們之后再去分析怎么實現(xiàn)相應的功能。

注意:下面的圖都是我自己畫的,不是真的頁面截圖,數(shù)值上可能不太準確。

下圖示例 transaction 報表:

transaction-type

點擊上圖中 type 中的某一項,我們有這個 type 下面每個 name 的報表。比如點擊 URL,我們可以得到每個接口的數(shù)據(jù)統(tǒng)計:

transaction-name

當然,上圖中點擊具體的 name,還有下一個層級 status 的統(tǒng)計數(shù)據(jù),這里就不再貼圖了。Dog 總共設計了 type、name、status 三級屬性。

上面兩個圖中的最后一列是 sample,它可以指引到 sample 視圖:

sample

Sample 就是取樣的意思,當我們看到有個接口失敗率很高,或者 P90 很高的時候,你知道出了問題,但因為它只有統(tǒng)計數(shù)據(jù),所以你不知道到底哪里出了問題,這個時候,就需要有一些樣本數(shù)據(jù)了。

我們每分鐘對 type、name、status 的不同組合分別保存最多 5 個成功、5 個失敗、5 個慢處理的樣本數(shù)據(jù)。

點擊上面的 sample 表中的某個 T、F、L 其實就會進入到我們的 trace 視圖,展示出這個請求的整個鏈路:

trace

通過上面這個 trace 視圖,可以非常快速地知道是哪個環(huán)節(jié)出了問題。

當然,我們之前也說過,我們的 trace 依賴于我們的埋點豐富度,但是 Dog 是一個 Metrics 為主的系統(tǒng),所以它的 Traces 能力是不夠的,不過大部分情況下,對于排查問題應該是足夠用的。

對于應用開發(fā)者來說,下面這個 Problem 視圖應該是非常有用的:

problem

它展示了各種錯誤的數(shù)據(jù)統(tǒng)計,并且提供了 sample 讓開發(fā)者去排查問題。

最后,我們再簡單介紹下 Heartbeat 視圖,它和前面的功能沒什么關系,就是大量的圖,我們有 gc、heap、os、thread 等各種數(shù)據(jù),讓我們可以觀察到系統(tǒng)的健康情況。

heartbeat-heap

這節(jié)主要介紹了一個 APM 系統(tǒng)通常包含哪些功能,其實也很簡單對不對,接下來我們從開發(fā)者的角度,來聊聊具體的實現(xiàn)細節(jié)問題。

客戶端數(shù)據(jù)模型

大家都是開發(fā)者,我就直接一些了,下圖介紹了客戶端的數(shù)據(jù)模型:

data-model

對于一條 Message 來說,用于統(tǒng)計的字段是 type, name, status,所以我們能基于 type、type+name、type+name+status 三種維度的數(shù)據(jù)進行統(tǒng)計。

Message 中其他的字段:

  • timestamp 表示事件發(fā)生的時間。
  • success 如果是 false,那么該事件會在 problem 報表中進行統(tǒng)計。
  • data 不具有統(tǒng)計意義,它只在鏈路追蹤排查問題的時候有用。
  • businessData 用來給業(yè)務系統(tǒng)上報業(yè)務數(shù)據(jù),需要手動打點,之后用來做業(yè)務數(shù)據(jù)分析。

Message 有兩個子類 Event 和 Transaction,區(qū)別在于 Transaction 帶有 duration 屬性,用來標識該 transaction 耗時多久,可以用來做 max time,min time,avg time,p90,p95 等。

而 event 指的是發(fā)生了某件事,只能用來統(tǒng)計發(fā)生了多少次,并沒有時間長短的概念。

Transaction 有個屬性 children,可以嵌套 Transaction 或者 Event,最后形成一顆樹狀結(jié)構(gòu),用來做 trace,我們稍后再介紹。

下面表格示例一下打點數(shù)據(jù),這樣比較直觀一些:

client

簡單介紹幾點內(nèi)容:

①type 為 URL、SQL、Redis、FeignClient、HttpClient 等這些數(shù)據(jù),屬于自動埋點的范疇。

通常做 APM 系統(tǒng)的,都要完成一些自動埋點的工作,這樣應用開發(fā)者不需要做任何的埋點工作,就能看到很多有用的數(shù)據(jù)。像最后兩行的 type=Order 屬于手動埋點的數(shù)據(jù)。

②打點需要特別注意 type、name、status 的維度“爆炸”,它們的組合太多會非常消耗資源,它可能會直接拖垮我們的 Dog 系統(tǒng)。

type 的維度可能不會太多,但是我們可能需要注意開發(fā)者可能會濫用 name 和 status,所以我們一定要做 normalize(如 url 可能是帶動態(tài)參數(shù)的,需要格式化處理一下)。

③表格中的最后兩條是開發(fā)者手動埋點的數(shù)據(jù),通常用來統(tǒng)計特定的場景,比如我想知道某個方法被調(diào)用的情況,調(diào)用次數(shù)、耗時、是否拋異常、入?yún)ⅰ⒎祷刂档取?/p>

因為自動埋點是業(yè)務不想關的,冷冰冰的數(shù)據(jù),開發(fā)者可能想要埋一些自己想要統(tǒng)計的數(shù)據(jù)。

④開發(fā)者在手動埋點的時候,還可以上報更多的業(yè)務相關的數(shù)據(jù)上來,參考表格最后一列,這些數(shù)據(jù)可以做業(yè)務分析來用。

比如我是做支付系統(tǒng)的,通常一筆支付訂單會涉及到非常多的步驟(國外的支付和大家平時使用的微信、支付寶稍微有點不一樣),通過上報每一個節(jié)點的數(shù)據(jù),最后我就可以在 Dog 上使用 bizId 來將整個鏈路串起來,在排查問題的時候是非常有用的(我們在做支付業(yè)務的時候,支付的成功率并沒有大家想象的那么高,很多節(jié)點可能出問題)。

客戶端設計

上一節(jié)我們介紹了單條 message 的數(shù)據(jù),這節(jié)我們覆蓋一下其他內(nèi)容。

首先,我們介紹客戶端的 API 使用:

  1. public void test() { 
  2.   Transaction transaction = Dog.newTransaction("URL""/test/user"); 
  3.   try { 
  4.     Dog.logEvent("User""name-xxx""status-yyy"); 
  5.  
  6.     // do something 
  7.  
  8.     Transaction sql = Dog.newTransaction("SQL""UserMapper.insert"); 
  9.     // try-catch-finally 
  10.  
  11.     transaction.setStatus("xxxx"); 
  12.     transaction.setSuccess(true/false); 
  13.   } catch (Throwable throwable) { 
  14.     transaction.setSuccess(false); 
  15.     transaction.setData(Throwables.getStackTraceAsString(throwable)); 
  16.     throw throwable; 
  17.   } finally { 
  18.     transaction.finish(); 
  19.   } 

上面的代碼示例了如何嵌套使用 Transaction 和 Event,當最外層的 Transaction 在 finally 代碼塊調(diào)用 finish() 的時候,完成了一棵樹的創(chuàng)建,進行消息投遞。

我們往 Kafka 中投遞的并不是一個 Message 實例,因為一次請求會產(chǎn)生很多的 Message 實例,而是應該組織成 一個 Tree 實例以后進行投遞。

下圖描述 Tree 的各個屬性:

tree

Tree 的屬性很好理解,它持有 root transaction 的引用,用來遍歷整顆樹。另外就是需要攜帶機器信息 messageEnv。

treeId 應該有個算法能保證全局唯一,簡單介紹下 Dog 的實現(xiàn):${appName}-${encode(ip)}-${當前分鐘}-${自增id}。

下面簡單介紹幾個 tree id 相關的內(nèi)容,假設一個請求從 A->B->C->D 經(jīng)過 4 個應用。

A 是入口應用,那么會有:

  • 總共會有 4 個 Tree 對象實例從 4 個應用投遞到 Kafka,跨應用調(diào)用的時候需要傳遞 treeId, parentTreeId, rootTreeId 三個參數(shù)。
  • A 應用的 treeId 是所有節(jié)點的 rootTreeId。
  • B 應用的 parentTreeId 是 A 的 treeId,同理 C 的 parentTreeId 是 B 應用的 treeId。
  • 在跨應用調(diào)用的時候,比如從 A 調(diào)用 B 的時候,為了知道 A 的下一個節(jié)點是什么,所以在 A 中提前為 B 生成 treeId,B 收到請求后,如果發(fā)現(xiàn) A 已經(jīng)為它生成了 treeId,直接使用該 treeId。

大家應該也很容易知道,通過這幾個 tree id,我們是想要實現(xiàn) trace 的功能。

介紹完了 tree 的內(nèi)容,我們再簡單討論下應用集成方案。

集成無外乎兩種技術(shù),一種是通過 javaagent 的方式,在啟動腳本中,加上相應的 agent。

這種方式的優(yōu)點是開發(fā)人員無感知,運維層面就可以做掉,當然開發(fā)者如果想要手動做一些埋點,可能需要再提供一個簡單的 client jar 包給開發(fā)者,用來橋接到 agent 里。另一種就是提供一個 jar 包,由開發(fā)者來引入這個依賴。

兩種方案各有優(yōu)缺點,Pinpoint 和 Skywalking 使用的是 javaagent 方案,Zipkin、Jaeger、Cat 使用的是第二種方案,Dog 也使用第二種手動添加依賴的方案。

通常來說,做 Traces 的系統(tǒng)選擇使用 javaagent 方案比較省心,因為這類系統(tǒng) agent 做完了所有需要的埋點,無需應用開發(fā)者感知。

最后,我再簡單介紹一下 Heartbeat 的內(nèi)容,這部分內(nèi)容其實最簡單,但是能做出很多花花綠綠的圖表出來,可以實現(xiàn)面向老板編程。

heartbeat-sample

前面我們介紹了 Message 有兩個子類 Event 和 Transaction,這里我們再加一個子類 Heartbeat,用來上報心跳數(shù)據(jù)。

我們主要收集了 thread、os、gc、heap、client 運行情況(產(chǎn)生多少個 tree,數(shù)據(jù)大小,發(fā)送失敗數(shù))等,同時也提供了 api 讓開發(fā)者自定義數(shù)據(jù)進行上報。

Dog client 會開啟一個后臺線程,每分鐘運行一次 Heartbeat 收集程序,上報數(shù)據(jù)。

再介紹細一些。核心結(jié)構(gòu)是一個 Map

關于客戶端,這里就介紹這么多了,其實實際編碼過程中,還有一些細節(jié)需要處理。

比如如果一棵樹太大了要怎么處理,比如沒有 rootTransaction 的情況怎么處理(開發(fā)者只調(diào)用了 Dog.logEvent(...)),比如內(nèi)層嵌套的 transaction 沒有調(diào)用 finish 怎么處理等等。

Dog server 設計

下圖示例了 server 的整體設計,值得注意的是,我們這里對線程的使用非常地克制,圖中只有 3 個工作線程。

server-design

首先是 Kafka Consumer 線程,它負責批量消費消息,從 Kafka 集群中消費到的是一個個 Tree 的實例,接下來考慮怎么處理它。

在這里,我們需要將樹狀結(jié)構(gòu)的 message 鋪平,我們把這一步叫做 deflate,并且做一些預處理,形成下面的結(jié)構(gòu):

deflate

接下來,我們就將 DeflateTree 分別投遞到兩個 Disruptor 實例中,我們把 Disruptor 設計成單線程生產(chǎn)和單線程消費,主要是性能上的考慮。

消費線程根據(jù) DeflateTree 的屬性使用綁定好的 Processor 進行處理,比如 DeflateTree 中 List problmes 不為空,同時自己綁定了 ProblemProcessor,那么就需要調(diào)用 ProblemProcessor 來處理。

科普時間:Disruptor 是一個高性能的隊列,性能比 JDK 中的 BlockingQueue 要好。

這里我們使用了 2 個 Disruptor 實例,當然也可以考慮使用更多的實例,這樣每個消費線程綁定的 processor 就更少。

我們這里把 Processor 綁定到了 Disruptor 實例上,其實原因也很簡單,為了性能考慮,我們想讓每個 processor 只有單線程使用它。

單線程操作可以減少線程切換帶來的開銷,可以充分利用到系統(tǒng)緩存,以及在設計 processor 的時候,不用考慮并發(fā)讀寫的問題。

這里要考慮負載均衡的情況,有些 processor 是比較耗費 CPU 和內(nèi)存資源的,一定要合理分配,不能把壓力最大的幾個任務分到同一個線程中去了。

核心的處理邏輯都在各個 processor 中,它們負責數(shù)據(jù)計算。接下來,我把各個 processor 需要做的主要內(nèi)容介紹一下,畢竟能看到這里的開發(fā)者,應該真的是對 APM 的數(shù)據(jù)處理比較感興趣的。

①Transaction processor

transaction processor 是系統(tǒng)壓力最大的地方,它負責報表統(tǒng)計,雖然 Message 有 Transaction 和 Event 兩個主要的子類,但是在實際的一顆樹中,絕大部分的節(jié)點都是 transaction 類型的數(shù)據(jù)。

transaction-type

下圖是 transaction processor 內(nèi)部的一個主要的數(shù)據(jù)結(jié)構(gòu),最外層是一個時間,我們用分鐘時間來組織,我們最后在持久化的時候,也是按照分鐘來存的。

第二層的 HostKey 代表哪個應用以及哪個 ip 來的數(shù)據(jù),第三層是 type、name、status 的組合。最內(nèi)層的 Statistics 是我們的數(shù)據(jù)統(tǒng)計模塊。

transaction-statistics

另外我們也可以看到,這個結(jié)構(gòu)到底會消耗多少內(nèi)存,其實主要取決于我們的 type、name、status 的組合也就是 ReportKey 會不會很多,也就是我們前面在說客戶端打點的時候,要避免維度爆炸。

最外層結(jié)構(gòu)代表的是時間的分鐘表示,我們的報表是基于每分鐘來進行統(tǒng)計的,之后持久化到 ClickHouse 中。

但是我們的使用者在看數(shù)據(jù)的時候,可不是一分鐘一分鐘看的,所以需要做數(shù)據(jù)聚合。

下面展示兩條數(shù)據(jù)是如何做聚合的,在很多數(shù)據(jù)的時候,都是按照同樣的方法進行合并。

transaction-cal

你仔細想想就會發(fā)現(xiàn),前面幾個數(shù)據(jù)的計算都沒毛病,但是 P90,P95 和 P99 的計算是不是有點欺騙人啊?

其實這個問題是真的無解的,我們只能想一個合適的數(shù)據(jù)計算規(guī)則,然后我們再想想這種計算規(guī)則,可能算出來的值也是差不多可用的就好了。

另外有一個細節(jié)問題,我們需要讓內(nèi)存中的數(shù)據(jù)提供最近 30 分鐘的統(tǒng)計信息,30 分鐘以上的才從 DB 讀取。然后做上面介紹的 merge 操作。

討論:我們是否可以丟棄一部分實時性,我們每分鐘持久化一次,我們讀取的數(shù)據(jù)都是從 DB 來的,這樣可行嗎?

不行,因為我們的數(shù)據(jù)是從 Kafka 消費來的,本身就有一定的滯后性,我們?nèi)绻陂_始一分鐘的時候就持久化上一分鐘的數(shù)據(jù),可能之后還會收到前面時間的消息,這種情況處理不了。

比如我們要統(tǒng)計最近一小時的情況,那么就會有 30 分鐘的數(shù)據(jù)從各個機器中獲得,有 30 分鐘的數(shù)據(jù)從 DB 獲得,然后做合并。

這里值得一提的是,在 transaction 報表中,count、failCount、min、max、avg 是比較好算的。

但是 P90、P95、P99 其實不太好算,我們需要一個數(shù)組結(jié)構(gòu),來記錄這一分鐘內(nèi)所有的事件的時間,然后進行計算。

我們這里討巧使用了 Apache DataSketches,它非常好用,這里我就不展開了,感興趣的同學可以自己去看一下。

到這里,大家可以去想一想儲存到 ClickHouse 的數(shù)據(jù)量的問題。app_name、ip、type、name、status 的不同組合,每分鐘一條數(shù)據(jù)。

②Sample Processor

sample processor 消費 deflate tree 中的 List transactions 和 List<:Event> events 的數(shù)據(jù)。

我們也是按照分鐘來采樣,最終每分鐘,對每個 type、name、status 的不同組合,采集最多 5 個成功、5 個失敗、5 個慢處理。

相對來說,這個還是非常簡單的,它的核心結(jié)構(gòu)如下圖:

sample-structure

結(jié)合 Sample 的功能來看比較容易理解:

sample

③Problem Processor

在做 deflate 的時候,所有 success=false 的 Message,都會被放入 List problmes 中,用來做錯誤統(tǒng)計。

Problem 內(nèi)部的數(shù)據(jù)結(jié)構(gòu)如下圖:

problem-structure

大家看下這個圖,其實也就知道要做什么了,我就不啰嗦了。其中 samples 我們每分鐘保存 5 個 treeId。

順便也再展示下 Problem 的視圖:

problem

關于持久化,我們是存到了 ClickHouse 中,其中 sample 用逗號連接成一個字符串,problem_data 的列如下:

  1. event_date, event_time, app_name, ip, type, name, status, count, sample 

④Heartbeat Processor

Heartbeat 處理 List heartbeats 的數(shù)據(jù),題外話,正常情況下,一顆樹里面只有一個 Heartbeat 實例。

前面我也簡單提到了一下,我們 Heartbeat 中用來展示圖表的核心數(shù)據(jù)結(jié)構(gòu)是一個 Map

收集到的 key-value 數(shù)據(jù)如下所示:

  1.   "os.systemLoadAverage": 1.5, 
  2.   "os.committedVirtualMemory": 1234562342, 
  3.   "os.openFileDescriptorCount": 800, 
  4.   "thread.count": 600, 
  5.   "thread.httpThreadsCount": 250, 
  6.   "gc.ZGC Count": 234, 
  7.   "gc.ZGC Time(ms)": 123435, 
  8.   "heap.ZHeap": 4051233219, 
  9.   "heap.Metaspace": 280123212 

前綴是分類,后綴是圖的名稱。客戶端每分鐘收集一次數(shù)據(jù)進行上報,然后就可以做很多的圖了,比如下圖展示了在 heap 分類下的各種圖:

heartbeat-heap

Heartbeat processor 要做的事情很簡單,就是數(shù)據(jù)存儲,Dog UI 上的數(shù)據(jù)是直接從 ClickHouse 中讀取的。

heartbeat_data 的列如下:

  1. event_date, event_time, timestamp, app_name, ip, name, value 

⑤MessageTree Processor

前面我們多次提到了 Sample 的功能,這些采樣的數(shù)據(jù)幫助我們恢復現(xiàn)場,這樣我們可以通過 trace 視圖來跟蹤調(diào)用鏈。

trace

要做上面的這個 trace 視圖,我們需要上下游的所有的 tree 的數(shù)據(jù),比如上圖是 3 個 tree 實例的數(shù)據(jù)。

之前我們在客戶端介紹的時候說過,這幾個 tree 通過 parent treeId 和 root treeId 來組織。要做這個視圖,給我們提出的挑戰(zhàn)就是,我們需要保存全量的數(shù)據(jù)。

大家可以想一想這個問題,為啥要保存全量數(shù)據(jù),我們直接保存被 sample 到的數(shù)據(jù)不就好了嗎?

這里我們用到了 Cassandra 的能力,Cassandra 在這種 kv 的場景中,有非常不錯的性能,而且它的運維成本很低。

我們以 treeId 作為主鍵,另外再加 data 一個列即可,它是整個 tree 的實例數(shù)據(jù),數(shù)據(jù)類型是 blob,我們會先做一次 gzip 壓縮,然后再扔給 Cassandra。

⑥Business Processor

我們在介紹客戶端的時候說過,每個 Message 都可以攜帶 Business Data,不過只有應用開發(fā)者自己手動埋點的時候才會有。

當我們發(fā)現(xiàn)有業(yè)務數(shù)據(jù)的時候,我們會做另一個事情,就是把這個數(shù)據(jù)存儲到 ClickHouse 中,用來做業(yè)務分析。

我們其實不知道應用開發(fā)者到底會把它用在什么場景中,因為每個人負責的項目都不一樣,所以我們只能做一個通用的數(shù)據(jù)模型。

data-model

回過頭來看這個圖,BusinessData 中我們定義了比較通用的 userId 和 bizId,我們認為它們可能是每個業(yè)務場景會用到的東西。userId 就不用說了,bizId 大家可以做來記錄訂單 id,支付單 id 等。

然后我們提供了 3 個 String 類型的列 ext1、ext2、ext3 和兩個數(shù)值類型的列 extVal1 和 extVal2,它們可以用來表達你的業(yè)務相關的參數(shù)。

我們的處理當然也非常簡單,將這些數(shù)據(jù)存到 ClickHouse 中就可以了,表中主要有這些列:

  1. event_data, event_time, user, biz_id, timestamp, type, name, status, app_name、ip、success、ext1、ext2、ext3、ext_val1、ext_val2 

這些數(shù)據(jù)對我們 Dog 系統(tǒng)來說肯定不認識,因為我們也不知道你表達的是什么業(yè)務,type、name、status 是開發(fā)者自己定義的,ext1,ext2,ext3 分別代表什么意思,我們都不知道,我們只負責存儲和查詢。

這些業(yè)務數(shù)據(jù)非常有用,基于這些數(shù)據(jù),我們可以做很多的數(shù)據(jù)報表出來。因為本文是討論 APM 的,所以該部分內(nèi)容就不再贅述了。

其他

ClickHouse 需要批量寫入,不然肯定是撐不住的,一般一個 batch 至少 10000 行數(shù)據(jù)。

我們在 Kafka 這層控制了,一個 app_name + ip 的數(shù)據(jù),只會被同一個 dog-server 消費,當然也不是說被多個 dog-server 消費會有問題,但是這樣寫入 ClickHouse 的數(shù)據(jù)就會更多。

還有個關鍵的點,前面我們說了每個 processor 是由單線程進行訪問的,但是有一個問題,那就是來自 Dog UI 上的請求可怎么辦?

這里我想了個辦法,那就是將請求放到一個 Queue 中,由 Kafka Consumer 那個線程來消費,它會將任務扔到兩個 Disruptor 中。

比如這個請求是 transaction 報表請求,其中一個 Disruptor 的消費者會發(fā)現(xiàn)這個是自己要干的,就會去執(zhí)行這個任務。

小結(jié)

如果你了解 Cat 的話,可以看到 Dog 在很多地方和 Cat 有相似之處,或者直接說“抄”也行,之前我們也考慮過直接使用 Cat 或者在 Cat 的基礎上做二次開發(fā)。

但是我看完 Cat 的源碼后,就放棄了這個想法,仔細想想,只是借鑒 Cat 的數(shù)據(jù)模型,然后我們自己寫一套 APM 其實不是很難,所以有了我們這個項目。

行文需要,很多地方我都避重就輕,因為這不是什么源碼分析的文章,沒必要處處談細節(jié),主要是給讀者一個全貌,讀者能通過我的描述大致想到需要處理哪些事情,需要寫哪些代碼,那就當我表述清楚了。

歡迎大家提出自己的疑問或者想法,有不懂或者我有錯漏的地方,歡迎指正~

作者:javadoop

編輯:陶家龍

出處:www.javadoop.com/post/apm

責任編輯:武曉燕 來源: javadoop
相關推薦

2021-07-15 14:29:06

LRU算法

2024-12-03 16:49:58

2020-09-17 14:04:32

拷貝

2020-09-15 08:55:07

算法數(shù)據(jù)基礎

2021-10-31 07:38:37

排序算法代碼

2023-09-18 09:10:11

Golang高性能緩存庫

2020-09-16 14:17:42

flat方法

2021-06-09 07:01:30

前端CallApply

2015-09-11 20:16:46

CAAPM應用性能

2021-05-18 07:52:31

PromiseAsyncAwait

2015-11-06 11:02:24

微信羅素生活

2024-08-06 10:16:52

Java AgentJava

2025-08-18 01:11:00

String類快手C++

2020-09-27 08:04:57

前端

2023-10-18 17:49:58

數(shù)據(jù)結(jié)構(gòu)隊列結(jié)構(gòu)

2025-08-12 02:55:00

2023-06-25 08:38:09

多線程循環(huán)打印

2025-08-11 02:25:00

2025-02-14 06:00:00

GoDNS協(xié)議gothdns

2022-05-12 18:30:12

SpringBoot循環(huán)依賴初始化
點贊
收藏

51CTO技術(shù)棧公眾號

狠狠爱免费视频| 国产精品国产一区二区| 国内毛片毛片毛片毛片毛片| 国产剧情一区二区在线观看| 亚洲午夜免费福利视频| 日本精品一区| 成人高潮片免费视频| 亚洲一区一卡| 美女精品视频一区| 一本加勒比北条麻妃| 综合久久伊人| 一本到一区二区三区| 中文字幕在线中文| 浮生影视网在线观看免费| 国产精品乡下勾搭老头1| 日韩av免费看网站| 欧美精品一区二区成人| 欧美午夜精彩| 日韩av在线直播| 天天干天天色天天干| 亚洲性色av| 亚洲一区在线观看网站| 亚洲第一在线综合在线| 日本高清视频免费观看| 久久电影网电视剧免费观看| 欧美在线观看网站| 欧美激情一区二区视频| 成人情趣视频网站| 亚洲精品视频久久| 性欧美18—19sex性高清| 午夜不卡一区| 欧美中文字幕一区二区三区亚洲| 秋霞无码一区二区| 在线观看中文| 一区视频在线播放| 日本一区二区三区视频免费看| 欧美视频一二区| 国产很黄免费观看久久| 成人xxxxx| 在线观看国产黄| 久久狠狠婷婷| 欧美亚洲国产成人精品| 中国一级免费毛片| 欧美成人中文| 欧美精品情趣视频| 国产黄在线免费观看| 91综合在线| 日韩一中文字幕| 国产又黄又粗又猛又爽的| 国产精品一区二区av交换| 亚洲成人网久久久| 国产一级免费片| 丁香婷婷成人| 欧美大片顶级少妇| 亚洲欧洲日韩综合| 91大神精品| 精品国产亚洲在线| 国产又粗又长又爽| 天堂99x99es久久精品免费| 亚洲福利精品在线| 五十路六十路七十路熟婆| 精品成人自拍视频| 日韩精品极品视频免费观看| 怡红院一区二区| 欧美综合自拍| 亚洲人成网站777色婷婷| 国产美女免费无遮挡| 成人三级视频| 欧美成人免费全部| 久久精品亚洲无码| 先锋影音久久久| 国产精品美女久久| 国产成人免费看一级大黄| 丁香婷婷综合网| 久久久久久九九九九| 国产一级片在线| 1区2区3区国产精品| 青草全福视在线| 91av久久| 欧美天天综合网| 91香蕉国产线在线观看| 国产精品久av福利在线观看| 亚洲美女久久久| 影音先锋男人看片资源| 国产一区观看| 日本三级韩国三级久久| 一区二区三区黄| 成人福利视频在线| 日本一区二区不卡高清更新| 精品51国产黑色丝袜高跟鞋| 午夜影院久久久| 亚洲最大综合网| 伊色综合久久之综合久久| 亚洲精品自拍第一页| 女教师淫辱の教室蜜臀av软件| 亚洲女同一区| 青青久久aⅴ北条麻妃| 亚洲一级视频在线观看| 菠萝蜜视频在线观看一区| 少妇免费毛片久久久久久久久| 怡红院在线观看| 91久久香蕉国产日韩欧美9色| 国产不卡的av| 精品国产乱码久久久久久1区2匹| 大胆欧美人体视频| 成年人视频免费| 懂色av一区二区三区免费观看| 免费观看国产成人| 亚洲www色| 欧洲精品在线观看| 日韩Av无码精品| 国产精品不卡| 日本精品久久久久影院| 亚洲成人黄色片| 国产精品女主播av| 116极品美女午夜一级| 1204国产成人精品视频| 日韩亚洲综合在线| 国产精品久久久久久人| 成人精品国产免费网站| 一区二区av| 亚洲欧美一区二区三区| 欧美成人精品高清在线播放| 任你操精品视频| 久久久精品性| 国产亚洲精品美女久久久m| 黄网站免费在线观看| 91福利社在线观看| 黄色短视频在线观看| 国内精品福利| 444亚洲人体| 乱人伦中文视频在线| 欧美视频在线观看一区| 国产毛片久久久久久久| 久久久久国产精品一区三寸 | 精品少妇人妻av免费久久洗澡| aa亚洲一区一区三区| 深夜福利一区二区| 亚洲综合网av| 中文字幕在线免费不卡| 麻豆一区二区三区视频| 欧洲激情综合| 国产精品一区二区久久国产| 国产youjizz在线| 欧美亚洲国产一区二区三区| jizz18女人高潮| 美腿丝袜一区二区三区| 色小子综合网| 久久这里只有精品视频首页| 一起草av在线| 亚洲欧洲国产日本综合| 涩涩网站在线看| 亚洲区综合中文字幕日日| 91九色国产社区在线观看| 免费在线毛片网站| 91精品国产色综合久久ai换脸| 久久国产高清视频| 国产91综合网| 日韩视频免费播放| 亚洲黄页网站| 日韩免费观看高清| 欧美性天天影视| 欧美一区欧美二区| 久久中文字幕在线观看| 成人免费视频免费观看| 欧美丰满熟妇bbbbbb百度| 精品毛片免费观看| 91精品在线观| 精品人人视频| 中文字幕欧美日韩在线| av免费在线观看不卡| 亚洲自拍偷拍图区| 免费看黄色aaaaaa 片| 男女性色大片免费观看一区二区 | 亚洲最大福利视频网| 污的网站在线观看| 国产丝袜一区二区三区免费视频| 激情视频网站在线观看| 国产精品久久久久久久久图文区| 女王人厕视频2ⅴk| 中国女人久久久| 亚洲欧洲国产精品久久| 亚洲精品观看| 国产黑人绿帽在线第一区| 国产盗摄在线观看| 日韩激情视频在线播放| 91精东传媒理伦片在线观看| 亚洲精品视频在线看| 日韩一级视频在线观看| 激情偷乱视频一区二区三区| 自拍日韩亚洲一区在线| 欧美日韩一二三四| 91黄在线观看| 黄频免费在线观看| 久久成人精品视频| 美丽的姑娘在线观看免费动漫| 777午夜精品免费视频| 五月婷婷开心网| 99re热视频这里只精品| 99日在线视频| 日韩国产欧美在线观看| 国产精品视频二| 999国产精品| 欧美精品一区二区三区四区五区| 精品一区二区三区中文字幕在线 | av在线国产精品| 日本91av在线播放| 日韩伦理电影网站| 久久精品91久久香蕉加勒比| 国产一级片在线| 亚洲国产精品大全| 国产黄色小视频在线观看| 欧美性色欧美a在线播放| 欧美成人aaaaⅴ片在线看| 国产精品久久毛片a| aa一级黄色片| 不卡视频一二三四| 欧美xxxxxbbbbb| 久久超级碰视频| 最新中文字幕免费视频| 性久久久久久| 久久国产亚洲精品无码| 亚洲久久视频| 久久99中文字幕| 欧美日韩一区二区国产| 免费观看黄色大片| 国产精品毛片久久| 国产一区视频导航| 香蕉视频在线网址| 日韩成人激情| 人禽交欧美网站免费| 美女视频亚洲色图| 国产一区二区三区四区五区在线| 日韩激情欧美| 亚洲xxx视频| 国产免费av国片精品草莓男男| 国产精品久久久久久亚洲影视 | 黄色av电影网站| 国产精品一二三区| 一级网站在线观看| 激情综合色播五月| 韩国一区二区在线播放| 黑人巨大精品欧美一区| 17c国产在线| 精品一区二区免费| 911福利视频| 狠狠色狠狠色综合系列| 欧美又黄又嫩大片a级| 国产一区二区三区四| 色姑娘综合天天| 国产成人精品www牛牛影视| 少妇丰满尤物大尺度写真| 国产精品亚洲成人| 性久久久久久久久久久| 久久伊人蜜桃av一区二区| 国产精品揄拍100视频| 久久香蕉国产线看观看99| 国产呦小j女精品视频| 久久久九九九九| 一级黄色录像毛片| 亚洲欧美怡红院| 免费麻豆国产一区二区三区四区| 亚洲成av人片在线观看| 久久一区二区三区视频| 在线观看av一区二区| 亚洲图片欧美在线| 欧美mv日韩mv亚洲| 深夜影院在线观看| 色妞欧美日韩在线| 欧美xxxx黑人又粗又长| 欧美亚洲日本黄色| 91精品国产一区二区在线观看| 不卡日韩av| 色棕色天天综合网| 中文字幕一区二区三区四区五区| 国产精品99一区二区| 日日鲁鲁鲁夜夜爽爽狠狠视频97| 日本vs亚洲vs韩国一区三区| 久久6免费视频| av资源站一区| 欧美成人短视频| 亚洲va欧美va国产va天堂影院| 国产www在线| 91麻豆精品国产91久久久使用方法| www.久久色| 亚洲三级av在线| 色呦呦在线视频| 国产精品高精视频免费| 精品一区视频| 日本精品一区二区三区高清 久久 日本精品一区二区三区不卡无字幕 | 色棕色天天综合网| 欧美与动交zoz0z| 久久成人免费| 亚洲成a人片在线www| 久久久精品国产免大香伊| 国产在线一卡二卡| 日本电影亚洲天堂一区| 蜜桃av中文字幕| 日韩在线观看免费av| 成人国产电影在线观看| 91免费视频国产| 精品99久久| 老太脱裤让老头玩ⅹxxxx| 麻豆精品一区二区av白丝在线| 久久久午夜精品福利内容| 中文字幕在线观看不卡| 久久久久在线视频| 精品精品欲导航| 2021国产在线| 国产精品入口免费视频一| 欧美毛片免费观看| 久久福利一区二区| 精品一区二区免费在线观看| 老熟妇一区二区| 黑人巨大精品欧美一区二区三区| 国产成人精品亚洲精品色欲| 正在播放亚洲1区| 日韩电影免费观| 精品不卡一区二区三区| 欧美久久99| 国产欧美精品一二三| 中文字幕成人在线观看| 无码人妻精品一区二| 日韩精品免费综合视频在线播放 | 国产精品国产三级国产aⅴ9色 | 天天人人精品| 久久久xxx| 中文字幕成人动漫| 疯狂蹂躏欧美一区二区精品| 天天操天天干天天| 欧美激情伊人电影| eeuss鲁片一区二区三区| 午夜啪啪福利视频| 国产一二精品视频| 男人av资源站| 4hu四虎永久在线影院成人| 欧美jizzhd69巨大| 成人亚洲激情网| 91精品1区| 欧美专区第二页| 亚洲激情五月婷婷| 亚洲产国偷v产偷v自拍涩爱| 欧美人与性动交| www.成人网| 国产素人在线观看| 久久综合国产精品| 日韩一级片中文字幕| 中文字幕v亚洲ⅴv天堂| 成人午夜sm精品久久久久久久| 视频一区亚洲| 久热成人在线视频| 国产97免费视频| 精品国产区一区| 国内精品国产三级国产aⅴ久| 91中文字幕精品永久在线| 亚洲一区在线不卡| 国产精品色呦呦| 国产一区二区三区中文字幕| 播播国产欧美激情| 久久伊人精品| 日韩亚洲欧美视频| av男人天堂一区| 男操女视频网站| 久久精品国产欧美亚洲人人爽| 日韩一区二区三区精品| 欧美精品自拍视频| 久久久久久久久久久久久女国产乱 | 国产又粗又猛又黄又爽无遮挡| 久久精品最新地址| 澳门成人av| 国产一级特黄a大片免费| 成人欧美一区二区三区小说| 亚洲第一视频在线播放| 4388成人网| 亚洲国产一区二区在线观看| www.美色吧.com| 在线中文字幕一区二区| 99自拍视频在线观看| 国产在线一区二区三区播放| 日韩1区2区3区| 久久精品视频免费在线观看| 亚洲国产成人爱av在线播放| 亚洲成人一区在线观看| 国产一级大片免费看| 久久精品一二三| 国产精品久久久久久久成人午夜| 韩国欧美亚洲国产| 欧美激情理论| 亚洲一级av无码毛片精品| 欧美日韩激情一区| av女在线播放| 二级片在线观看| 国产亚洲视频系列| 精品国产无码一区二区| 国产精品9999| 伊人天天综合| 小嫩苞一区二区三区| 精品无人国产偷自产在线| 欧美国产中文高清| 亚洲77777|