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

Kafka 核心全面總結(jié),高可靠高性能核心原理探究

開發(fā) 架構(gòu)
為了提升系統(tǒng)的吞吐,一個(gè) topic 下通常有多個(gè) partition,partition 分布在不同的 Broker 上,用于存儲 topic 的消息,這使 Kafka 可以在多臺機(jī)器上處理、存儲消息,給 kafka 提供給了并行的消息處理能力和橫向擴(kuò)容能力。

你好,我是碼哥,可以叫我靚仔

作者:mo

引言

在探究 Kafka 核心知識之前,我們先思考一個(gè)問題:什么場景會(huì)促使我們使用 Kafka?  說到這里,我們頭腦中或多或少會(huì)蹦出異步解耦和削峰填谷等字樣,是的,這就是 Kafka 最重要的落地場景。

異步解耦:同步調(diào)用轉(zhuǎn)換成異步消息通知,實(shí)現(xiàn)生產(chǎn)者和消費(fèi)者的解耦。想象一個(gè)場景,在商品交易時(shí),在訂單創(chuàng)建完成之后,需要觸發(fā)一系列其他的操作,比如進(jìn)行用戶訂單數(shù)據(jù)的統(tǒng)計(jì)、給用戶發(fā)送短信、給用戶發(fā)送郵件等等。如果所有操作都采用同步方式實(shí)現(xiàn),將嚴(yán)重影響系統(tǒng)性能。針對此場景,我們可以利用消息中間件解耦訂單創(chuàng)建操作和其他后續(xù)行為。

削峰填谷:利用 broker 緩沖上游生產(chǎn)者瞬時(shí)突發(fā)的流量,使消費(fèi)者消費(fèi)流量整體平滑。對于發(fā)送能力很強(qiáng)的上游系統(tǒng),如果沒有消息中間件的保護(hù),下游系統(tǒng)可能會(huì)直接被壓垮導(dǎo)致全鏈路服務(wù)雪崩。想象秒殺業(yè)務(wù)場景,上游業(yè)務(wù)發(fā)起下單請求,下游業(yè)務(wù)執(zhí)行秒殺業(yè)務(wù)(庫存檢查,庫存凍結(jié),余額凍結(jié),生成訂單等等),下游業(yè)務(wù)處理的邏輯是相當(dāng)復(fù)雜的,并發(fā)能力有限,如果上游服務(wù)不做限流策略,瞬時(shí)可能把下游服務(wù)壓垮。針對此場景,我們可以利用 MQ 來做削峰填谷,讓高峰流量填充低谷空閑資源,達(dá)到系統(tǒng)資源的合理利用。

通過上述例子可以發(fā)現(xiàn)交易、支付等場景常需要異步解耦和削峰填谷功能解決問題,而交易、支付等場景對性能、可靠性要求特別高。那么,我們本文的主角 Kafka 能否滿足相應(yīng)要求呢?下面我們來探討下。

Kafka 宏觀認(rèn)知

在探究 Kafka 的高性能、高可靠性之前,我們從宏觀上來看下 Kafka 的系統(tǒng)架構(gòu):

圖片

如上圖所示,Kafka 由 Producer、Broker、Consumer 以及負(fù)責(zé)集群管理的 ZooKeeper 組成,各部分功能如下:

  • Producer:生產(chǎn)者,負(fù)責(zé)消息的創(chuàng)建并通過一定的路由策略發(fā)送消息到合適的 Broker;
  • Broker:服務(wù)實(shí)例,負(fù)責(zé)消息的持久化、中轉(zhuǎn)等功能;
  • Consumer :消費(fèi)者,負(fù)責(zé)從 Broker 中拉取(Pull)訂閱的消息并進(jìn)行消費(fèi),通常多個(gè)消費(fèi)者構(gòu)成一個(gè)分組,消息只能被同組中的一個(gè)消費(fèi)者消費(fèi);
  • ZooKeeper:負(fù)責(zé) broker、consumer 集群元數(shù)據(jù)的管理等;(注意:Producer 端直接連接 broker,不在 zk 上存任何數(shù)據(jù),只是通過 ZK 監(jiān)聽 broker 和 topic 等信息)

上圖消息流轉(zhuǎn)過程中,還有幾個(gè)特別重要的概念—主題(Topic)、分區(qū)(Partition)、分段(segment)、位移(offset)。

  • topic:消息主題。Kafka 按 topic 對消息進(jìn)行分類,我們在收發(fā)消息時(shí)只需指定 topic。
  • partition:分區(qū)。為了提升系統(tǒng)的吞吐,一個(gè) topic 下通常有多個(gè) partition,partition 分布在不同的 Broker 上,用于存儲 topic 的消息,這使 Kafka 可以在多臺機(jī)器上處理、存儲消息,給 kafka 提供給了并行的消息處理能力和橫向擴(kuò)容能力。另外,為了提升系統(tǒng)的可靠性,partition 通常會(huì)分組,且每組有一個(gè)主 partition、多個(gè)副本 partition,且分布在不同的 broker 上,從而起到容災(zāi)的作用。
  • segment:分段。宏觀上看,一個(gè) partition 對應(yīng)一個(gè)日志(Log)。由于生產(chǎn)者生產(chǎn)的消息會(huì)不斷追加到 log 文件末尾,為防止 log 文件過大導(dǎo)致數(shù)據(jù)檢索效率低下,Kafka 采取了分段和索引機(jī)制,將每個(gè) partition 分為多個(gè) segment,同時(shí)也便于消息的維護(hù)和清理。每個(gè) segment 包含一個(gè).log 日志文件、兩個(gè)索引(.index、timeindex)文件以及其他可能的文件。每個(gè) Segment 的數(shù)據(jù)文件以該段中最小的 offset 為文件名,當(dāng)查找 offset 的 Message 的時(shí)候,通過二分查找快找到 Message 所處于的 Segment 中。
  • offset:消息在日志中的位置,消息在被追加到分區(qū)日志文件的時(shí)候都會(huì)分配一個(gè)特定的偏移量。offset 是消息在分區(qū)中的唯一標(biāo)識,是一個(gè)單調(diào)遞增且不變的值。Kafka 通過它來保證消息在分區(qū)內(nèi)的順序性,不過 offset 并不跨越分區(qū),也就是說,Kafka 保證的是分區(qū)有序而不是主題有序。

Kafka 高可靠性、高性能探究

在對 Kafka 的整體系統(tǒng)框架及相關(guān)概念簡單了解后,下面我們來進(jìn)一步深入探討下高可靠性、高性能實(shí)現(xiàn)原理。

Kafka 高可靠性探究

Kafka 高可靠性的核心是保證消息在傳遞過程中不丟失,涉及如下核心環(huán)節(jié):

  • 消息從生產(chǎn)者可靠地發(fā)送至 Broker;-- 網(wǎng)絡(luò)、本地丟數(shù)據(jù);
  • 發(fā)送到 Broker 的消息可靠持久化;-- Pagecache 緩存落盤、單點(diǎn)崩潰、主從同步跨網(wǎng)絡(luò);
  • 消費(fèi)者從 Broker 消費(fèi)到消息且最好只消費(fèi)一次 -- 跨網(wǎng)絡(luò)消息傳輸 。
消息從生產(chǎn)者可靠地發(fā)送至 Broker

為了保障消息從生產(chǎn)者可靠地發(fā)送至 Broker,我們需要確保兩點(diǎn);

  1. Producer 發(fā)送消息后,能夠收到來自 Broker 的消息保存成功 ack;
  2. Producer 發(fā)送消息后,能夠捕獲超時(shí)、失敗 ack 等異常 ack 并做處理。
