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

如何實現一個前端監控回放系統

開發 前端
本文通過各類調研以及實際項目開發中總結的經驗,以記錄在實現前端監控回放系統的過程中會用到的一些 Web API 與新技術能力,為大家提供一些可行的實現思路。

隨著 Web 項目日趨復雜,除了不斷還原 UI 稿、搞好各端與各機型兼容外,如何解決用戶在訪問頁面時實際遇到的各種“疑難雜癥”,逐漸成為開發者需要面臨的問題之一。

傳統手段上,我們有各式各樣的埋點方案,無痕埋點、全鏈路監控、用戶行為上報、接口狀態監控等等,但這些都是針對真實用戶行為以及表現的采樣,遇到問題時他們并不一定能發揮最大用處,很多時候還需要程序員的參與介入。有什么辦法可以最完整的將用戶問題展現在我們面前呢?無外乎我們可以完整、真實地重現用戶當時的操作行為與結果,要是在用戶端能安裝一個隨時隨地的錄屏軟件就好了。

本文通過各類調研以及實際項目開發中總結的經驗,以記錄在實現前端監控回放系統的過程中會用到的一些 Web API 與新技術能力,為大家提供一些可行的實現思路。本文未涉及到的方面也歡迎評論告知補充。

本文結構如下:

  1. 實現方案思路
  2. 關鍵技術基礎
    • 應用與狀態的序列化
    • 記錄 DOM 變化與交互的快照
    • 回放重演
    • 沙箱
  3. 技術項拆分與相關 Web API
  4. 結語

1 / 實現方案思路

要想給用戶的訪問做一次完整的應用狀態監控錄制與回放,除了錄制視頻外,通過 Web API 實現大致有兩類主流的解決思路。

第一種方案是通過記錄 DOM 的每次變更,并將內容序列化下來,然后在沙箱中還原并回放這些 UI 變化。這種方案的優點之一是能夠給 DOM 創建快照的概念,在應用每一次狀態變化后進行收集,把這個序列串起來后我們便可以靈活掌握回放速度、并針對關鍵性節點進行自定義回放。在社區開源方案中,這類技術最成熟的莫過于 rrweb https://github.com/rrweb-io/rrweb ,此外若干互聯網企業也有提供一些商業解決方案,但技術設計上大同小異,大致都可以拆分為 DOM 序列化、構建快照序列、反序列化回放以及運行沙箱環境等四方面。

另一種可行方案,從我們的調研結果來看,是將用戶側所有數據收集起來,然后在一個可控運行環境下嚴格按照校準時間對用戶事件操作進行派發,從而控制回放。這里主要分為兩部分,一方面因為我們通過派發事件來重演用戶的行為,便需要高精度計時器以及完善的用戶事件收集策略;另一方面,由于要保證應用狀態在兩端的變化一致性,只嚴格觸發用戶的操作還不夠,我們還要將任何應用與網絡之前的交互操作(請求與響應內容)精準對齊,所以我們需要攔截所有請求、即時的前端構建產物以及用戶操作序列等等。

第二種方案由于可以近乎模擬用戶側運行時的狀態變化,相當于將當時的用戶整個搬到了我們面前,因此可以方便開發同學進一步調試。在社區開源方案中,我們沒有找到類似的實現,但商業方案中 https://logrocket.com/ 最接近這個思路,其提供的不少功能甚至比這里提及的功能要更強大。

但本文中,我們只討論當需要實現這樣一個系統時的涉及技術項。所以接下來,來看看我們在調研中記錄的一些有用的 Web API,希望對你們實現有幫助。

2 / 關鍵技術基礎

本章主要介紹要實現這樣一個前端監控回放系統的四個環節,關于這些內容,rrweb 的描述篇幅會更加翔實,可以更進一步查閱他們的文檔描述。

2.1 應用與狀態的序列化

如何回放一個應用的狀態變化?首先,我們要將應用的內容以及狀態變化收集起來。如果用 jQuery 我們可以這樣實現 body 內容的收集與替換:

  1. // record 
  2. const snapshot = $('body').clone(); 
  3. // replay 
  4. $('body').replaceWith(snapshot); 

