国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

別造輪子了,先來“抄”一下 RocketMQ 的文件編程藝術

開發 前端
學習 RocketMQ,我們學的不僅是 “術”(具體的實現技巧),更是 “道”(解決問題的思想和方法論)。希望通過本文的剖析,能為你打開一扇通往高性能存儲世界的大門,并在未來的系統設計中,能夠更加游刃有余地運用這些閃耀著智慧光芒的編程藝術。?

最近有朋友問我,如何從零開始設計一套高性能、高可靠、且支持快速查詢的文件存儲系統?

這是一個典型的后端面試高頻題,也是一個極具挑戰的真實工程難題。我的回答是:與其閉門造車,不如站在巨人的肩膀上——去“抄”一個頂級的開源實現。

他追問:“抄誰?”

我說:“當然是抄 RocketMQ!它在文件處理上的設計,堪稱 Java 領域的教科書,充滿了工程的智慧與藝術。無論是海量消息的順序寫入,還是高效的索引查詢,亦或是對內存和磁盤的極致運用,RocketMQ 的每一處細節都值得我們細細品味。下面,就讓我們一同深入這座寶庫,探索其背后的文件編程藝術。”

消息存儲格式看文件編程

commitlog 文件設計:學習文件編程的起點

我們知道,RocketMQ 的全量消息均存儲在 commitlog 文件中。由于每條消息的大小不一,我們面臨第一個挑戰:如何高效地組織這些消息,以便在讀取時能夠準確地區分每條消息的邊界?

在基于文件的編程模型中,首要任務是定義一套清晰的消息存儲格式。一個通用的實踐是將數據結構設計為 Header + Body 的形式。其中,Header 部分采用定長設計,存放元數據信息;Body 部分則存放實際的數據。在 RocketMQ 的存儲協議中,我們可以將記錄消息總長度的 4 字節視為 Header,其后的所有字段則構成 Body,包含了與消息相關的業務屬性,它們共同按照預定格式組裝。

圖片圖片

對于 Header + Body 這種協議,提取一條完整消息通常分為兩步:首先,讀取定長的 Header,從中解析出 Body 的長度(在 RocketMQ 中即為消息的總長度)。然后,根據這個長度,從消息的起始位置 讀取完整的消息數據,并按照預定義的格式解析出各個業務字段。

那問題又來了,如果確定一條消息的開頭呢?難不成從文件的開始處開始遍歷?

正如關系型數據庫會為每條記錄分配一個唯一的 ID,在文件編程模型中,我們也為每條消息引入了一個關鍵的身份標識:消息物理偏移量(Physical Offset),它精確地指明了消息在文件中的起始存儲位置。

圖片圖片

因此,通過 物理偏移量 + 消息大小(SIZE) 這一組合,我們便能輕而易舉地從海量數據中精確定位并提取出任何一條完整的消息。

此外,commitlog 的文件組織還揭示了另一個通用實踐:文件通常以一個“魔數”(Magic Number)開頭,用于快速校驗文件類型和完整性。同時,在文件末尾,可能會使用特殊填充(PAD)來處理空間。例如,當文件剩余空間不足以容納一條完整的消息時,系統不會將消息拆分存儲,而是用 PAD 填充剩余部分,以保證下一條消息能從新文件的起始位置寫入,維持了消息的原子性。

consumequeue 文件設計:索引的藝術

commitlog 文件基于物理偏移量查詢消息效率極高,但若要按 Topic 進行檢索,則顯得力不從心。為了解決這一痛點,RocketMQ 精心設計了 consumequeue文件作為消費隊列索引。

圖片圖片

consumequeue 的設計極具巧思。其核心在于,每個索引條目都采用固定長度設計:8 字節的 commitlog 物理偏移量、4 字節的消息長度和 8 字節的 Tag 哈希值。這里存儲的是 Tag 的哈希值而非原始字符串,正是為了確保每個條目定長。這種設計使得consumequeue 文件可以像數組一樣,通過下標(queueOffset)進行快速隨機訪問,極大地提升了索引查詢性能。

由此可見,在高性能文件存儲設計中,為特定查詢場景構建高效的索引至關重要。而索引設計的關鍵原則之一,就是采用定長條目,從而實現類似數組的 O(1) 復雜度快速定位。

性能飛躍:內存映射與頁緩存