ack 策略

針對問題 1,Kafka 為我們提供了三種 ack 策略,

  • Request.required.acks = 0:請求發(fā)送即認(rèn)為成功,不關(guān)心有沒有寫成功,常用于日志進(jìn)行分析場景;
  • Request.required.acks = 1:當(dāng) leader partition 寫入成功以后,才算寫入成功,有丟數(shù)據(jù)的可能;
  • Request.required.acks= -1:ISR 列表里面的所有副本都寫完以后,這條消息才算寫入成功,強(qiáng)可靠性保證;

為了實(shí)現(xiàn)強(qiáng)可靠的 kafka 系統(tǒng),我們需要設(shè)置 Request.required.acks= -1,同時(shí)還會(huì)設(shè)置集群中處于正常同步狀態(tài)的副本 follower 數(shù)量 min.insync.replicas>2,另外,設(shè)置 unclean.leader.election.enable=false 使得集群中 ISR 的 follower 才可變成新的 leader,避免特殊情況下消息截?cái)嗟某霈F(xiàn)。

消息發(fā)送策略

針對問題 2,kafka 提供兩類消息發(fā)送方式:同步(sync)發(fā)送和異步(async)發(fā)送,相關(guān)參數(shù)如下:

圖片

以 sarama 實(shí)現(xiàn)為例,在消息發(fā)送的過程中,無論是同步發(fā)送還是異步發(fā)送都會(huì)涉及到兩個(gè)協(xié)程--負(fù)責(zé)消息發(fā)送的主協(xié)程和負(fù)責(zé)消息分發(fā)的 dispatcher 協(xié)程。

異步發(fā)送

對于異步發(fā)送(ack != 0 場景,等于 0 時(shí)不關(guān)心寫 kafka 結(jié)果,后文詳細(xì)講解)而言,其流程大概如下:

  1. 在主協(xié)程中調(diào)用異步發(fā)送 kafka 消息的時(shí)候,其本質(zhì)是將消息體放進(jìn)了一個(gè) input 的 channel,只要入 channel 成功,則這個(gè)函數(shù)直接返回,不會(huì)產(chǎn)生任何阻塞。相反,如果入 channel 失敗,則會(huì)返回錯(cuò)誤信息。因此調(diào)用 async 寫入的時(shí)候返回的錯(cuò)誤信息是入 channel 的錯(cuò)誤信息,至于具體最終消息有沒有發(fā)送到 kafka 的 broker,我們無法從返回值得知。
  2. 當(dāng)消息進(jìn)入 input 的 channel 后,會(huì)有另一個(gè)dispatcher 的協(xié)程負(fù)責(zé)遍歷 input,來真正發(fā)送消息到特定 Broker 上的主 Partition 上。發(fā)送結(jié)果通過一個(gè)異步協(xié)程進(jìn)行監(jiān)聽,循環(huán)處理 err channel 和 success channel,出現(xiàn)了 error 就記一個(gè)日志。因此異步寫入場景時(shí),寫 kafka 的錯(cuò)誤信息,我們暫時(shí)僅能夠從這個(gè)錯(cuò)誤日志來得知具體發(fā)生了什么錯(cuò),并且也不支持我們自建函數(shù)進(jìn)行兜底處理,這一點(diǎn)在 trpc-go 的官方也得到了承認(rèn)。

同步發(fā)送

同步發(fā)送(ack != 0 場景)是在異步發(fā)送的基礎(chǔ)上加以條件限制實(shí)現(xiàn)的。同步消息發(fā)送在 newSyncProducerFromAsyncProducer 中開啟兩個(gè)異步協(xié)程處理消息成功與失敗的“回調(diào)”,并使用 waitGroup 進(jìn)行等待,從而將異步操作轉(zhuǎn)變?yōu)橥讲僮鳎淞鞒檀蟾湃缦拢?/p>

通過上述分析可以發(fā)現(xiàn),kafka 消息發(fā)送本質(zhì)上都是異步的,不過同步發(fā)送通過 waitGroup 將異步操作轉(zhuǎn)變?yōu)橥讲僮鳌M桨l(fā)送在一定程度上確保了我們在跨網(wǎng)絡(luò)向 Broker 傳輸消息時(shí),消息一定可以可靠地傳輸?shù)?Broker。因?yàn)樵谕桨l(fā)送場景我們可以明確感知消息是否發(fā)送至 Broker,若因網(wǎng)絡(luò)抖動(dòng)、機(jī)器宕機(jī)等故障導(dǎo)致消息發(fā)送失敗或結(jié)果不明,可通過重試等手段確保消息至少一次(at least once) 發(fā)送到 Broker。另外,Kafka(0.11.0.0 版本后)還為 Producer 提供兩種機(jī)制來實(shí)現(xiàn)精確一次(exactly once) 消息發(fā)送:冪等性(Idempotence)和事務(wù)(Transaction)。

小結(jié)

通過 ack 策略配置、同步發(fā)送、事務(wù)消息組合能力,我們可以實(shí)現(xiàn)exactly once 語意跨網(wǎng)絡(luò)向 Broker 傳輸消息。但是,Producer 收到 Broker 的成功 ack,消息一定不會(huì)丟失嗎?為了搞清這個(gè)問題,我們首先要搞明白 Broker 在接收到消息后做了哪些處理。

發(fā)送到 Broker 的消息可靠持久化

為了確保 Producer 收到 Broker 的成功 ack 后,消息一定不在 Broker 環(huán)節(jié)丟失,我們核心要關(guān)注以下幾點(diǎn):

  • Broker 返回 Producer 成功 ack 時(shí),消息是否已經(jīng)落盤;
  • Broker 宕機(jī)是否會(huì)導(dǎo)致數(shù)據(jù)丟失,容災(zāi)機(jī)制是什么;
  • Replica 副本機(jī)制帶來的多副本間數(shù)據(jù)同步一致性問題如何解決;

Broker 異步刷盤機(jī)制

kafka 為了獲得更高吞吐,Broker 接收到消息后只是將數(shù)據(jù)寫入 PageCache 后便認(rèn)為消息已寫入成功,而 PageCache 中的數(shù)據(jù)通過 linux 的 flusher 程序進(jìn)行異步刷盤(刷盤觸發(fā)條:主動(dòng)調(diào)用 sync 或 fsync 函數(shù)、可用內(nèi)存低于閥值、dirty data 時(shí)間達(dá)到閥值),將數(shù)據(jù)順序?qū)懙酱疟P。消息處理示意圖如下:

由于消息是寫入到 pageCache,單機(jī)場景,如果還沒刷盤 Broker 就宕機(jī)了,那么 Producer 產(chǎn)生的這部分?jǐn)?shù)據(jù)就可能丟失。為了解決單機(jī)故障可能帶來的數(shù)據(jù)丟失問題,Kafka 為分區(qū)引入了副本機(jī)制。

Replica 副本機(jī)制

Kafka 每組分區(qū)通常有多個(gè)副本,同組分區(qū)的不同副本分布在不同的 Broker 上,保存相同的消息(可能有滯后)。副本之間是“一主多從”的關(guān)系,其中 leader 副本負(fù)責(zé)處理讀寫請求,follower 副本負(fù)責(zé)從 leader 拉取消息進(jìn)行同步。分區(qū)的所有副本統(tǒng)稱為 AR(Assigned Replicas),其中所有與 leader 副本保持一定同步的副本(包括 leader 副本在內(nèi))組成 ISR(In-Sync Replicas),與 leader 同步滯后過多的副本組成 OSR(Out-of-Sync Replicas),由此可見,AR=ISR+OSR。