如果換用MediaRecorder API,利用 ondataavailable 和 onstop 兩個事件處理 API,我們還可以將 DOM 轉變成可播放的媒體文件,比如這樣:

  1. startRecording() { 
  2.     const stream = (this.canvas as any).captureStream(); 
  3.     this.recorder = new MediaRecorder(stream, { mimeType: 'video/webm' }); 
  4.     const data = []; 
  5.    
  6.     this.recorder.ondataavailable = (event) => { 
  7.       if (event.data && event.data.size) { 
  8.         data.push(event.data); 
  9.       } 
  10.     }; 
  11.   
  12.     this.recorder.onstop = () => { 
  13.       const url = URL.createObjectURL(new Blob(data, { type: 'video/webm' })); 
  14.       this.videoUrl$.next( 
  15.         this.sanitizer.bypassSecurityTrustUrl(url) 
  16.       ); 
  17.     }; 
  18.   
  19.     this.recorder.start(); 
  20.     this.recordVideo$.next(true); 

但這些內容是沒法序列化的,而媒體體積又過于龐大且無法進一步分析。舉個例子,一個 input 標簽,如果我們不做任何額外處理只將其轉化成文本進行存儲(類似 innerHTML),那么其中包含的 value 等狀態便會丟失,這便是我們首先需要將 DOM 及其視圖狀態進行序列化的原因所在。關于如何序列化 DOM 也有不少開源方案比如 https://github.com/inikulin/parse5 ,rrweb 在文檔中有提到為什么沒有采用的原因,而我這里簡單列一下在序列化環節中需要考慮的幾點細節:

  • 如何將 DOM 樹轉化成一個帶視圖狀態的樹狀結構,包括未反映在 HTML 中的視圖狀態;
  • 如何定義唯一標識,方便應用狀態變化(快照)的溯源;
  • 如何處理特殊標簽諸如 script 以及樣式等內容,因方案而異;

簡而言之,這部分的目的是完成一個 DOM 樹至可存儲狀態的數據結構映射。

2.2 記錄 DOM 變化與交互的快照

如果只是記錄 DOM 變更的話,我們可以很方便的利用 MutationObserver API 達到變更監聽與記錄這一目的,但此外我們還需要考慮如何將 Mutation 的批量序列轉化為快照上的增量更新。

比如,為了方便針對 Node 進行增刪時可以唯一確定其在樹形結構中的位置,我們最好設計一個合適的 DOM 唯一標記策略,此外,如何優化諸如 mousemove 以及大量頻繁 input 輸入導致的視圖變更等。前者的設計可以繼續用在增量快照的實現上,而后者的表現則直接影響用戶體驗,容易導致 DOM 在更改時 Node 記錄的出現順序錯誤。

這一環節,主要依賴 DOM 的序列化方案繼續處理。在添加一些其他必要信息諸如時間序列編號等,便可以進行存儲等操作了。

2.3 回放重演

簡單來說,重演就是將收集到的數據按照順序依次“播放”一遍,視頻文件的播放需要音視頻解碼器,而我們的重演環節要做的工作就可以簡單理解成一個 Web 應用解碼器,從用戶端收集上來的數據結構除了要做清洗和存儲外,還不能直接被回放側使用,其中有不少需要考慮的細節。

舉個簡單的例子,我們利用 Web API 是沒法達到派發 hover 事件的,但是我們的項目中一定存在大量的 hover 樣式,那么如何針對這些交互做額外處理,對 Node 狀態變化做相應樣式的補全,便成為一個需要考慮的環節結合 mousedown 和 mouseup 兩個事件的觸發時機是否夠用?事件收集的掛載節點如何圈定?這些都是需要考慮的地方。再比如,回放中想跳過被認為無意義的操作片段,如何設計才能保持應用在前后兩個時間節點上不因為跳過的操作而缺失視圖狀態?

這一環節,目的是為了實現對快照存儲下來的數據結構進行回放。因為要保證回放側與收集側的嚴格一致,諸如高精度計時、DOM 補全以及交互效果模擬等細節,都需要詳細設計。

2.4 沙箱

沙箱,是為了給回放提供一個安全可控的運行環境。如何采用 DOM 快照方案,那么便需要考慮如何禁止一些“不安全”的 DOM 操作。例如應用內鏈接跳轉、我們不太可能會直接給用戶打開一個新的 tab,為了保證快照狀態依次回放,我們還需要考慮如何安全準確的反序列化構建 DOM。如果實現上考慮通過派發事件的思路來實現,那么如何準確定位派發的 Node 節點、如何匹配數據請求并響應等等都是需要重點考慮的。

兩種思路都有一些需要共同考慮的事情,比如如何保證運行環境符合瀏覽器的安全限制、需要展示和用戶操作保持一致的渲染層等等。這部分在技術項拆分一節,會提到兩個解決方案,分別是 iframe 以及 puppeteer,此處不再贅述。

3 / 技術項拆分與相關 Web API

Web 有強大的 API List,本章節針對可能用到的 API 與相關技術做一一講解。

3.1 MutationObserver

MutationObserver API 可以用于監聽觀察 DOM 對象的變化并予以記錄數組的形式返回,這可以用在應用初始化和增量快照的記錄部分。關于 MutationObserver 的方法與入參這里不做詳細介紹,簡單來看,要用 MutationObserver API 監聽一個 Node 的變更大致分為這么幾步:

  • 利用 document.getElementById 等 API 定位你所需要的 Node
  • 定義一個 MutationObserverInit 對象,此對象的配置項描述了 DOM 的哪些變化應該提供給當前觀察者的回調函數
  • 定義上述回調函數被調用時的執行邏輯
  • 創建一個觀察器實例并傳入回調函數
  • 調用 MutationObserver 的 observe() 方法開始觀察

以下節選自 MDN 的一段示例代碼用于釋義:

  1. const targetNode = document.getElementById('some-id'); 
  2. const config = { attributes: true, childList: true, subtree: true }; 
  3.  
  4. const callback = function(mutationsList, observer) { 
  5.     for(let mutation of mutationsList) { 
  6.         if (mutation.type === 'childList') { 
  7.             console.log('A child node has been added or removed.'); 
  8.         } 
  9.     } 
  10. }; 
  11.  
  12. const observer = new MutationObserver(callback); 
  13. observer.observe(targetNode, config); 

3.2 iframe 標簽

iframe 大家肯定都用過,它能夠將另一個 HTML 頁面嵌入到當前頁面中。利用 iframe ,我們可以快速構建一個安全的沙箱機制,比如將其用于限制 JavaScript 執行等。除了我們平時直接給 iframe 的 src 屬性賦值外,iframe 還有不少其他值得了解的屬性,這些在完善運行沙箱環境上都會有所幫助,比如其中的 sandbox 屬性便可以對呈現在 iframe 中的內容啟用一些額外的限制條件。

此外,要實現沙箱中不同容器的的通信,可以通過 postMessage API 來完成。這里有一篇非常詳細的文章介紹了 iframe 的方方面面,可以進一步查閱 https://blog.logrocket.com/the-ultimate-guide-to-iframes/ 。

3.3 HTTP Archive

HTTP 請求與響應匯集,即我們常說的 HAR 格式數據。打開 chrome devtools,network tab 下的每一條數據流都代表一個請求,從 http 到 weoscket,request 到 response,包含 request 入參、headers、連接耗時、發起時間、響應時間、TTFB、內容大小等等。如同前面所述,如果要在回放側派發用戶操作的話,那么即需要在采集時將所有請求攔截并標號進行存儲,如此一來,直接收集 HAR 便成了最佳的選擇。

但想要收集 HAR 會遇到一些限制,比如這類數據只在 chrome devtools API 中開放,所以要實現這塊數據的收集,必須采用類似 chrome 插件的形式進行開發,但這樣一來,如何進行用戶無感知的數據收集與上報又成了一個難題。

3.4 network 與 webRequest API

利用 chrome.webRequest API 可以允許我們觀察和分析流量,并在運行中攔截、阻止或修改它。從上文描述來看,要收集 HAR 只能通過這個 API 來實現,下面給出一個調用 network 對請求攔截處理的示例,詳細用法可參照文檔 https://developer.chrome.com/extensions/webRequest

  1. chrome.devtools.network.onRequestFinished.addListener(function (req) { 
  2.     // Only collect Resource when XHR option is enabled 
  3.     if (document.getElementById('check-xhr').checked) { 
  4.         console.log('Resource Collector pushed: ', req.request.url); 
  5.         req.getContent(function (body, encoding) { 
  6.             if (!body) { 
  7.                 console.log('No Content Detected!, Resource Collector will ignore: ', req.request.url); 
  8.             } else { 
  9.                 reqs[req.request.url] = { 
  10.                     body, 
  11.                     encoding 
  12.                 }; 
  13.             } 
  14.             setResourceCount(); 
  15.         }); 
  16.         setResourceCount(); 
  17.     } 
  18. }); 

3.5 Service Worker 與 proxy

我們都知道 Servcie Worker 的 cache API 在 PWA 應用中廣泛使用,但其實除了可以將其用于離線應用體驗增強外,由于 Servcie Worker 擁有更精細、更完整的控制特性,它完全可以作為一個頁面與服務器之間的代理中間層,用于捕獲它所負責的頁面請求,并返回相應資源。

一般來說,基于框架 HTTPClient (Angular) 或者原生 XMLHttpRequest 的監聽,對頁面的請求攔截都或多或少存在一些無法捕獲的盲區,但 Service Worker 不會,你所需要注意的是需要將它放在你的應用根目錄下,或者通過入參在注冊時指定 scope 以使你的 Service Worker 在指定范圍內生效。

關于它,除了理解其生命周期外,還有些細節需要注意,比如作用域 scope。單個 Service Worker 可以控制多個頁面,每個頁面不會有自己獨有的 worker,所以請注意 scope 的生效范圍;在你 scope 范圍內的頁面在加載完時,Service Worker 便可以開始控制它,所以請小心定義 Service Worker 腳本里的全局變量。

當然,為了方便開發,你可以使用 TypeScript、Babel、webpack 等語言和工具,來加速你的開發體驗。MDN 有一篇教程對 Service Worker 入門介紹的挺詳細,可以一看 https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers

上圖為 Service Worker 的生命周期

3.6 Web Worker

Web Worker 的作用,就是為 JavaScript 創造多線程環境,允許主線程創建 Worker 線程,將一些任務分配給后者運行。在主線程運行的同時,Worker 線程在后臺運行,兩者互不干擾。等到 Worker 線程完成計算任務,再把結果返回給主線程。這樣的好處是,一些計算密集型或高延遲的任務,被 Worker 線程負擔了,主線程(通常負責 UI 交互)就會很流暢,不會被阻塞或拖慢。—— 阮一峰的網絡日志

用于收集數據上報的計算工作,由于重計算邏輯且需要頻繁做數據處理,最好不要在主線程操作,否則很影響交互體驗。Web Worker 是一個很好的解決方案,在采用它之后,其余涉及到 BOM/DOM 的相關操作還可以將需要的數據直接事件傳遞通知或者 SharedArrayBuffer 共享。

那么,如何構建一個 Web Worker 呢?一個 worker 文件由簡單的 JavaScript 代碼組成,在寫完之后,你需要在主線程中傳入 URI 來構建這么一個 worker 線程,如下圖所示:

  1. const myWorker = new Worker('worker.js'); 

在 worker 中,除了完善用于計算的邏輯代碼外,我們還可以引入腳本、通過 postMessage 與主線程通信等等:

  1. // 引入腳本 
  2. importScripts('foo.js''bar.js'); 
  3.  
  4. // 在 Web Worker 中監聽消息與向外通信 
  5. onmessage = function(e) { 
  6.   console.log('Message received from main script'); 
  7.   var workerResult = 'Result: ' + (e.data[0] * e.data[1]); 
  8.   console.log('Posting message back to main script'); 
  9.   postMessage(workerResult); 

上面提到的 Service Worker 也可以算作 Web Worker 的一個相似品,但與一般的 Web Worker 不同,Service Worker 有一些額外的特性來實現代理的目的。只要它們被安裝且被激活,Service Worker 就可以攔截主線程中發起的任何網絡請求。

此外,還有一個特別的 Worker 叫做 Worklet,這些 API 都挺有意思,值得另開篇幅介紹,但與本文暫不相關,便不詳述。

3.7 空閑調度與 requestIdleCallback

window.requestIdleCallback() 方法將在瀏覽器的空閑時段內調用的函數排隊。這使開發者能夠在主事件循環上執行后臺和低優先級工作,而不會影響延遲關鍵事件,如動畫和輸入響應。函數一般會按先進先調用的順序執行,然而,如果回調函數指定了執行超時時間 timeout,則有可能為了在超時前執行函數而打亂執行順序。 —— MDN

由于系統需要對用戶數據進行全量收集,除了計算邏輯的負擔分攤外,包含序列化節點、快照等結構數據的上傳勢必又會成為項目潛在的瓶頸與需要考慮的優化點。利用 requestIdleCallback API,我們可以保證數據在處理后的上報(網絡請求)不對用戶交互造成影響,例如使用戶頁面卡頓等。

更為人熟知的一個 Web API 是 requestAnimationFrame,這個 API 可以告訴瀏覽器在下次重繪之前執行傳入的回調函數,由于是每幀執行一次,所以其每秒的執行次數與瀏覽器屏幕刷新次數一致,通常是每秒60次。而 requestIdleCallback 與其相反,它會在每幀的最后執行,但并不是每一幀都保證會執行 requestIdleCallback。這個原因很簡單,我們無法保證每一幀結束時我們還有時間,所以并不能保證 requestIdleCallback 的執行時間。

requestIdleCallback API 的設計很簡單,一個空閑調度函數,一個可選配置項參數。

  1. const handle = window.requestIdleCallback(callback[, options]) 

舉個例子,假設我們現在需要追蹤用戶的點擊事件,并將數據上報服務器,利用這個 API 我們可以這樣完成數據收集以及上報調度:

  1. const btns = btns.forEach(btn =>  
  2. btn.addEventListener('click', e => { 
  3.     // 其他交互     
  4.  
  5.     putIntoQueue({ 
  6.       type: 'click' 
  7.       // 收集數據 
  8.     })); 
  9.     schedule(); 
  10. }); 
  11.  
  12. function schedule() { 
  13.     requestIdleCallback( 
  14.       deadline => { 
  15.           while (deadline > 0) { 
  16.             const event = queues.pop(); 
  17.             send(event); 
  18.           } 
  19.       }, 
  20.       { timeout: 1000 } 
  21.    ); 

3.8 本地持久化存儲 - localStorage 與 IndexedDB

既然要上報,那么就要考慮在數據未完成上報時用戶的意外退出或者網絡斷開等。在這些情況下,瀏覽器的本地存儲方案便派上了用場。由于需要保證數據上報的完整性,持久化存儲推薦 localStorage API 以及 IndexedDB API。

利用 localStorage API,我們可以快速的存取字符串形式的鍵值對,但受瀏覽器限制,存儲大小一般有限,僅幾兆而已。

利用 IndexedDB API,我們可以在客戶端存儲大量的結構化數據(也包括文件/二進制大型對象等),IndexedDB 被瀏覽器存在本地磁盤中,于是,你可以將其存儲上限近似看成計算機的剩余存儲容量。

IndexedDB 是一個事務型數據庫系統,類似于基于 SQL 的 RDBMS。 然而,不像 RDBMS 使用固定列表,IndexedDB 是一個基于 JavaScript 的面向對象數據庫。IndexedDB 允許您存儲和檢索用鍵索引的對象;可以存儲結構化克隆算法支持的任何對象。您只需要指定數據庫模式,打開與數據庫的連接,然后檢索和更新一系列事務。

localStorage 與 IndexedDB 的使用都相對容易,MDN 上有較為完善的入門指導,此處便不貼代碼了。

3.9 puppeteer - Headless Chrome Node.js API

假設我們不使用 iframe 來做便捷沙箱環境,那么一個更強大的解決方案便是 puppeteer。

puppeteer 是谷歌官方出品的一個通過 DevTools 協議控制 headless Chrome 的 Node 庫,我們可以通過 puppeteer 提供的 API 直接控制 Chrome,進而模擬大部分用戶在瀏覽器上的操作,來進行 UI Test 或者作為爬蟲訪問頁面來收集數據。有關 puppeteer 的使用可以參考文檔 https://pptr.dev/

回到我們的場景,當需要重演用戶的操作時,我們可以便捷的利用 page API 來做,比如下面這個例子:

  1. const puppeteer = require('puppeteer'); 
  2.  
  3. (async () => { 
  4.   const browser = await puppeteer.launch() 
  5.   const page = await browser.newPage() 
  6.    
  7.   await page.goto('https://hijiangtao.github.io/'
  8.    
  9.   await page.setViewport({ width: 2510, height: 1306 }) 
  10.    
  11.   await page.waitForSelector('.ant-tabs-nav-wrap > .ant-tabs-nav-scroll > .ant-tabs-nav > div'
  12.   await page.click('.ant-tabs-nav-wrap > .ant-tabs-nav-scroll > .ant-tabs-nav > div > .ant-tabs-tab:nth-child(2)'
  13.    
  14.   await page.waitForSelector('.ant-tabs-nav-wrap > .container'
  15.   await page.click('.ant-tabs-nav-wrap > .ant-tabs-nav-scroll > .ant-tabs-nav > div > .ant-tabs-tab:nth-child(1)'
  16.    
  17.   await browser.close() 
  18. })() 

當然,puppeteer 存在廣泛的使用場景,比如生成頁面截圖或者 PDF、進行自動化 UI 測試、構建爬蟲系統、捕獲頁面時間軸進行性能診斷等等,曾經寫過一篇文章介紹如何利用 puppeteer 實現網頁自動分頁截圖,感興趣的朋友可以戳此《 用 puppeteer 實現網站自動分頁截取的趣事 》查看,本文不再對其余細節做詳細解讀。

4 / 結語

本文通過組合介紹了各類 Web API 及一些新技術,試圖通過技術調研以及實際項目開發中總結的經驗,為大家在考慮解決「如何實現一個前端監控回放系統」這一問題上提供思路,本文介紹中若有未涉及到的缺漏之處,也歡迎評論告知補充。

 

責任編輯:張燕妮 來源: Joe’s Blog
相關推薦

2022-04-08 09:52:13

前端監控系統

2020-05-19 10:45:31

沙箱前端原生對象

2017-03-02 13:31:02

監控系統

2023-03-01 09:39:40

調度系統

2019-12-11 10:45:08

Python 開發編程語言

2022-09-05 08:07:25

goreplay監控工具

2017-07-07 15:54:26

Linux監控場景

2025-01-09 06:00:00

Checkmate監控系統開源

2017-12-12 15:24:32

Web Server單線程實現

2018-07-19 09:15:27

2018-09-18 09:38:11

RPC遠程調用網絡通信

2020-11-30 06:20:13

javascript

2022-11-29 17:34:43

虛擬形象系統

2014-05-20 09:59:27

Mnitrix輕型監控系統系統管理員

2023-02-26 01:37:57

goORM代碼

2021-06-15 09:33:44

Kubernetes Prometheus 容器

2020-08-17 08:20:16

iOSAOP框架

2023-09-08 08:22:30

2023-09-08 08:10:48

2024-08-27 12:49:20

點贊
收藏

51CTO技術棧公眾號

97人人爽人人| 中文字幕精品在线播放| 国产一级淫片a视频免费观看| 国产成人三级| 欧美夫妻性生活| 国产情侣第一页| 黄视频在线播放| 国产一区在线观看视频| 668精品在线视频| 成人免费视频入口| xvideos.蜜桃一区二区| 日本福利一区二区| www.欧美黄色| wwwww在线观看免费视频| 国产精品资源在线| 国产成人精品综合久久久| 老女人性淫交视频| 精品国产不卡| 亚洲国产精品人久久电影| 最新天堂中文在线| 国产免费拔擦拔擦8x在线播放 | 香蕉在线观看视频| 亚洲第一二三四区| 亚洲v精品v日韩v欧美v专区 | 成年人视频在线看| 成人高清伦理免费影院在线观看| 国产欧美日韩高清| 午夜影院免费在线观看 | 色综合av综合无码综合网站| 成人在线播放免费观看| 国产亚洲成aⅴ人片在线观看| 99国产超薄肉色丝袜交足的后果| 日韩精选在线观看| 免费视频久久| 久久久久成人精品| 男人在线观看视频| 日韩国产在线| 国产亚洲综合久久| 日本黄色特级片| 电影一区二区在线观看| 日韩美女在线视频| 久久成年人网站| 久久亚洲精品人成综合网| 日韩欧美中文第一页| 黄色成人在线免费观看| 成人影院www在线观看| 国产精品日韩成人| 神马影院一区二区| 啊v视频在线| 国产欧美日韩久久| 亚欧洲精品在线视频免费观看| 三级无遮挡在线观看| 波多野结衣中文字幕一区| 成人激情直播| 欧美在线 | 亚洲| 成人精品电影在线观看| 国产乱码一区| 天堂网在线播放| 99免费精品视频| 精品视频免费观看| 亚洲色图欧美视频| 91伊人久久大香线蕉| 欧美精品二区三区四区免费看视频| 天天干在线观看| 99精品视频免费在线观看| 国内精品久久国产| 视频一区二区三区在线看免费看| 91在线高清观看| 欧美日韩天天操| 国产精品麻豆一区二区三区 | 久久成人久久爱| 91丝袜美腿美女视频网站| aa视频在线免费观看| 国产传媒一区在线| 久久精品美女| jizz亚洲| 亚洲男人的天堂在线观看| 日韩欧美猛交xxxxx无码| а√天堂中文在线资源8| 欧美午夜丰满在线18影院| 不卡av免费在线| 国产精品久久久久久久久久久久久久久 | 日韩午夜视频在线| 日韩视频123| 中文字幕在线永久| 精品免费视频| 欧美成人精品三级在线观看| 日产欧产va高清| 日韩中文字幕av电影| 成人国产精品一区| 污污网站在线免费观看| 国产免费久久精品| 妺妺窝人体色www看人体| 欧美韩国亚洲| 91精品国产色综合久久ai换脸| 亚洲欧洲国产视频| 免费黄色成人| 欧美精品性视频| 亚洲天堂一区在线| 精品亚洲国产成人av制服丝袜| 成人av免费电影| 黄色片在线看| 一区二区三区免费在线观看| 成年网站在线免费观看| 国产美女精品视频免费播放软件| 亚洲精品在线91| 最新一区二区三区| 久久精品一区| 成人自拍网站| 夜级特黄日本大片_在线| 午夜精品久久久久久不卡8050| 九九热精品在线播放| 麻豆成人入口| 久久久999精品视频| 日日摸天天添天天添破| 国产精品88av| 亚洲自拍的二区三区| 多野结衣av一区| 欧美一区二区在线不卡| 永久免费毛片在线观看| 亚洲久久一区| 97中文在线| 91精彩视频在线播放| 精品福利在线看| 99精品视频免费版的特色功能| 国产免费久久| 91精品国产91久久久久久久久| 99久久免费国产精精品| 国产日韩欧美a| 男人天堂1024| 美国成人xxx| 久久久久久999| www.色视频| 综合网在线视频| 成人性生交免费看| 精品日韩欧美一区| 国产精品wwwwww| 三级视频网站在线| 午夜精品影院在线观看| 亚洲色图欧美另类| 欧美日韩视频一区二区三区| 亚洲wwwav| 黄色的网站在线观看| 欧美绝品在线观看成人午夜影视| 中文字幕av久久爽一区| 日日摸夜夜添夜夜添精品视频| 久久99精品久久久久子伦| 2018av在线| 亚洲国产另类 国产精品国产免费| 真实国产乱子伦对白在线| 激情都市一区二区| 亚洲一区 在线播放| 精品一区二区三区免费看| 久久躁狠狠躁夜夜爽| 国产三级伦理片| 亚洲激情图片一区| 亚洲一二三四五| 亚洲国产精品一区制服丝袜| 国产视频在线观看一区| а√在线中文在线新版| 亚洲第一福利网站| 毛片毛片女人毛片毛片| 久久精品人人爽人人爽| 国产又大又黄又粗的视频| 日韩一区二区在线免费| 国产欧美久久一区二区| 成人在线观看亚洲| 亚洲国产精品99久久| 自拍偷拍欧美亚洲| 国产网站一区二区| 亚洲精品性视频| 亚洲欧美综合| 极品校花啪啪激情久久| 日韩大尺度黄色| 色综合影院在线| av小说天堂网| 精品久久久久久久久久久久久久 | 美国成人xxx| 国产成人精品电影久久久| melody高清在线观看| 欧美一区二区三区四区在线观看| 18精品爽视频在线观看| xfplay精品久久| 男女视频在线看| kk眼镜猥琐国模调教系列一区二区 | 亚洲人吸女人奶水| 久久久久无码国产精品一区李宗瑞 | 色青青草原桃花久久综合| 97视频免费在线| 亚洲成人你懂的| 国产精品密蕾丝袜| 国产乱子伦视频一区二区三区 | 老司机免费视频一区二区| 熟女熟妇伦久久影院毛片一区二区| 都市激情久久| 国产精品久久久亚洲| 制服丝袜在线播放| 亚洲欧洲一区二区三区久久| 国产精品爽爽久久| 精品久久久久久久中文字幕 | 国产精品美女久久久免费| 久久黄色美女电影| 亚洲精品影视在线观看| 国产喷水福利在线视频| 色综合久久久久| 激情五月少妇a| 国产欧美日韩精品在线| 国产污在线观看| 久草热8精品视频在线观看| 人妻夜夜添夜夜无码av| 日韩国产在线| 免费影院在线观看一区| 日本一区二区乱| 国产精品久久久久国产a级| 精品丝袜在线| 久久综合国产精品台湾中文娱乐网| 日韩午夜影院| 欧美成人伊人久久综合网| 中国老头性行为xxxx| 岛国av在线不卡| 久久婷婷国产麻豆91| 国产精品国产自产拍在线| 成人性生交大免费看| 成人美女视频在线观看| 午夜免费视频网站| 久久99精品一区二区三区 | 激情偷乱视频一区二区三区| 欧美精品第三页| 国产日韩高清一区二区三区在线| 女人床在线观看| 91日韩在线| 亚洲成人蜜桃| 欧美偷拍自拍| 欧美一区二区在线视频观看| 青草久久视频| 国内一区二区三区在线视频| 亚洲不卡视频| 亚洲最大av网| 精品一区二区三区在线观看视频| 国产美女主播一区| 成人黄色毛片| 国产精品欧美久久久| 日韩欧美一区二区三区在线观看| 欧美一区第一页| 涩涩视频网站在线观看| 91国产美女视频| 草草在线观看| 91成人在线观看国产| a在线视频v视频| 性欧美在线看片a免费观看| 国产啊啊啊视频在线观看| 欧美肥婆姓交大片| 大黄网站在线观看| 韩国精品久久久999| 免费高潮视频95在线观看网站| 高清视频欧美一级| 日本不卡免费高清视频在线| 97国产一区二区精品久久呦 | 精品欧美国产一区二区三区| 日韩成人免费在线观看| 精品欧美一区二区三区| 在线免费观看av网址| 欧美丝袜丝交足nylons图片| 中文字幕人妻一区二区三区视频| 欧美图区在线视频| 国产视频一区二区三区四区五区| 日韩一级片在线观看| 亚洲免费视频网| 亚洲精品国偷自产在线99热| 亚洲欧美日本在线观看| 国产一区二区精品丝袜| 看黄网站在线观看| 97国产精品久久| 97人人做人人爽香蕉精品| 成人精品在线视频| 国产精品对白| 欧美久久久久久一卡四| 婷婷久久一区| 国产v片免费观看| 日韩电影在线免费看| 中文字幕1234区| 91在线视频官网| 免费一级suv好看的国产网站 | 97中文字幕在线| 亚洲综合不卡| 亚洲怡红院在线| 成人禁用看黄a在线| 免费视频91蜜桃| 亚洲自拍偷拍麻豆| wwwwww在线观看| 日韩欧美国产电影| 国产在线色视频| 欧美国产精品日韩| 99久久er| 国内精品久久久久久久果冻传媒| 久久精品国产亚洲夜色av网站| 日韩精品一区二区在线视频| 免费观看久久久4p| 岛国精品资源网站| 中文字幕色av一区二区三区| 久草视频在线观| 欧美一级国产精品| 国产日韩精品在线看| 色综合久久天天综线观看| 亚州一区二区三区| 国产一区在线观| 91精品啪在线观看国产81旧版| 成年人视频观看| 国产乱码字幕精品高清av | 亚洲自拍另类综合| 最好看的日本字幕mv视频大全| 日韩视频在线观看一区二区| 男女av在线| 午夜精品在线观看| 欧美成年网站| 色一情一乱一伦一区二区三区 | 天天摸天天舔天天操| 91美女片黄在线观看91美女| 国产精品老熟女一区二区| 欧美天天综合网| 四虎影院在线域名免费观看| 欧美丰满少妇xxxx| 精品一区二区三区四区五区| 亚洲欧美日韩精品综合在线观看| 亚洲资源av| 亚洲黄色免费在线观看| 亚洲黄色免费网站| 国产精品一区二区av白丝下载 | 一二三四区在线观看| 国产精品久久久久9999| 九九久久成人| 99999精品视频| 99九九99九九九视频精品| 国产大片aaa| 精品日韩av一区二区| 草莓福利社区在线| 成人国产精品久久久久久亚洲| 欧美理论电影大全| 久久婷婷国产91天堂综合精品| 91蜜桃婷婷狠狠久久综合9色| 国产一级生活片| 亚洲成人aaa| 高清电影在线观看免费| 国产91视觉| 亚洲国产高清一区二区三区| 国产综合内射日韩久| 亚洲国产日产av| 亚洲欧美高清视频| 久久久视频免费观看| 精品嫩草影院| 内射国产内射夫妻免费频道| 97se狠狠狠综合亚洲狠狠| 国产手机在线视频| 亚洲精品乱码久久久久久按摩观| 国产精品高颜值在线观看| 精品不卡在线| 久久久久国产精品一区二区| 中文字幕网站在线观看| 欧美在线观看你懂的| 992tv免费直播在线观看| 国产精品综合网站| 亚洲色图欧美| 深夜视频在线观看| 欧美日韩精品在线| 国产视频网站在线| 成人免费午夜电影| 黄色日韩精品| 女~淫辱の触手3d动漫| 欧美综合色免费| 黄色免费在线观看| 国产精品区一区二区三在线播放| 日韩亚洲国产欧美| 久久久久久久毛片| 欧美精品在线一区二区三区| 日本天码aⅴ片在线电影网站| 国产伦精品一区二区三区| 老鸭窝毛片一区二区三区| 毛片视频免费播放| 欧美成人一区二区三区在线观看| 午夜激情电影在线播放| 日韩影院一区| 国产成人精品免费视频网站| 日韩福利片在线观看| 国产亚洲一区二区精品| 久久久久久爱| 欧洲av无码放荡人妇网站| 国产精品麻豆一区二区| 亚洲第一天堂影院| 国产精品v片在线观看不卡| 在线观看日韩| 中文精品在线观看| 678五月天丁香亚洲综合网| 热三久草你在线| 7777在线视频| 久久蜜桃av一区二区天堂| 国产免费的av| 欧美一级淫片丝袜脚交| 午夜日韩在线| 黄色片在线观看免费| 亚洲第一av网|