解決了數據存儲格式與唯一標識的問題后,下一個核心挑戰是如何提升I/O性能。在文件編程實踐中,為了便于管理和數據刪除(例如過期的消息),通常會采用固定大小的日志文件策略,將數據切分成多個大小相等的文件段。RocketMQ 的 commitlog文件夾中那些 1G 大小的文件正是這一思想的體現。

圖片圖片

采用定長文件的一個核心優勢在于,它極大地簡化了內存映射(Memory Mapping)的實現。通過內存映射技術,我們可以將磁盤文件直接映射到進程的虛擬地址空間,之后就可以像訪問內存一樣讀寫磁盤上的數據,從而繞過傳統 read/write 系統調用帶來的多次數據拷貝,顯著提升文件操作性能。

在 Java 中使用內存映射的示例代碼如下:

FileChannel fileChannel = new RandomAccessFile(this.file, "rw").getChannel();
MappedByteBuffer mappedByteBuffer = this.fileChannel.map(MapMode.READ_WRITE, 0, fileSize);

實現要點如下:

  • 首先需要通過 RandomAccessFile 構建一個文件讀寫通道 FileChannel。
  • 再通過 FileChannel 的 map 方法創建內存映射區域 MappedByteBuffer。

在 Linux 系統中,MappedByteBuffer 的背后正是強大的頁緩存(Page Cache)。Linux 會盡可能地利用空閑物理內存作為頁緩存,以緩存磁盤數據。當應用程序讀寫文件時,實際上是在與頁緩存交互。如果數據在緩存中(緩存命中),則無需磁盤 I/O;如果不在(緩存未命中),系統會觸發一個缺頁中斷,透明地將數據從磁盤加載到頁緩存中。整個過程由操作系統自動管理,對應用層開發者完全透明,極大地簡化了高性能應用的開發。

通過內存映射寫入的數據,首先會進入頁緩存,并不會立即刷寫到磁盤。持久化操作由操作系統根據其策略在后臺異步完成。這意味著,即使 Broker 進程異常崩潰,只要操作系統仍然運行,頁緩存中的數據就不會丟失,最終會被寫入磁盤,保證了一定程度的可靠性。然而,如果發生機器斷電或操作系統宕機等災難性故障,尚未從頁緩存刷入磁盤的數據將會永久丟失,這是內存映射技術需要權衡的風險。

磁盤性能提升:順序寫

要進一步壓榨磁盤性能,另一個關鍵的設計原則是使用順序寫替代隨機寫。與隨機寫相比,磁盤順序寫的性能要高出幾個數量級。這一原則在所有高性能存儲系統中都得到了廣泛應用。以大家熟悉的 MySQL InnoDB 存儲引擎為例:當執行一條更新語句時,數據首先在內存(Buffer Pool)中被修改。為了保證事務的持久性,InnoDB 并不會立即將修改后的數據頁(這會導致隨機 I/O)刷回磁盤,而是先將變更以 Redo Log 的形式順序追加到一個專用的日志文件中,并確保 Redo Log 被同步刷盤。數據文件本身的更新則可以在后臺異步、批量地進行。

圖片圖片

試想,如果沒有 Redo Log,每次更新都直接修改磁盤上的數據文件,那么更新不同表的數據就會導致磁頭在磁盤上大幅度來回尋道,產生大量的隨機 I/O,性能將慘不忍睹。通過引入 Redo Log,InnoDB 將多次離散的隨機寫操作,巧妙地轉換成了一次集中的順序寫操作,盡管多了一步寫日志的操作,但整體性能卻得到了質的飛躍。這個例子雄辯地證明了順序寫相比隨機寫的巨大優勢。

因此,在設計文件存儲模型時,一個黃金法則是:盡可能地將操作設計為順序追加(Append-Only),避免原地更新(In-Place Update)。

資源管理的智慧:引用計數器

在基于 NIO 的文件編程中,我們頻繁地與 ByteBuffer 打交道。為了在不進行數據拷貝的前提下共享緩沖區內容,slice() 方法被廣泛使用。它能創建一個與原 ByteBuffer 共享同一塊內存區域,但擁有獨立 position、limit 和 mark 指針的新 ByteBuffer。

圖片圖片

上圖 selectMappedBuffer 方法的作用,正是從一個內存映射文件(如 commitlog)的指定位置“切割”出一段數據。這種零拷貝操作雖然高效,但也引入了復雜的資源管理問題:被主 MappedByteBuffer 切割(slice)出的多個子 ByteBuffer,它們的生命周期各不相同。我們必須確保在所有子 ByteBuffer 都使用完畢之前,主 MappedByteBuffer 不能被釋放,否則將導致懸空指針和程序崩潰。