follower 副本是否與 leader 同步的判斷標(biāo)準(zhǔn)取決于 Broker 端參數(shù) replica.lag.time.max.ms(默認(rèn)為 10 秒),follower 默認(rèn)每隔 500ms 向 leader fetch 一次數(shù)據(jù),只要一個(gè) Follower 副本落后 Leader 副本的時(shí)間不連續(xù)超過 10 秒,那么 Kafka 就認(rèn)為該 Follower 副本與 leader 是同步的。在正常情況下,所有的 follower 副本都應(yīng)該與 leader 副本保持一定程度的同步,即 AR=ISR,OSR 集合為空。

當(dāng) leader 副本所在 Broker 宕機(jī)時(shí),Kafka 會(huì)借助 ZK 從 follower 副本中選舉新的 leader 繼續(xù)對外提供服務(wù),實(shí)現(xiàn)故障的自動(dòng)轉(zhuǎn)移,保證服務(wù)可用。為了使選舉的新 leader 和舊 leader 數(shù)據(jù)盡可能一致,當(dāng) leader 副本發(fā)生故障時(shí),默認(rèn)情況下只有在 ISR 集合中的副本才有資格被選舉為新的 leader,而在 OSR 集合中的副本則沒有任何機(jī)會(huì)(可通過設(shè)置 unclean.leader.election.enable 改變)。

當(dāng) Kafka 通過多副本機(jī)制解決單機(jī)故障問題時(shí),同時(shí)也帶來了多副本間數(shù)據(jù)同步一致性問題。Kafka 通過高水位更新機(jī)制、副本同步機(jī)制、 Leader Epoch 等多種措施解決了多副本間數(shù)據(jù)同步一致性問題,下面我們來依次看下這幾大措施。

HW 和 LEO

首先,我們來看下兩個(gè)和 Kafka 中日志相關(guān)的重要概念 HW 和 LEO:

  • HW: High Watermark,高水位,表示已經(jīng)提交(commit)的最大日志偏移量,Kafka 中某條日志“已提交”的意思是 ISR 中所有節(jié)點(diǎn)都包含了此條日志,并且消費(fèi)者只能消費(fèi) HW 之前的數(shù)據(jù);
  • LEO: Log End Offset,表示當(dāng)前 log 文件中下一條待寫入消息的 offset;

如上圖所示,它代表一個(gè)日志文件,這個(gè)日志文件中有 8 條消息,0 至 5 之間的消息為已提交消息,5 至 7 的消息為未提交消息。日志文件的 HW 為 6,表示消費(fèi)者只能拉取到 5 之前的消息,而 offset 為 5 的消息對消費(fèi)者而言是不可見的。日志文件的 LEO 為 8,下一條消息將在此處寫入。

注意:所有副本都有對應(yīng)的 HW 和 LEO,只不過 Leader 副本比較特殊,Kafka 使用 Leader 副本的高水位來定義所在分區(qū)的高水位。換句話說,分區(qū)的高水位就是其 Leader 副本的高水位。Leader 副本和 Follower 副本的 HW 有如下特點(diǎn):

  • Leader HW:min(所有副本 LEO),為此 Leader 副本不僅要保存自己的 HW 和 LEO,還要保存 follower 副本的 HW 和 LEO,而 follower 副本只需保存自己的 HW 和 LEO;
  • Follower HW:min(follower 自身 LEO,leader HW)。

注意:為方便描述,下面Leader HW簡記為HWL,F(xiàn)ollower HW簡記為F,Leader LEO簡記為LEOL ,F(xiàn)ollower LEO簡記為LEOF。

下面我們演示一次完整的 HW / LEO 更新流程:

  1. 初始狀態(tài)

HWL=0,LEOL=0,HWF=0,LEOF=0。

  1. Follower 第一次 fetch
  • Leader收到Producer發(fā)來的一條消息完成存儲, 更新LEOL=1;
  • Follower從Leader fetch數(shù)據(jù),  Leader收到請求,記錄follower的LEOF =0,并且嘗試更新HWL =min(全部副本LEO)=0;
  • eade返回HWL=0和LEOL=1給Follower,F(xiàn)ollower存儲消息并更新LEOF =1, HW=min(LEOF,HWL)=0。
  1. Follower 第二次 fetch
  • Follower再次從Leader fetch數(shù)據(jù),  Leader收到請求,記錄follower的LEOF =1,并且嘗試更新HWL =min(全部副本LEO)=1;
  • leade返回HWL=1和LEOL=1給Follower,Leader收到請求,更新自己的 HW=min(LEOF,HWL)=1。

上述更新流程中 Follower 和 Leader 的 HW 更新有時(shí)間 GAP。如果 Leader 節(jié)點(diǎn)在此期間發(fā)生故障,則 Follower 的 HW 和 Leader 的 HW 可能會(huì)處于不一致狀態(tài),如果 Followe 被選為新的 Leader 并且以自己的 HW 為準(zhǔn)對外提供服務(wù),則可能帶來數(shù)據(jù)丟失或數(shù)據(jù)錯(cuò)亂問題。

KIP-101 問題:數(shù)據(jù)丟失&數(shù)據(jù)錯(cuò)亂 ^參 5^

數(shù)據(jù)丟失

第 1 步:

  1. 副本 B 作為 leader 收到 producer 的 m2 消息并寫入本地文件,等待副本 A 拉取。
  2. 副本 A 發(fā)起消息拉取請求,請求中攜帶自己的最新的日志 offset(LEO=1),B 收到后更新自己的 HW 為 1,并將 HW=1 的信息以及消息 m2 返回給 A。
  3. A 收到拉取結(jié)果后更新本地的 HW 為 1,并將 m2 寫入本地文件。發(fā)起新一輪拉取請求(LEO=2),B 收到 A 拉取請求后更新自己的 HW 為 2,沒有新數(shù)據(jù)只將 HW=2 的信息返回給 A,并且回復(fù)給 producer 寫入成功。此處的狀態(tài)就是圖中第一步的狀態(tài)。

第 2 步:

此時(shí),如果沒有異常,A 會(huì)收到 B 的回復(fù),得知目前的 HW 為 2,然后更新自身的 HW 為 2。但在此時(shí) A 重啟了,沒有來得及收到 B 的回復(fù),此時(shí) B 仍然是 leader。A 重啟之后會(huì)以 HW 為標(biāo)準(zhǔn)截?cái)嘧约旱娜罩荆驗(yàn)?A 作為 follower 不知道多出的日志是否是被提交過的,防止數(shù)據(jù)不一致從而截?cái)喽嘤嗟臄?shù)據(jù)并嘗試從 leader 那里重新同步。

第 3 步:

B 崩潰了,min.isr 設(shè)置的是 1,所以 zookeeper 會(huì)從 ISR 中再選擇一個(gè)作為 leader,也就是 A,但是 A 的數(shù)據(jù)不是完整的,從而出現(xiàn)了數(shù)據(jù)丟失現(xiàn)象。

問題在哪里?在于 A 重啟之后以 HW 為標(biāo)準(zhǔn)截?cái)嗔硕嘤嗟娜罩尽2唤財(cái)嘈胁恍校坎恍校驗(yàn)檫@個(gè)日志可能沒被提交過(也就是沒有被 ISR 中的所有節(jié)點(diǎn)寫入過),如果保留會(huì)導(dǎo)致日志錯(cuò)亂。

數(shù)據(jù)錯(cuò)亂

在分析日志錯(cuò)亂的問題之前,我們需要了解到 kafka 的副本可靠性保證有一個(gè)前提:在 ISR 中至少有一個(gè)節(jié)點(diǎn)。如果節(jié)點(diǎn)均宕機(jī)的情況下,是不保證可靠性的,在這種情況會(huì)出現(xiàn)數(shù)據(jù)丟失,數(shù)據(jù)丟失是可接受的。這里我們分析的問題比數(shù)據(jù)丟失更加槽糕,會(huì)引發(fā)日志錯(cuò)亂甚至導(dǎo)致整個(gè)系統(tǒng)異常,而這是不可接受的。

第 1 步:

  1. A 和 B 均為 ISR 中的節(jié)點(diǎn)。副本 A 作為 leader,收到 producer 的消息 m2 的請求后寫入 PageCache 并在某個(gè)時(shí)刻刷新到本地磁盤。
  2. 副本 B 拉取到 m2 后寫入 PageCage 后(尚未刷盤)再次去 A 中拉取新消息并告知 A 自己的 LEO=2,A 收到更新自己的 HW 為 1 并回復(fù)給 producer 成功。
  3. 此時(shí) A 和 B 同時(shí)宕機(jī),B 的 m2 由于尚未刷盤,所以 m2 消息丟失。此時(shí)的狀態(tài)就是第 1 步的狀態(tài)。

第 2 步:

由于 A 和 B 均宕機(jī),而 min.isr=1 并且 unclean.leader.election.enable=true(關(guān)閉 unclean 選擇策略),所以 Kafka 會(huì)等到第一個(gè) ISR 中的節(jié)點(diǎn)恢復(fù)并選為 leader,這里不幸的是 B 被選為 leader,而且還接收到 producer 發(fā)來的新消息 m3。注意,這里丟失 m2 消息是可接受的,畢竟所有節(jié)點(diǎn)都宕機(jī)了。

第 3 步:

A 恢復(fù)重啟后發(fā)現(xiàn)自己是 follower,而且 HW 為 2,并沒有多余的數(shù)據(jù)需要截?cái)啵蚤_始和 B 進(jìn)行新一輪的同步。但此時(shí) A 和 B 均沒有意識到,offset 為 1 的消息不一致了。

問題在哪里?在于日志的寫入是異步的,上面也提到 Kafka 的副本策略的一個(gè)設(shè)計(jì)是消息的持久化是異步的,這就會(huì)導(dǎo)致在場景二的情況下被選出的 leader 不一定包含所有數(shù)據(jù),從而引發(fā)日志錯(cuò)亂的問題。

Leader Epoch

為了解決上述缺陷,Kafka 引入了 Leader Epoch 的概念。leader epoch 和 raft 中的任期號的概念很類似,每次重新選擇 leader 的時(shí)候,用一個(gè)嚴(yán)格單調(diào)遞增的 id 來標(biāo)志,可以讓所有 follower 意識到 leader 的變化。而 follower 也不再以 HW 為準(zhǔn),每次奔潰重啟后都需要去 leader 那邊確認(rèn)下當(dāng)前 leader 的日志是從哪個(gè) offset 開始的。下面看下 Leader Epoch 是如何解決上面兩個(gè)問題的。

數(shù)據(jù)丟失解決

這里的關(guān)鍵點(diǎn)在于副本 A 重啟后作為 follower,不是忙著以 HW 為準(zhǔn)截?cái)嘧约旱娜罩荆窍劝l(fā)起 LeaderEpochRequest 詢問副本 B 第 0 代的最新的偏移量是多少,副本 B 會(huì)返回自己的 LEO 為 2 給副本 A,A 此時(shí)就知道消息 m2 不能被截?cái)啵?m2 得到了保留。當(dāng) A 選為 leader 的時(shí)候就保留了所有已提交的日志,日志丟失的問題得到解決。

如果發(fā)起 LeaderEpochRequest 的時(shí)候就已經(jīng)掛了怎么辦?這種場景下,不會(huì)出現(xiàn)日志丟失,因?yàn)楦北?A 被選為 leader 后不會(huì)截?cái)嘧约旱娜罩荆罩窘財(cái)嘀粫?huì)發(fā)生在 follower 身上。

數(shù)據(jù)錯(cuò)亂解決

這里的關(guān)鍵點(diǎn)還是在第 3 步,副本 A 重啟作為 follower 的第一步還是需要發(fā)起 LeaderEpochRequest 詢問 leader 當(dāng)前第 0 代最新的偏移量是多少,由于副本 B 已經(jīng)經(jīng)過換代,所以會(huì)返回給 A 第 1 代的起始偏移(也就是 1),A 發(fā)現(xiàn)沖突后會(huì)截?cái)嘧约浩屏繛?1 的日志,并重新開始和 leader 同步。副本 A 和副本 B 的日志達(dá)到了一致,解決了日志錯(cuò)亂。

小結(jié)

Broker 接收到消息后只是將數(shù)據(jù)寫入 PageCache 后便認(rèn)為消息已寫入成功,但是,通過副本機(jī)制并結(jié)合 ACK 策略可以大概率規(guī)避單機(jī)宕機(jī)帶來的數(shù)據(jù)丟失問題,并通過 HW、副本同步機(jī)制、 Leader Epoch 等多種措施解決了多副本間數(shù)據(jù)同步一致性問題,最終實(shí)現(xiàn)了 Broker 數(shù)據(jù)的可靠持久化。

消費(fèi)者從 Broker 消費(fèi)到消息且最好只消費(fèi)一次

Consumer 在消費(fèi)消息的過程中需要向 Kafka 匯報(bào)自己的位移數(shù)據(jù),只有當(dāng) Consumer 向 Kafka 匯報(bào)了消息位移,該條消息才會(huì)被 Broker 認(rèn)為已經(jīng)被消費(fèi)。因此,Consumer 端消息的可靠性主要和 offset 提交方式有關(guān),Kafka 消費(fèi)端提供了兩種消息提交方式:

正常情況下我們很難實(shí)現(xiàn) exactly once 語意的消息,通常是通過手動(dòng)提交+冪等實(shí)現(xiàn)消息的可靠消費(fèi)。

Kafka 高性能探究

Kafka 高性能的核心是保障系統(tǒng)低延遲、高吞吐地處理消息,為此,Kafaka 采用了許多精妙的設(shè)計(jì):

  • 異步發(fā)送
  • 批量發(fā)送
  • 壓縮技術(shù)
  • Pagecache 機(jī)制&順序追加落盤
  • 零拷貝
  • 稀疏索引
  • broker & 數(shù)據(jù)分區(qū)
  • 多 reactor 多線程網(wǎng)絡(luò)模型

異步發(fā)送

如上文所述,Kafka 提供了異步和同步兩種消息發(fā)送方式。在異步發(fā)送中,整個(gè)流程都是異步的。調(diào)用異步發(fā)送方法后,消息會(huì)被寫入 channel,然后立即返回成功。Dispatcher 協(xié)程會(huì)從 channel 輪詢消息,將其發(fā)送到 Broker,同時(shí)會(huì)有另一個(gè)異步協(xié)程負(fù)責(zé)處理 Broker 返回的結(jié)果。同步發(fā)送本質(zhì)上也是異步的,但是在處理結(jié)果時(shí),同步發(fā)送通過 waitGroup 將異步操作轉(zhuǎn)換為同步。使用異步發(fā)送可以最大化提高消息發(fā)送的吞吐能力。

批量發(fā)送