RocketMQ 如何優雅地解決這個問題呢?答案是引入引用計數器(Reference Counting)。

其核心思想是:每次調用 slice() 派生出一個新的 ByteBuffer 時,就將主對象的引用計數加一;當任何一個派生對象使用完畢被釋放時,就將引用計數減一。只有當引用計數歸零時,才真正執行底層的資源釋放操作。

圖片圖片

結合 selectMappedBuffer 方法的實現,我們可以看到:

  1. 調用 hold() 方法來增加引用計數,這標志著 MappedByteBuffer 又被“借用”了一次。
  2. 返回的 SelectMappedBufferResult 對象中封裝了派生出的 ByteBuffer。當使用者調用其 release() 方法時,內部會調用 ReferenceResource 的 release(),使引用計數減一。

可靠與性能的權衡:同步與異步刷盤

內存映射機制極大地提升了寫入性能,但其“數據先到內存,后到磁盤”的特性也帶來了新的抉擇:當 Broker 接收到消息后,是應該在數據寫入頁緩存后就向客戶端返回成功,還是必須等到數據被持久化到磁盤后才返回?

這本質上是系統性能與數據可靠性之間的權衡。為此,RocketMQ 提供了兩種持久化策略:同步刷盤和異步刷盤。

所謂的 “刷盤”,在代碼層面其實就是調用 FileChannel 或 MappedByteBuffer 的 force() 方法,強制操作系統將頁緩存中對應的數據寫入物理磁盤。

圖片圖片

同步刷盤

同步刷盤策略要求 Broker 將消息寫入內存后,必須立即將其持久化到磁盤,然后才能向客戶端返回成功響應。

但這里有一個關鍵問題:同步刷盤是每條消息都單獨刷一次盤嗎?答案是否定的。

RocketMQ 的同步刷盤實現隱藏著一個重要的優化:組提交(Group Commit)。其入口位于 GroupCommitService 類中,從類名就能看出其設計思想。

圖片圖片

實現中有兩個關鍵點:

  • 組提交:一次刷盤操作會將當前所有待刷盤的消息(一個消息組)一次性寫入磁盤,而不是一條一條地刷。這極大地減少了磁盤 I/O 次數。
  • 同步轉異步:實現上采用 CountDownLatch 將同步調用轉換為異步處理模式。主線程提交一個刷盤請求后,會限時等待 (await) 后臺刷盤線程的結果,從而實現業務邏輯的解耦。

接下來繼續探討組提交的設計理念。

圖片圖片

判斷一條刷盤請求成功的條件:當前已刷盤指針大于該條消息對應的物理偏移量,這里使用了刷盤重試機制。然后喚醒主線程并返回刷盤結果。

所謂的組提交,其核心理念理念是調用刷盤時使用的是 MappedFileQueue.flush 方法,該方法并不是只將一條消息寫入磁盤,而是會將當期未刷盤的數據一次性刷寫到磁盤,既組提交,故即使在同步刷盤情況下,也并不是每一條消息都會被執行 flush 方法,為了更直觀的展現組提交的設計理念,給出如下流程圖:

圖片圖片

異步刷盤

同步刷盤提供了最高級別的數據可靠性,但犧牲了寫入性能。考慮到 RocketMQ 的消息首先寫入 PageCache,在非極端掉電情況下數據丟失的概率很小,因此,如果業務能容忍極低概率的數據丟失以換取更高的吞吐量,可以選擇異步刷盤。

異步刷盤模式下,Broker 將消息寫入 PageCache 后會立即向客戶端返回成功,然后由一個后臺線程(FlushRealTimeService)定時將臟頁數據刷入磁盤,默認間隔為 500ms。

你可能會猜測這是用 ScheduledExecutorService 之類的定時任務實現的,但 RocketMQ 的實現更為精妙。它同樣利用了帶超時時間的 CountDownLatch.await()。這種方式的好處在于:

  • 在沒有新消息寫入時,線程會安靜地等待 500ms,避免了空輪詢帶來的 CPU 消耗。
  • 一旦有新消息寫入(wakeup() 被調用),線程會立即被喚醒執行刷盤,而無需等待 500ms 周期結束,保證了刷盤的及時性。

保障數據一致性:文件恢復機制

圖片圖片