Kafka 支持批量發(fā)送消息,將多個(gè)消息打包成一個(gè)批次進(jìn)行發(fā)送,從而減少網(wǎng)絡(luò)傳輸?shù)拈_銷,提高網(wǎng)絡(luò)傳輸?shù)男屎屯掏铝俊afka 的批量發(fā)送消息是通過以下兩個(gè)參數(shù)來控制的:

  1. batch.size:控制批量發(fā)送消息的大小,默認(rèn)值為 16KB,可適當(dāng)增加 batch.size 參數(shù)值提升吞吐。但是,需要注意的是,如果批量發(fā)送的大小設(shè)置得過大,可能會(huì)導(dǎo)致消息發(fā)送的延遲增加,因此需要根據(jù)實(shí)際情況進(jìn)行調(diào)整。
  2. linger.ms:控制消息在批量發(fā)送前的等待時(shí)間,默認(rèn)值為 0。當(dāng) linger.ms 大于 0 時(shí),如果有消息發(fā)送,Kafka 會(huì)等待指定的時(shí)間,如果等待時(shí)間到達(dá)或者批量大小達(dá)到 batch.size,就會(huì)將消息打包成一個(gè)批次進(jìn)行發(fā)送。可適當(dāng)增加 linger.ms 參數(shù)值提升吞吐,比如 10 ~ 100。

在 Kafka 的生產(chǎn)者客戶端中,當(dāng)發(fā)送消息時(shí),如果啟用了批量發(fā)送,Kafka 會(huì)將消息緩存到緩沖區(qū)中。當(dāng)緩沖區(qū)中的消息大小達(dá)到 batch.size 或者等待時(shí)間到達(dá) linger.ms 時(shí),Kafka 會(huì)將緩沖區(qū)中的消息打包成一個(gè)批次進(jìn)行發(fā)送。如果在等待時(shí)間內(nèi)沒有達(dá)到 batch.size,Kafka 也會(huì)將緩沖區(qū)中的消息發(fā)送出去,從而避免消息積壓。

壓縮技術(shù)

Kafka 支持壓縮技術(shù),通過將消息進(jìn)行壓縮后再進(jìn)行傳輸,從而減少網(wǎng)絡(luò)傳輸?shù)拈_銷(壓縮和解壓縮的過程會(huì)消耗一定的 CPU 資源,因此需要根據(jù)實(shí)際情況進(jìn)行調(diào)整。),提高網(wǎng)絡(luò)傳輸?shù)男屎屯掏铝俊afka 支持多種壓縮算法,在 Kafka2.1.0 版本之前,僅支持 GZIP,Snappy 和 LZ4,2.1.0 后還支持 Zstandard 算法(Facebook 開源,能夠提供超高壓縮比)。這些壓縮算法性能對比(兩指標(biāo)都是越高越好)如下:

  • 吞吐量:LZ4>Snappy>zstd 和 GZIP,壓縮比:zstd>LZ4>GZIP>Snappy。

在 Kafka 中,壓縮技術(shù)是通過以下兩個(gè)參數(shù)來控制的:

  1. compression.type:控制壓縮算法的類型,默認(rèn)值為 none,表示不進(jìn)行壓縮。
  2. compression.level:控制壓縮的級別,取值范圍為 0-9,默認(rèn)值為-1。當(dāng)值為-1 時(shí),表示使用默認(rèn)的壓縮級別。

在 Kafka 的生產(chǎn)者客戶端中,當(dāng)發(fā)送消息時(shí),如果啟用了壓縮技術(shù),Kafka 會(huì)將消息進(jìn)行壓縮后再進(jìn)行傳輸。在消費(fèi)者客戶端中,如果消息進(jìn)行了壓縮,Kafka 會(huì)在消費(fèi)消息時(shí)將其解壓縮。注意:Broker 如果設(shè)置了和生產(chǎn)者不通的壓縮算法,接收消息后會(huì)解壓后重新壓縮保存。Broker 如果存在消息版本兼容也會(huì)觸發(fā)解壓后再壓縮。

Pagecache 機(jī)制&順序追加落盤

kafka 為了提升系統(tǒng)吞吐、降低時(shí)延,Broker 接收到消息后只是將數(shù)據(jù)寫入PageCache后便認(rèn)為消息已寫入成功,而 PageCache 中的數(shù)據(jù)通過 linux 的 flusher 程序進(jìn)行異步刷盤(避免了同步刷盤的巨大系統(tǒng)開銷),將數(shù)據(jù)順序追加寫到磁盤日志文件中。由于 pagecache 是在內(nèi)存中進(jìn)行緩存,因此讀寫速度非常快,可以大大提高讀寫效率。順序追加寫充分利用順序 I/O 寫操作,避免了緩慢的隨機(jī) I/O 操作,可有效提升 Kafka 吞吐。

如上圖所示,消息被順序追加到每個(gè)分區(qū)日志文件的尾部。

零拷貝

Kafka 中存在大量的網(wǎng)絡(luò)數(shù)據(jù)持久化到磁盤(Producer 到 Broker)和磁盤文件通過網(wǎng)絡(luò)發(fā)送(Broker 到 Consumer)的過程,這一過程的性能直接影響 Kafka 的整體吞吐量。傳統(tǒng)的 IO 操作存在多次數(shù)據(jù)拷貝和上下文切換,性能比較低。Kafka 利用零拷貝技術(shù)提升上述過程性能,其中網(wǎng)絡(luò)數(shù)據(jù)持久化磁盤主要用 mmap 技術(shù),網(wǎng)絡(luò)數(shù)據(jù)傳輸環(huán)節(jié)主要使用 sendfile 技術(shù)。

索引加速之 mmap

傳統(tǒng)模式下,數(shù)據(jù)從網(wǎng)絡(luò)傳輸?shù)轿募枰?4 次數(shù)據(jù)拷貝、4 次上下文切換和兩次系統(tǒng)調(diào)用。如下圖所示:

為了減少上下文切換以及數(shù)據(jù)拷貝帶來的性能開銷,Kafka使用mmap來處理其索引文件。Kafka中的索引文件用于在提取日志文件中的消息時(shí)進(jìn)行高效查找。這些索引文件被維護(hù)為內(nèi)存映射文件,這允許Kafka快速訪問和搜索內(nèi)存中的索引,從而加速在日志文件中定位消息的過程。mmap 將內(nèi)核中讀緩沖區(qū)(read buffer)的地址與用戶空間的緩沖區(qū)(user buffer)進(jìn)行映射,從而實(shí)現(xiàn)內(nèi)核緩沖區(qū)與應(yīng)用程序內(nèi)存的共享,省去了將數(shù)據(jù)從內(nèi)核讀緩沖區(qū)(read buffer)拷貝到用戶緩沖區(qū)(user buffer)的過程,整個(gè)拷貝過程會(huì)發(fā)生 4 次上下文切換,1 次CPU 拷貝和 2次 DMA 拷貝。

網(wǎng)絡(luò)數(shù)據(jù)傳輸之 sendfile

傳統(tǒng)方式實(shí)現(xiàn):先讀取磁盤、再用 socket 發(fā)送,實(shí)際也是進(jìn)過四次 copy。如下圖所示:

為了減少上下文切換以及數(shù)據(jù)拷貝帶來的性能開銷,Kafka 在 Consumer 從 Broker 讀數(shù)據(jù)過程中使用了 sendfile 技術(shù)。具體在這里采用的方案是通過 NIO 的 transferTo/transferFrom 調(diào)用操作系統(tǒng)的 sendfile 實(shí)現(xiàn)零拷貝。總共發(fā)生 2 次內(nèi)核數(shù)據(jù)拷貝、2 次上下文切換和一次系統(tǒng)調(diào)用,消除了 CPU 數(shù)據(jù)拷貝,如下:

稀疏索引