在 RocketMQ 的架構中,commitlog 是主數據文件,而 consumequeue 和 indexFile 等索引文件是根據 commitlog 的內容異步構建的。既然是異步構建,就必然存在數據不一致的風險窗口。例如,Broker 在將數據轉發到 consumequeue 之前異常關閉,重啟后如何保證數據的一致性?

在探討恢復機制前,我們先設想幾個典型的異常場景:

  • 消息已同步刷盤到 commitlog,但在轉發到 consumequeue 之前,機器斷電。
  • 一次批量刷盤操作(例如 100MB 數據)在執行到一半(例如 50MB)時,機器斷電,導致 commitlog 文件末尾存在一條不完整的消息。
  • commitlog 刷盤成功,但在更新 checkpoint 文件(記錄各文件刷盤點)之前,進程退出。

在 RocketMQ 中,文件恢復分為正常停止和異常停止兩種場景。

兩種場景定位恢復起點的邏輯略有不同,但一旦定位到起始恢復文件,后續的文件校正思路是統一的:

  • 首先嘗試恢復 consumequeue:遍歷 consumequeue 文件,根據其定長格式(8字節偏移量 + 4字節長度 + 8字節Tag哈希碼),找到最后一條完整條目所指向的 commitlog 物理偏移量,記為 maxPhysicalOfConsumequeue。
  • 然后嘗試恢復 commitlog:遍歷 commitlog 文件,校驗魔數,并根據消息存儲格式找到最后一條完整的、校驗合格的消息,記錄其物理偏移量 physicalOffset。
  • 對比與校正:

如果 commitlog 的有效偏移量小于 consumequeue 中記錄的最大偏移量,說明 consumequeue 中存在無效的“超前”索引,需要被截斷。

如果 commitlog 的有效偏移量大于 consumequeue 中記錄的最大偏移量,說明有部分消息還未建立索引,需要從 commitlog 中重新讀取這部分消息,并重建 consumequeue 和其他索引文件。

那么,如何高效地定位到可能需要恢復的文件呢?

正常退出定位文件

在 RocketMQ 啟動時候會創建一個名為 abort 的文件,并在正常關閉時將其刪除。因此,通過檢查 abort 文件是否存在,即可判斷上次是否為異常退出。

圖片圖片

對于正常退出場景,恢復策略相對樂觀:

  • ConsumeQueue 的恢復從每個主題的第一個文件開始。
  • commitlog 的恢復從倒數第三個文件開始向后檢查。因為正常退出時,大部分文件都已完整寫入并刷盤。

異常退出定位文件

異常退出時,不確定性大大增加,恢復策略必須更加嚴謹。此時,checkpoint 文件就派上了用場。該文件記錄了 commitlog、consumequeue 等文件的最后一次刷盤時間戳。

圖片圖片

  • physicMsgTimestamp:commitlog 文件最后的刷盤的時間點
  • logicsMsgTimestamp: consumequeue 文件最后的刷盤時間點
  • indexMsgTimestamp: indexfile 文件最后的刷盤時間點

checkpoint 文件的更新總是在 commitlog 刷盤成功之后進行。

圖片圖片

這意味著 checkpoint 中記錄的刷盤點是“絕對可靠”的,早于該時間點的數據一定已經落盤。基于此,異常退出時的恢復策略是:

  • ConsumeQueue 是按照 topic 進行恢復的,從第一文件開始恢復。
  • commitlog 的恢復從最后一個文件開始,逐個向前掃描。讀取每個文件的第一條消息的存儲時間,與 checkpoint 中記錄的 physicMsgTimestamp 進行比較。一旦找到一個文件的起始時間小于等于 checkpoint 的時間戳,那么就從這個文件開始執行恢復流程。

文件恢復的入口位于 DefaultMessageStore#recover 方法,讀者可根據上述理念,自行探索源碼,定會事半功倍。

終極性能優化:Java 零拷貝

在高性能網絡文件服務中,“零拷貝”(Zero-Copy)是一個高頻詞匯。這里我們不贅述其底層原理,而是直接看 RocketMQ 在消息消費場景下,如何結合 Netty 實現零拷貝,將磁盤文件高效地發送到網絡。

圖片圖片

零拷貝的關鍵實現要點:

  • 當需要發送消息時,RocketMQ 首先通過內存映射從 commitlog 文件中獲取一個代表消息數據的 ByteBuf。重要的是,這個 ByteBuf 僅僅是一個引用,數據本身仍在頁緩存中,并未被加載到 Java 堆內存。
  • 然后,將這個 ByteBuf 包裝成一個 Netty 的 FileRegion 對象,并最終調用其 transferTo 方法。該方法的底層實現委托給了 FileChannel.transferTo()。

在 ManyMessageTransfer 類中可以看到具體的 transferTo 實現:

圖片圖片

FileChannel.transferTo() 在 Linux 系統上會觸發 sendfile() 系統調用。這個系統調用可以直接在內核空間中,將數據從文件句柄(頁緩存)拷貝到套接字緩沖區,避免了數據在內核態和用戶態之間的來回拷貝,實現了真正的零拷貝,極大地提升了數據傳輸效率。

本文從文件編程的視角,跟隨 RocketMQ 的設計學習了諸多優秀的工程技巧。希望這些內容能對你有所啟發

結語

學習 RocketMQ,我們學的不僅是 “術”(具體的實現技巧),更是 “道”(解決問題的思想和方法論)。希望通過本文的剖析,能為你打開一扇通往高性能存儲世界的大門,并在未來的系統設計中,能夠更加游刃有余地運用這些閃耀著智慧光芒的編程藝術。

責任編輯:武曉燕 來源: JAVA日知錄
相關推薦

2015-08-06 10:14:15

造輪子facebook

2020-09-08 08:45:39

jupyter插件代碼

2022-04-27 20:02:22

Dubbo注冊中心開發

2023-02-14 12:40:44

ChatGPTAI聊天

2023-02-06 17:27:48

2018-09-03 14:05:08

編程語言Python編程技巧

2022-12-07 10:34:45

AST前端編譯

2021-02-26 13:59:41

RocketMQProducer底層

2024-04-07 09:34:59

集群RemoteTransform

2022-05-13 09:16:49

Python代碼

2021-10-09 18:26:59

二叉樹多叉樹搜索

2012-01-13 16:00:05

愛國者馮軍蘋果

2020-06-11 18:06:03

電腦電路板元件

2021-10-28 19:10:51

RustPythonjs

2021-04-09 10:26:43

Python編程技術

2021-11-08 22:38:44

電腦科技C盤

2022-06-29 10:04:01

PiniaVuex

2024-05-14 08:11:56

ReactuseState造輪子

2021-03-15 09:44:39

Broker源碼RocketMQ

2024-07-01 08:01:45

API網關接口
點贊
收藏

51CTO技術棧公眾號