為了方便對日志進(jìn)行檢索和過期清理,kafka 日志文件除了有用于存儲日志的.log 文件,還有一個(gè)位移索引文件.index和一個(gè)時(shí)間戳索引文件.timeindex 文件,并且三文件的名字完全相同,如下:

Kafka 的索引文件是按照稀疏索引的思想進(jìn)行設(shè)計(jì)的。稀疏索引的核心是不會(huì)為每個(gè)記錄都保存索引,而是寫入一定的記錄之后才會(huì)增加一個(gè)索引值,具體這個(gè)間隔有多大則通過 log.index.interval.bytes 參數(shù)進(jìn)行控制,默認(rèn)大小為 4 KB,意味著 Kafka 至少寫入 4KB 消息數(shù)據(jù)之后,才會(huì)在索引文件中增加一個(gè)索引項(xiàng)。可見,單條消息大小會(huì)影響 Kakfa 索引的插入頻率,因此 log.index.interval.bytes 也是 Kafka 調(diào)優(yōu)一個(gè)重要參數(shù)值。由于索引文件也是按照消息的順序性進(jìn)行增加索引項(xiàng)的,因此 Kafka 可以利用二分查找算法來搜索目標(biāo)索引項(xiàng),把時(shí)間復(fù)雜度降到了 O(lgN),大大減少了查找的時(shí)間。

位移索引文件.index

位移索引文件的索引項(xiàng)結(jié)構(gòu)如下:

相對位移:保存于索引文件名字上面的起始位移的差值,假設(shè)一個(gè)索引文件為:00000000000000000100.index,那么起始位移值即 100,當(dāng)存儲位移為 150 的消息索引時(shí),在索引文件中的相對位移則為 150 - 100 = 50,這么做的好處是使用 4 字節(jié)保存位移即可,可以節(jié)省非常多的磁盤空間。

文件物理位置:消息在 log 文件中保存的位置,也就是說 Kafka 可根據(jù)消息位移,通過位移索引文件快速找到消息在 log 文件中的物理位置,有了該物理位置的值,我們就可以快速地從 log 文件中找到對應(yīng)的消息了。下面我用圖來表示 Kafka 是如何快速檢索消息:

假設(shè) Kafka 需要找出位移為 3550 的消息,那么 Kafka 首先會(huì)使用二分查找算法找到小于 3550 的最大索引項(xiàng):[3528, 2310272],得到索引項(xiàng)之后,Kafka 會(huì)根據(jù)該索引項(xiàng)的文件物理位置在 log 文件中從位置 2310272 開始順序查找,直至找到位移為 3550 的消息記錄為止。

時(shí)間戳索引文件.timeindex

Kafka 在 0.10.0.0 以后的版本當(dāng)中,消息中增加了時(shí)間戳信息,為了滿足用戶需要根據(jù)時(shí)間戳查詢消息記錄,Kafka 增加了時(shí)間戳索引文件,時(shí)間戳索引文件的索引項(xiàng)結(jié)構(gòu)如下:

時(shí)間戳索引文件的檢索與位移索引文件類似,如下快速檢索消息示意圖:

broker & 數(shù)據(jù)分區(qū)

Kafka 集群包含多個(gè) broker。一個(gè) topic 下通常有多個(gè) partition,partition 分布在不同的 Broker 上,用于存儲 topic 的消息,這使 Kafka 可以在多臺機(jī)器上處理、存儲消息,給 kafka 提供給了并行的消息處理能力和橫向擴(kuò)容能力。

多 reactor 多線程網(wǎng)絡(luò)模型

多 Reactor 多線程網(wǎng)絡(luò)模型 是一種高效的網(wǎng)絡(luò)通信模型,可以充分利用多核 CPU 的性能,提高系統(tǒng)的吞吐量和響應(yīng)速度。Kafka 為了提升系統(tǒng)的吞吐,在 Broker 端處理消息時(shí)采用了該模型,示意如下:

圖片

SocketServer和KafkaRequestHandlerPool是其中最重要的兩個(gè)組件:

  • SocketServer:實(shí)現(xiàn) Reactor 模式,用于處理多個(gè) Client(包括客戶端和其他 broker 節(jié)點(diǎn))的并發(fā)請求,并將處理結(jié)果返回給 Client
  • KafkaRequestHandlerPool:Reactor 模式中的 Worker 線程池,里面定義了多個(gè)工作線程,用于處理實(shí)際的 I/O 請求邏輯。

整個(gè)服務(wù)端處理請求的流程大致分為以下幾個(gè)步驟:

  1. Acceptor 接收客戶端發(fā)來的請求
  2. 輪詢分發(fā)給 Processor 線程處理
  3. Processor 將請求封裝成 Request 對象,放到 RequestQueue 隊(duì)列
  4. KafkaRequestHandlerPool 分配工作線程,處理 RequestQueue 中的請求
  5. KafkaRequestHandler 線程處理完請求后,將響應(yīng) Response 返回給 Processor 線程
  6. Processor 線程將響應(yīng)返回給客戶端

其他知識探究

負(fù)載均衡

生產(chǎn)者負(fù)載均衡

Kafka 生產(chǎn)端的負(fù)載均衡主要指如何將消息發(fā)送到合適的分區(qū)。Kafka 生產(chǎn)者生產(chǎn)消息時(shí),根據(jù)分區(qū)器將消息投遞到指定的分區(qū)中,所以 Kafka 的負(fù)載均衡很大程度上依賴于分區(qū)器。Kafka 默認(rèn)的分區(qū)器是 Kafka 提供的 DefaultPartitioner。它的分區(qū)策略是根據(jù) Key 值進(jìn)行分區(qū)分配的:

  • 如果 key 不為 null:對 Key 值進(jìn)行 Hash 計(jì)算,從所有分區(qū)中根據(jù) Key 的 Hash 值計(jì)算出一個(gè)分區(qū)號;擁有相同 Key 值的消息被寫入同一個(gè)分區(qū),順序消息實(shí)現(xiàn)的關(guān)鍵;
  • 如果 key 為 null:消息將以輪詢的方式,在所有可用分區(qū)中分別寫入消息。如果不想使用 Kafka 默認(rèn)的分區(qū)器,用戶可以實(shí)現(xiàn) Partitioner 接口,自行實(shí)現(xiàn)分區(qū)方法。

消費(fèi)者負(fù)載均衡

在 Kafka 中,每個(gè)分區(qū)(Partition)只能由一個(gè)消費(fèi)者組中的一個(gè)消費(fèi)者消費(fèi)。當(dāng)消費(fèi)者組中有多個(gè)消費(fèi)者時(shí),Kafka 會(huì)自動(dòng)進(jìn)行負(fù)載均衡,將分區(qū)均勻地分配給每個(gè)消費(fèi)者。在 Kafka 中,消費(fèi)者負(fù)載均衡算法可以通過設(shè)置消費(fèi)者組的 partition.assignment.strategy 參數(shù)來選擇。目前主流的分區(qū)分配策略以下幾種:

  • range: 在保證均衡的前提下,將連續(xù)的分區(qū)分配給消費(fèi)者,對應(yīng)的實(shí)現(xiàn)是 RangeAssignor;
  • round-robin:在保證均衡的前提下,輪詢分配,對應(yīng)的實(shí)現(xiàn)是 RoundRobinAssignor;
  • 0.11.0.0 版本引入了一種新的分區(qū)分配策略 StickyAssignor,其優(yōu)勢在于能夠保證分區(qū)均衡的前提下盡量保持原有的分區(qū)分配結(jié)果,從而避免許多冗余的分區(qū)分配操作,減少分區(qū)再分配的執(zhí)行時(shí)間。