欧美日韩精品免费看| 亚洲国产一二三精品无码| 亚洲精华液一区二区三区| 国产精品传媒视频| 91麻豆天美传媒在线| 欧美日韩在线大尺度| 国产成人鲁鲁免费视频a| 精品中文字幕一区二区三区| 亚洲欧美精品伊人久久| 免费看电影在线| 制服丝袜亚洲网站| 1769视频在线播放免费观看| 午夜伊人狠狠久久| 日韩av电影免费| 久久日一线二线三线suv| 国产曰肥老太婆无遮挡| 久久av老司机精品网站导航| 茄子视频成人在线观看| 99riav国产精品| 91国产在线播放| 国产精品美女久久久久久不卡| 午夜精品福利电影| 欧美a大片欧美片| 91精品国产色综合久久不卡98| 久久爱www.| 久久久噜噜噜久久久| 超碰成人97| 欧美伊久线香蕉线新在线| 日本成人a网站| 国产精品久久不能| 亚洲国产精品久久久天堂 | 亚洲小说区图片区| 精品乱码一区二区三区| 久久婷婷影院| 亚洲精品成人三区| 精品一区二区久久| 久无码久无码av无码| xf在线a精品一区二区视频网站| 91黄色小网站| 国产欧美精品国产国产专区| 国产对白国语对白| 黄网站色欧美视频| 日本最黄一级片免费在线| 欧美成人欧美edvon| 中文日产幕无线码一区二区| 久久综合九色九九| 成人情趣视频| 蜜桃传媒视频麻豆一区| 国产精品资源网| 精品久久久噜噜噜噜久久图片| 亚洲精品免费在线播放| 97视频精彩视频在线观看| 亚洲精品久久久久| 日韩有吗在线观看| 国产又爽又黄的激情精品视频| 夜夜爽av福利精品导航| 黄色录像特级片| 亚洲欧洲日韩在线| 成人免费高清在线播放| 亚洲欧洲一区二区三区在线观看| 永久免费精品视频| 91久久国产综合久久蜜月精品| 奇米精品一区二区三区四区| 国产a级一级片| 色综合天天综合网天天看片| 色偷偷偷在线视频播放| 日本不卡高字幕在线2019| 性一交一乱一区二区洋洋av| 欧美a级片网站| 日韩欧美一区二区视频在线播放 | 色婷婷av一区| 岛国av在线网站| 97久久超碰福利国产精品…| 欧美日韩综合| 国产黄视频在线| 亚洲高清免费观看| 国产一二区在线观看| 色综合五月天导航| 在线亚洲观看| 日韩不卡一二三| 日本一区二区三区视频免费看 | 日本黄xxxxxxxxx100| av在线播放一区二区三区| 91黑丝在线| 日韩欧美一区在线| 伊色综合久久之综合久久| 高清av免费一区中文字幕| 成人少妇影院yyyy| 男操女在线观看| 欧美久久精品午夜青青大伊人| 亚洲欧洲一区| 国产黄色免费网| 欧美另类女人| 国产成人无码精品久久久性色| 91久久精品一区二区三区| 国产成人午夜性a一级毛片| 91丝袜美腿美女视频网站| 99久久国产免费看| 久久综合网导航| 国产精品老女人精品视频| 国产99一区视频免费| 日p在线观看| 国产精品视频网址| 丁香花高清视频完整版在线观看| 日韩一二在线观看| 希岛爱理av一区二区三区| 国产 福利 在线| 精品国产凹凸成av人导航| 亚洲成av人片乱码色午夜| 欧美黄色一级片视频| 精品国产乱子伦一区| 狠狠久久婷婷| 在线免费观看高清视频色| 欧美精品国产精品日韩精品| 国产.精品.日韩.另类.中文.在线.播放| 成人日日夜夜| 国产高清在线精品一区二区三区| 亚洲综合男人的天堂| 欧美日韩大片免费观看| av免费观看国产| 亚洲欧美www| 蜜桃视频在线观看一区二区| a视频网址在线观看| 亚洲自拍偷拍视频| 亚洲国产成人av网| 色愁久久久久久| 成年美女网站| 欧美亚洲在线观看| 中文字幕亚洲视频| 日韩中出av| 嫩草嫩草嫩草| 国产精品自拍偷拍| 香蕉加勒比综合久久| 精品国产一区二区三区噜噜噜| 丁香六月婷婷| 日韩av片电影专区| 亚洲第一搞黄网站| 婷婷另类小说| 97在线观看免费观看高清| 久久涩涩网站| 精品国产成人在线影院| 狠狠色丁香久久婷婷综| 久久人体av| 国产原创精品在线| 国产91露脸中文字幕在线| 亚洲曰韩产成在线| 欧美不卡视频| 欧美xxxxhdvideosex| 国产在线观看欧美| 欧美成人激情图片网| 国产精品国产三级国产三级人妇 | 91专区在线观看| 欧美夫妻性生活视频| 国产精品久久一卡二卡| 色爱综合av| 高h视频在线| 一区二区精品免费视频| 一本色道久久88综合日韩精品 | 久久久久久久免费视频| 色天天综合狠狠色| 亚洲伊人影院| 久草福利资源在线视频| 国产精品久久久999| 在线欧美日韩精品| 奇米色777欧美一区二区| 韩日精品一区| www.97| 国产高清自拍一区| 亚洲欧美激情另类校园| 欧美国产欧美亚州国产日韩mv天天看完整 | 国产精品日韩精品欧美在线| 成人免费在线观看av| 日本网站在线免费观看视频| 亚洲7777| 欧美激情一级欧美精品| 欧美日韩在线观看视频| 欧美电影免费观看| 黄色片视频在线播放| 国产精品91在线观看| 欧美日韩电影在线播放| 国产超碰在线一区| 欧美日韩水蜜桃| 久久国产精品黑丝| 国产精品v日韩精品v在线观看| 亚洲综合中文字幕68页| 日韩hd视频在线观看| 中文字幕中文字幕中文字幕亚洲无线 | av丝袜天堂网| 99视频在线播放| 亚洲香蕉成视频在线观看| 天天天综合网| 九九色在线视频| 成全视频全集| 一区二区三区国| 国产精品麻豆va在线播放| 亚洲精品一区中文字幕乱码| 五月激情综合网| 91色.com| 久久福利资源站| 久久久久av|