集群管理

Kafka 借助 ZooKeeper 進(jìn)行集群管理。Kafka 中很多信息都在 ZK 中維護(hù),如 broker 集群信息、consumer 集群信息、 topic 相關(guān)信息、 partition 信息等。Kafka 的很多功能也是基于 ZK 實(shí)現(xiàn)的,如 partition 選主、broker 集群管理、consumer 負(fù)載均衡等,限于篇幅本文將不展開陳述,這里先附一張網(wǎng)上截圖大家感受下:

圖片

參考文獻(xiàn)

  1. https://www.cnblogs.com/arvinhuang/p/16437948.html
  2. https://segmentfault.com/a/1190000039133960
  3. http://matt33.com/2018/11/04/kafka-transaction/
  4. https://blog.51cto.com/u_14020077/5836698
  5. https://t1mek1ller.github.io/2020/02/15/kafka-leader-epoch/
  6. https://cwiki.apache.org/confluence/display/KAFKA/KIP-101+-+Alter+Replication+Protocol+to+use+Leader+Epoch+rather+than+High+Watermark+for+Truncation
  7. https://xie.infoq.cn/article/c06fea629926e2b6a8073e2f0
  8. https://xie.infoq.cn/article/8191412c8da131e78cbfa6600
  9. https://mp.weixin.qq.com/s/iEk0loXsKsMO_OCVlUsk2Q
  10. https://cloud.tencent.com/developer/article/1657649
  11. https://www.cnblogs.com/vivotech/p/16347074.html
責(zé)任編輯:武曉燕 來源: 碼哥字節(jié)
相關(guān)推薦

2021-06-21 17:00:05

云計(jì)算Hologres云原生

2024-07-12 08:42:58

Redis高性能架構(gòu)

2009-08-12 17:48:56

存儲高性能計(jì)算曙光

2011-07-01 09:36:30

高性能Web

2025-01-27 11:49:55

2022-06-28 08:42:03

磁盤kafka高性能

2010-03-11 15:31:11

核心交換機(jī)

2021-09-06 08:31:11

Kafka架構(gòu)主從架構(gòu)

2020-01-07 16:16:57

Kafka開源消息系統(tǒng)

2021-12-26 00:03:25

Spark性能調(diào)優(yōu)

2019-09-03 09:41:48

運(yùn)維架構(gòu)技術(shù)

2020-11-02 09:35:04

ReactHook

2020-12-03 08:14:45

Axios核心Promise

2009-11-17 10:14:27

核心路由器

2024-08-15 06:51:31

2023-03-09 10:22:00

SpringBootRabbitMQ

2015-09-23 09:35:42

高性能高可靠塊存儲

2020-06-24 08:43:29

5G核心網(wǎng)通信

2019-09-12 08:50:37

Kafka分布式系統(tǒng)服務(wù)器

2025-04-03 00:20:00

點(diǎn)贊
收藏

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

日本高清视频一区二区三区| 91精品国产91久久久久久| 红桃视频 国产| 黄网站在线免费| 高清不卡在线观看av| 午夜精品一区二区三区在线视| 91黄色免费视频| 最新日韩一区| 亚洲在线免费播放| 欧美日韩亚洲在线| 国产av无码专区亚洲av| 99pao成人国产永久免费视频| 在线播放日韩精品| 久久av一区二区三| ww久久综合久中文字幕| 亚洲国产精品久久不卡毛片 | 青青艹视频在线| 99免在线观看免费视频高清| 成人永久aaa| 成人福利在线视频| 亚洲天堂一区在线观看| 亚洲人metart人体| 国产性猛交xxxx免费看久久| 久久久国产精品无码| 秋霞一区二区三区| 欧美人妖巨大在线| 亚洲人成无码www久久久| 波多野结衣乳巨码无在线观看| 国产精品夫妻自拍| 欧美日韩一区二区三区在线观看免| www.香蕉视频| 国产一区三区三区| 国产情人节一区| 亚洲国产av一区二区三区| 精品91久久久久| 欧美成aaa人片免费看| av在线免费播放网址| 狠狠做六月爱婷婷综合aⅴ| 亚洲成人aaa| 中文字幕第六页| 国产一区二区三区视频在线| 欧美日韩国产高清一区二区 | 国产精品国产三级在线观看| 欧美日韩久久久一区| 精品久久久久久久无码 | 黄黄的网站在线观看| 国产农村妇女毛片精品久久麻豆| 欧美在线激情| 国产在线高清| 国产欧美一区二区三区在线老狼| 免费久久一级欧美特大黄| 无码精品黑人一区二区三区| 成人91在线观看| 久久伊人一区二区| 日韩av资源| 国产日韩欧美电影| 亚洲国产日韩欧美| 蜜芽在线免费观看| 国产精品视频看| 自拍偷拍亚洲色图欧美| 国产在线看片| 一区二区三区蜜桃网| 日本福利视频网站| 成年女人在线看片| 91黄色免费看| 色婷婷激情视频| 亚洲天堂中文字幕在线观看| 亚洲高清久久网| 在线免费观看麻豆| 久久精品播放| 久久99热精品| 久久久久女人精品毛片九一| 日韩高清不卡在线| 91在线播放国产| 亚洲高清视频网站| 久久久天堂av| 亚洲aⅴ天堂av在线电影软件| 毛片在线不卡| 午夜视黄欧洲亚洲| www.xxx亚洲| 精品视频在线观看免费观看| 亚洲高清久久网| 18精品爽国产三级网站| 综合国产精品| 91av在线视频观看| 亚洲天堂狠狠干| 成年人午夜久久久| 亚洲看片网站| 大黄网站在线观看| 欧美中文字幕亚洲一区二区va在线| 日韩av自拍偷拍| 欧美精品中文| 久久精品电影一区二区| 奇米影视第四色777| 蜜臀av性久久久久蜜臀aⅴ| 成人免费91在线看| 91在线视频免费看| 亚洲国产成人av网| 亚洲天堂国产视频| 亚洲a级精品| 美女黄色丝袜一区| 啪啪小视频网站| 成人午夜大片免费观看| 一个色的综合| 亚洲精品一区| 日韩精品专区在线| 国产aaaaaaaaa| 宅男噜噜噜66国产日韩在线观看| 成人久久精品视频| 国产私拍精品| 亚洲午夜激情网站| 国产精品中文久久久久久| 国产精品一区高清| 777精品视频| 国产aⅴ爽av久久久久成人| 国产精品入口麻豆九色| 国产a级一级片| 99re91这里只有精品| 久久精品色欧美aⅴ一区二区| 日韩一级在线视频| 91网站黄www| 欧美国产视频一区| 久久免费福利| 日韩中文字幕在线看| 国产成人av免费| 久久色成人在线| 久久久亚洲精品无码| 亚洲午夜免费| 欧美成人精品一区| 国产免费黄色片| 国产精品久久久久久户外露出| 116极品美女午夜一级| 国产精品极品在线观看| 久久99热这里只有精品国产| www.黄色国产| 亚洲精品国产无套在线观| www.51色.com| 在线成人激情| 91手机在线播放| 日韩特级毛片| 精品免费国产二区三区| 久久免费小视频| 丁香五精品蜜臀久久久久99网站| av动漫在线播放| 亚洲天堂av资源在线观看| 欧美多人爱爱视频网站| 亚洲春色一区二区三区| 亚洲国产综合色| 少妇极品熟妇人妻无码| 在线观看亚洲| 精品麻豆av| 黑人精品一区| 一本色道久久88综合亚洲精品ⅰ| 91视频久久久| 国产精品久久久久影院| 亚洲一区二区中文字幕在线观看| 自拍偷拍欧美| 国产乱子伦精品| 色老头在线一区二区三区| 日韩久久午夜影院| 亚洲午夜无码久久久久| 自拍偷拍国产精品| 亚洲美女高潮久久久| aⅴ色国产欧美| 日韩久久不卡| 在线欧美激情| 国内精品久久久久影院 日本资源| 黄色福利在线观看| 色综合天天综合| 任你操精品视频| 国产精品一二三在| 国产原创中文在线观看| 欧美久久精品一级c片| 亚洲a级在线播放观看| 51漫画成人app入口| 亚洲视频在线观看免费| 91亚洲精品国偷拍自产在线观看 | 韩国av中文字幕| 欧美国产97人人爽人人喊| 色姑娘综合天天| 亚洲影院在线| 中日韩在线视频| 国产精品乱战久久久| 国产精品福利片| 视频在线这里都是精品| 精品在线欧美视频| 国产精品一区二区免费视频| 亚洲成人激情自拍| 美女网站视频色| 成人av先锋影音| 天天干天天操天天玩| 99热这里只有精品8| 伊人色综合影院| 日本妇女一区| 91欧美精品午夜性色福利在线| 国产在线精彩视频| 久久久国产91| 每日更新av在线播放| 日韩午夜精品电影| 亚洲 小说区 图片区| 亚洲电影一级黄| 亚洲精品一区二区三区在线播放| 99久久精品国产网站| 制服下的诱惑暮生| 久久这里有精品15一区二区三区| 欧美黄网在线观看| 97精品国产福利一区二区三区| 久久久一本精品99久久精品66| 2019中文亚洲字幕| 国产精品成熟老女人| 日本蜜桃在线观看视频| 欧美成人免费一级人片100| 成人午夜影视| 国产视频一区在线| 欧美熟女一区二区| 欧美一区二区三区在线| 久久这里只有精品9| 狠狠干狠狠久久| 日本在线视频免费观看| 一区二区在线观看视频| 免费91在线观看| 国产欧美一区二区在线观看| 日本黄色网址大全| 97久久人人超碰| 国产免费一区二区三区最新6| 国产精品自产自拍| 91丝袜超薄交口足| 看电视剧不卡顿的网站| 国产精品人人爽人人爽| 日韩在线一区二区| 麻豆av免费在线| 亚洲免费中文| 欧美aⅴ在线观看| 亚洲一区不卡| 亚洲 高清 成人 动漫| 9色精品在线| 日本少妇高潮喷水视频| 中文亚洲免费| 无码精品a∨在线观看中文| 亚洲婷婷在线| www精品久久| 中文高清一区| 青青草原成人网| 日韩中文字幕亚洲一区二区va在线 | 免费久久一级欧美特大黄| 九热爱视频精品视频| 日韩精品第一页| 成人6969www免费视频| 一区二区91美女张开腿让人桶| 日韩欧美1区| 亚洲国产精品女人| 欧美日韩日本国产亚洲在线 | 久久久久久久尹人综合网亚洲| 国产在线观看福利| 日韩国产欧美三级| 艹b视频在线观看| 国产精品伊人色| 在线观看亚洲免费视频| xfplay精品久久| 成人小视频免费看| 亚洲图片激情小说| 国产一级大片在线观看| 精品国产户外野外| 亚洲午夜无码久久久久| 欧美一级久久久久久久大片| 丰满肉嫩西川结衣av| 亚洲精品在线视频| 在线毛片网站| 欧美劲爆第一页| 中国色在线日|韩| 国产视频福利一区| 高潮久久久久久久久久久久久久 | 日本动漫理论片在线观看网站| 久久免费精品日本久久中文字幕| 性欧美18xxxhd| 成人黄色av播放免费| 成人h动漫精品一区二区器材| 久久久久久精| 国产精品久久观看| 日本www在线视频| 麻豆免费精品视频| 日本精品一二三区| 久久久久久久久久久久久夜| 免费成人深夜夜行网站| 性欧美大战久久久久久久久| 亚洲精品国产欧美在线观看| 欧美精品一区二区三区蜜臀| 国产三级视频在线看| 欧美人交a欧美精品| 惠美惠精品网| 懂色中文一区二区三区在线视频| 精品国产一区二区三区av片| 毛片av在线播放| 美国十次了思思久久精品导航 | 99久久精品国产一区色| 亚洲精品网址在线观看| 成人av福利| 国产精品欧美一区二区| 久久成人福利| 青青草原网站在线观看| 日韩在线观看一区二区| 东京热av一区| 最近日韩中文字幕| 波多野结衣一区二区在线| 精品久久久久久久久久久久包黑料| h网站在线免费观看| 2020久久国产精品| 香蕉成人app| 在线国产精品网| 日韩国产欧美视频| 精品少妇一区二区三区免费观 | 国产又粗又猛又爽又| 亚洲大胆人体av| 午夜激情在线| 91在线观看免费观看 | 蜜月aⅴ免费一区二区三区 | 1234区中文字幕在线观看| 91色视频在线导航| 成人同人动漫免费观看| 日本精品一区在线观看| 99视频超级精品| 国产亚洲自拍av| 日韩一级片网址| 男人的天堂在线视频免费观看| 国产精品精品久久久| 亚洲性视频大全| 女人喷潮完整视频| 91在线一区二区三区| 国产精品成人久久| 精品日韩99亚洲| 蜜桃成人365av| 波多野结衣精品久久| 国产精品videosex极品| 欧美69精品久久久久久不卡| 亚洲三级电影网站| 国产伦子伦对白视频| 久久亚洲精品国产亚洲老地址| 台湾天天综合人成在线| 亚欧精品在线| 久久99精品久久久久| 精品视频第一页| 制服丝袜中文字幕亚洲| 菠萝菠萝蜜在线视频免费观看| 成人在线激情视频| 女生裸体视频一区二区三区| 亚洲三级在线视频| 一区二区三区在线视频免费观看| 国产高清第一页| 欧美激情久久久| 全球av集中精品导航福利| 国产99久久九九精品无码| 久久久久久日产精品| 中文字幕免费在线看| 久久精品99国产精品酒店日本| 美女久久精品| 成年人午夜视频在线观看| www激情久久| 中国女人一级一次看片| 久久久97精品| 国产伦乱精品| 999精品网站| 自拍偷拍亚洲激情| 日韩在线视频第一页| 国产成人短视频| 天天揉久久久久亚洲精品| 国产无套精品一区二区三区| 午夜私人影院久久久久| 粉嫩一区二区三区国产精品| 91沈先生作品| 99国产精品| 国产又粗又猛又爽又黄的视频小说| 日韩欧美一级片| gay欧美网站| 精品国产无码在线| av一二三不卡影片| 中文字幕激情视频| 久久久久国产一区二区三区| 欧美热在线视频精品999| 亚洲热在线视频| 色综合久久88色综合天天| 黄色免费网站在线观看| 欧美一区二区三区在线视频| 丝袜美腿美女被狂躁在线观看 | 美日韩黄色大片| 乌克兰美女av| 亚洲第一成人在线| 92国产在线视频| 激情五月综合色婷婷一区二区| 蜜桃av一区二区三区电影| 中文在线观看免费网站| 中文字幕v亚洲ⅴv天堂| 国产成人一二| 午夜视频在线网站| 色天使色偷偷av一区二区| 性欧美ⅴideo另类hd| 欧美在线视频二区| 成人性生交大合| 91九色蝌蚪91por成人| 欧美一区二区三区四区在线| 亚洲欧美一级二级三级|