一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

深入理解嵌入式中重要的編程模型

 漢無為 2021-10-31
大家好,我是寫代碼的籃球球癡。
今天我們看一看業(yè)界一些著名的編程模型

背景

模型是對(duì)事物共性的抽象,編程模型就是對(duì)編程的共性的抽象。


什么是編程的共性呢?

最重要的共性就是:程序設(shè)計(jì)時(shí),代碼的抽象方式、組織方式或復(fù)用方式。編程模型主要是方法與思想。編程模型處于方法或思想性的層面,在很多情況下,也可稱為編程方法、編程方式、編程模式或編程技術(shù)、編程范式。在這里就當(dāng)做同一種說法。

當(dāng)面對(duì)一個(gè)新問題時(shí),通常的想法是通過分析,不斷的轉(zhuǎn)化和轉(zhuǎn)換,得到本質(zhì)相同的熟悉的、或抽象的、簡(jiǎn)單的一個(gè)問題,這就是化歸思想。把初始的問題或?qū)ο蠓Q為原型,把化歸后的相對(duì)定型的模擬化或理想化的對(duì)象稱為模型

編程模型,簡(jiǎn)單地可以理解它就是模板,遇到相似問題就可以方便依模板解決,這樣就簡(jiǎn)化了編程問題。不同的編程環(huán)境和不同的應(yīng)用對(duì)象有不同的編程模型。


圖片


事件驅(qū)動(dòng)

圖片

來源于《Software Architecture Patterns》

事件驅(qū)動(dòng)架構(gòu)(Event-Driven Architecture)是一種用于設(shè)計(jì)應(yīng)用的軟件架構(gòu)和模型,程序的執(zhí)行流由外部事件來決定,它的特點(diǎn)是包含一個(gè)事件循環(huán),當(dāng)外部事件發(fā)生時(shí)使用回調(diào)機(jī)制來觸發(fā)相應(yīng)的處理。主要包括 4 個(gè)基本組件:

  • 事件隊(duì)列(event queue):接收事件的入口,存儲(chǔ)待處理事件

  • 分發(fā)器(event mediator):將不同的事件分發(fā)到不同的業(yè)務(wù)邏輯單元

  • 事件通道(event channel):分發(fā)器與處理器之間的聯(lián)系渠道

  • 事件處理器(event processor):實(shí)現(xiàn)業(yè)務(wù)邏輯,處理完成后會(huì)發(fā)出事件,觸發(fā)下一步操作

為什么采用事件驅(qū)動(dòng)模型?

事件驅(qū)動(dòng)模型也就是我們常說的觀察者,或者發(fā)布-訂閱模型;

圖片

理解它的幾個(gè)關(guān)鍵點(diǎn):

  • 首先是一種對(duì)象間的一對(duì)多的關(guān)系;最簡(jiǎn)單的如交通信號(hào)燈,信號(hào)燈是目標(biāo)(一方),行人注視著信號(hào)燈(多方);

  • 當(dāng)目標(biāo)發(fā)送改變(發(fā)布),觀察者(訂閱者)就可以接收到改變;

  • 觀察者如何處理(如行人如何走,是快走/慢走/不走,目標(biāo)不會(huì)管的),目標(biāo)無需干涉;所以就松散耦合了它們之間的關(guān)系。

許多現(xiàn)代應(yīng)用設(shè)計(jì)都是由事件驅(qū)動(dòng)的,事件驅(qū)動(dòng)應(yīng)用可以用任何一種編程語言來創(chuàng)建,因?yàn)槭录?qū)動(dòng)本身是一種編程方法,而不是一種編程語言。

  • 松耦合——服務(wù)不需要(也不應(yīng)該)知道或依賴于其他服務(wù)。在使用事件時(shí),服務(wù)獨(dú)立運(yùn)行,不了解其他服務(wù),包括其實(shí)現(xiàn)細(xì)節(jié)和傳輸協(xié)議。事件模型下的服務(wù)可以獨(dú)立地、更容易地更新、測(cè)試和部署。

  • 易擴(kuò)展——通過高度獨(dú)立和解耦的事件處理器自然地實(shí)現(xiàn)了可擴(kuò)展性。每個(gè)事件處理器都可以單獨(dú)擴(kuò)展,從而實(shí)現(xiàn)細(xì)粒度的可擴(kuò)展性。

  • 恢復(fù)支持——帶有隊(duì)列的事件驅(qū)動(dòng)架構(gòu)可以通過“重播”過去的事件來恢復(fù)丟失的工作。當(dāng)用戶需要恢復(fù)時(shí),這對(duì)于防止數(shù)據(jù)丟失非常有用。

事件驅(qū)動(dòng)架構(gòu)可以最大程度減少耦合度,因此是現(xiàn)代化分布式應(yīng)用架構(gòu)的理想之選。

深入理解事件驅(qū)動(dòng)

1.異步處理和主動(dòng)輪訓(xùn),要理解事件驅(qū)動(dòng)和程序,就需要與非事件驅(qū)動(dòng)的程序進(jìn)行比較。實(shí)際上,現(xiàn)代的程序大多是事件驅(qū)動(dòng)的,比如多線程的程序,肯定是事件驅(qū)動(dòng)的。早期則存在許多非事件驅(qū)動(dòng)的程序,這樣的程序,在需要等待某個(gè)條件觸發(fā)時(shí),會(huì)不斷地檢查這個(gè)條件,直到條件滿足,這是很浪費(fèi)cpu時(shí)間的。而事件驅(qū)動(dòng)的程序,則有機(jī)會(huì)釋放cpu從而進(jìn)入睡眠態(tài)(注意是有機(jī)會(huì),當(dāng)然程序也可自行決定不釋放cpu),當(dāng)事件觸發(fā)時(shí)被操作系統(tǒng)喚醒,這樣就能更加有效地使用cpu。

2.IO模型,事件驅(qū)動(dòng)框架一般是采用Reactor模式或者Proactor模式的IO模型。

Reactor模式其中非常重要的一環(huán)就是調(diào)用函數(shù)來完成數(shù)據(jù)拷貝,這部分是應(yīng)用程序自己完成的,內(nèi)核只負(fù)責(zé)通知監(jiān)控的事件到來了,所以本質(zhì)上Reactor模式屬于非阻塞同步IO。

圖片

來自:深入理解Linux高性能網(wǎng)絡(luò)架構(gòu)的那些事

Proactor模式,借助于系統(tǒng)本身的異步IO特性,由操作系統(tǒng)進(jìn)行數(shù)據(jù)拷貝,在完成之后來通知應(yīng)用程序來取就可以,效率更高一些,但是底層需要借助于內(nèi)核的異步IO機(jī)制來實(shí)現(xiàn),可能借助于DMA和Zero-Copy技術(shù)來實(shí)現(xiàn),理論上性能更高。

當(dāng)前Windows系統(tǒng)通過IOCP實(shí)現(xiàn)了真正的異步I/O,而在Linux 系統(tǒng)的異步I/O還不完善,比如Linux中的boost.asio模塊就是異步IO的支持,但是目前Linux系統(tǒng)還是以基于Reactor模式的非阻塞同步IO為主。

3.事件隊(duì),事件驅(qū)動(dòng)的程序必定會(huì)直接或者間接擁有一個(gè)事件隊(duì)列,用于存儲(chǔ)未能及時(shí)處理的事件,這個(gè)事件隊(duì)列,可以采用消息隊(duì)列。

4.事件串聯(lián),事件驅(qū)動(dòng)的程序的行為,完全受外部輸入的事件控制,所以事件驅(qū)動(dòng)框架中,存在大量處理程序邏輯,可以通過事件把各個(gè)處理流程關(guān)聯(lián)起來。

5.順序性和原子化,事件驅(qū)動(dòng)的程序可以按照一定的順序處理隊(duì)列中的事件,而這個(gè)順序則是由事件的觸發(fā)順序決定的,這一特性往往被用于保證某些過程的順序性和原子化。

事件驅(qū)動(dòng)的缺點(diǎn)

  • 事件驅(qū)動(dòng)架構(gòu),就是通過引入中間層 來實(shí)現(xiàn)事件發(fā)布-訂閱機(jī)制進(jìn)行組件解耦,看似能帶來不少誘人的優(yōu)點(diǎn),也必然會(huì)增加系統(tǒng)的復(fù)雜度,間接增加開發(fā)難度和維護(hù)難度。

  • 事件驅(qū)動(dòng)架構(gòu)改變了編程思維,將完整的功能過程,拆解為了不同的異步事件處理,也喪失了連貫的流程處理能力。如果事件數(shù)量眾多,就容易在“事件叢林”中迷了路,比如中斷風(fēng)暴,驚群效應(yīng)等。

常用的事件驅(qū)動(dòng)框架
  • select
  • poll
  • epoll
  • libev

  • 中斷系統(tǒng)

消息驅(qū)動(dòng)

圖片

消息驅(qū)動(dòng)事件驅(qū)動(dòng)很類似,都是先有一個(gè)事件,然后產(chǎn)生一個(gè)相應(yīng)的消息,再把消息放入消息隊(duì)列,由需要的項(xiàng)目獲取。他們只是一些細(xì)微區(qū)別,一般都采用相同框架,細(xì)微的區(qū)別:

消息驅(qū)動(dòng):生產(chǎn)者A發(fā)送一個(gè)消息到消息隊(duì)列,消費(fèi)者B收到該消息。生產(chǎn)者A很明確這個(gè)消息是發(fā)給消費(fèi)者B的。通常是P2P模式。

事件驅(qū)動(dòng):生產(chǎn)者A發(fā)出一個(gè)事件,消費(fèi)者B或者消費(fèi)者C收到這個(gè)事件,或者沒人收到這個(gè)事件,生產(chǎn)者A只會(huì)產(chǎn)生一個(gè)事件,不關(guān)心誰會(huì)處理這個(gè)事件 ,通常是發(fā)布-訂閱模型。

現(xiàn)代軟件系統(tǒng)是跨多個(gè)端點(diǎn)運(yùn)行并通過大型網(wǎng)絡(luò)連接的分布式系統(tǒng)。例如,考慮一位航空公司客戶通過 Web 瀏覽器購買機(jī)票。該訂單可能會(huì)通過API,然后通過一系列返回結(jié)果的過程。這些來回通信的一個(gè)術(shù)語是消息傳遞。在消息驅(qū)動(dòng)架構(gòu)中,這些 API 調(diào)用看起來非常像一個(gè)函數(shù)調(diào)用:API 知道它在調(diào)用什么,期待某個(gè)結(jié)果并等待該結(jié)果。

消息驅(qū)動(dòng)的優(yōu)點(diǎn)

  • 開發(fā)難度低:消息驅(qū)動(dòng)類似經(jīng)典的編程模型,調(diào)用一個(gè)函數(shù),等待一個(gè)結(jié)果,對(duì)結(jié)果做一些事情,編程簡(jiǎn)單快速,開發(fā)難度低。

  • 方便調(diào)試維護(hù):因?yàn)榫幊踢壿嬊逦?jiǎn)單,流程清晰,調(diào)試起來更加直接方便,后期維護(hù)也容易。

常用的消息驅(qū)動(dòng)框架
  • API網(wǎng)關(guān)
  • gRPC
  • 微服務(wù)架構(gòu)

事件驅(qū)動(dòng)vs消息驅(qū)動(dòng)

消息驅(qū)動(dòng)的方法與事件驅(qū)動(dòng)的方法一樣有很多優(yōu)點(diǎn)和缺點(diǎn),但每種方法都有自己最適合的情況。

消息感覺很像經(jīng)典的編程模型:調(diào)用一個(gè)函數(shù),等待一個(gè)結(jié)果,對(duì)結(jié)果做一些事情。除了為大多數(shù)程序員所熟悉之外,這種結(jié)構(gòu)還可以使調(diào)試更加直接。另一個(gè)優(yōu)點(diǎn)是消息“阻塞”,這意味著呼叫和響應(yīng)的各個(gè)單元坐下來等待輪到接收者進(jìn)行處理。

事件驅(qū)動(dòng)系統(tǒng)使單個(gè)事件易于隔離測(cè)試。然而,這種與整個(gè)應(yīng)用系統(tǒng)的分離也抑制了這些單元報(bào)告錯(cuò)誤、重試調(diào)用程序甚至只是向用戶確認(rèn)進(jìn)程已完成的能力。換句話說:當(dāng)事件驅(qū)動(dòng)系統(tǒng)中發(fā)生錯(cuò)誤時(shí),很難追蹤到底是哪里出了問題??捎^察性工具正在應(yīng)對(duì)調(diào)試復(fù)雜事件鏈的挑戰(zhàn)。但是,添加到業(yè)務(wù)交易交叉點(diǎn)的每個(gè)工具都會(huì)為負(fù)責(zé)管理這些工作流的程序員帶來另一層復(fù)雜性。

如果通信通常以一對(duì)一的方式進(jìn)行,并且優(yōu)先接收定期狀態(tài)更新或確認(rèn),那么您將傾向于使用基于消息的方法。但是,如果系統(tǒng)之間的交互特別復(fù)雜,并且確認(rèn)和狀態(tài)更新導(dǎo)致的延遲使得等待它們變得不切實(shí)際,那么事件驅(qū)動(dòng)的設(shè)計(jì)可能更合適。但是請(qǐng)記住,大多數(shù)大型組織最終會(huì)采用混合策略,一些面向客戶/API 調(diào)用使用消息驅(qū)動(dòng),而企業(yè)本身使用事件驅(qū)動(dòng)。因此,盡可能多地熟悉兩者并沒有什么壞處。

數(shù)據(jù)驅(qū)動(dòng)

數(shù)據(jù)驅(qū)動(dòng)核心出發(fā)點(diǎn)是相對(duì)于程序邏輯,人類更擅長(zhǎng)于處理數(shù)據(jù)。數(shù)據(jù)比程序邏輯更容易駕馭,所以我們應(yīng)該盡可能的將設(shè)計(jì)的復(fù)雜度從程序代碼轉(zhuǎn)移至數(shù)據(jù)。

例子

假設(shè)有一個(gè)程序,需要處理其他程序發(fā)送的消息,消息類型是字符串,每個(gè)消息都需要一個(gè)函數(shù)進(jìn)行處理。第一印象,我們可能會(huì)這樣處理:

圖片

上面的消息類型取自sip協(xié)議(不完全相同,sip協(xié)議借鑒了http協(xié)議),消息類型可能還會(huì)增加??粗35牧鞒炭赡苡悬c(diǎn)累,檢測(cè)一下中間某個(gè)消息有沒有處理也比較費(fèi)勁,而且,每增加一個(gè)消息,就要增加一個(gè)流程分支。

按照數(shù)據(jù)驅(qū)動(dòng)編程的思路,可能會(huì)這樣設(shè)計(jì):

圖片

下面這種思路的優(yōu)勢(shì):

1、可讀性更強(qiáng),消息處理流程一目了然。

2、更容易修改,要增加新的消息,只要修改數(shù)據(jù)即可,不需要修改流程。

3、重用,第一種方案的很多的else if其實(shí)只是消息類型和處理函數(shù)不同,但是邏輯是一樣的。下面的這種方案就是將這種相同的邏輯提取出來,而把容易發(fā)生變化的部分提到外面。

隱含在背后的思想

很多設(shè)計(jì)思路背后的原理其實(shí)都是相通的,隱含在數(shù)據(jù)驅(qū)動(dòng)編程背后的實(shí)現(xiàn)思想包括:

1、控制復(fù)雜度。通過把程序邏輯的復(fù)雜度轉(zhuǎn)移到人類更容易處理的數(shù)據(jù)中來,從而達(dá)到控制復(fù)雜度的目標(biāo)。

2、隔離變化。像上面的例子,每個(gè)消息處理的邏輯是不變的,但是消息可能是變化的,那就把容易變化的消息和不容易變化的邏輯分離。

3、機(jī)制和策略的分離。和第二點(diǎn)很像,本書中很多地方提到了機(jī)制和策略。上例中,我的理解,機(jī)制就是消息的處理邏輯,策略就是不同的消息處理:

圖片

深入理解編程藝術(shù)之策略與機(jī)制相分離

數(shù)據(jù)驅(qū)動(dòng)編程可以用來做什么

  1. 表驅(qū)動(dòng)法(Table-Driven)

    消除重復(fù)代碼,考慮一個(gè)消息(事件)驅(qū)動(dòng)的系統(tǒng),系統(tǒng)的某一模塊需要和其他的幾個(gè)模塊進(jìn)行通信。它收到消息后,需要根據(jù)消息的發(fā)送方,消息的類型,自身的狀態(tài),進(jìn)行不同的處理。比較常見的一個(gè)做法是用三個(gè)級(jí)聯(lián)的switch分支實(shí)現(xiàn)通過硬編碼來實(shí)現(xiàn):

    switch(sendMode){case:}switch(msgEvent){case:}switch(myStatus){case:}

這種方法的缺點(diǎn):

  • 可讀性不高:找一個(gè)消息的處理部分代碼需要跳轉(zhuǎn)多層代碼。

  • 過多的switch分支,這其實(shí)也是一種重復(fù)代碼。他們都有共同的特性,還   可以再進(jìn)一步進(jìn)行提煉。

  • 可擴(kuò)展性差:如果為程序增加一種新的模塊的狀態(tài),這可能要改變所有的  消息處理的函數(shù),非常的不方便,而且過程容易出錯(cuò)。

  • 程序缺少核心主干:缺少一個(gè)能夠提綱挈領(lǐng)的主干,程序的主干被淹沒在    大量的代碼邏輯之中。


用表驅(qū)動(dòng)法來實(shí)現(xiàn)

根據(jù)定義的三個(gè)枚舉:模塊類型,消息類型,自身模塊狀態(tài),定義一個(gè)函數(shù)跳轉(zhuǎn)表:

typedef struct  __EVENT_DRIVE{  MODE_TYPE mod;//消息的發(fā)送模塊  EVENT_TYPE event;//消息類型  STATUS_TYPE status;//自身狀態(tài)  EVENT_FUN eventfun;//此狀態(tài)下的處理函數(shù)指針}EVENT_DRIVE;
EVENT_DRIVE eventdriver[] = //這就是一張表的定義,不一定是數(shù)據(jù)庫中的表。也可以使自己定義的一個(gè)結(jié)構(gòu)體數(shù)組。{ {MODE_A, EVENT_a, STATUS_1, fun1} {MODE_A, EVENT_a, STATUS_2, fun2} {MODE_A, EVENT_a, STATUS_3, fun3} {MODE_A, EVENT_b, STATUS_1, fun4} {MODE_A, EVENT_b, STATUS_2, fun5}
{MODE_B, EVENT_a, STATUS_1, fun6} {MODE_B, EVENT_a, STATUS_2, fun7} {MODE_B, EVENT_a, STATUS_3, fun8} {MODE_B, EVENT_b, STATUS_1, fun9} {MODE_B, EVENT_b, STATUS_2, fun10}};
int driversize = sizeof(eventdriver) / sizeof(EVENT_DRIVE)//驅(qū)動(dòng)表的大小
EVENT_FUN GetFunFromDriver(MODE_TYPE mod, EVENT_TYPE event, STATUS_TYPE status)//驅(qū)動(dòng)表查找函數(shù){int i = 0;for (i = 0; i < driversize; i ++) {if ((eventdriver[i].mod == mod) && (eventdriver[i].event == event) && (eventdriver[i].status == status)) {return eventdriver[i].eventfun; } }return NULL;}

這種方法的好處:

  • 提高了程序的可讀性。一個(gè)消息如何處理,只要看一下驅(qū)動(dòng)表就知道,非常明顯。

  • 減少了重復(fù)代碼。這種方法的代碼量肯定比第一種少。為什么?因?yàn)樗岩恍┲貜?fù)的東西:switch分支處理進(jìn)行了抽象,把其中公共的東西——根據(jù)三個(gè)元素查找處理方法抽象成了一個(gè)函數(shù)GetFunFromDriver外加一個(gè)驅(qū)動(dòng)表。

  • 可擴(kuò)展性。注意這個(gè)函數(shù)指針,他的定義其實(shí)就是一種契約,類似于java中的接口,c++中的純虛函數(shù),只有滿足這個(gè)條件(入?yún)?,返回值),才可以作為一個(gè)事件的處理函數(shù)。這個(gè)有一點(diǎn)插件結(jié)構(gòu)的味道,你可以對(duì)這些插件進(jìn)行方便替換,新增,刪除,從而改變程序的行為。而這種改變,對(duì)事件處理函數(shù)的查找又是隔離的(也可以叫做隔離了變化)。、

  • 程序有一個(gè)明顯的清晰主干。

  • 降低了復(fù)雜度。通過把程序邏輯的復(fù)雜度轉(zhuǎn)移到人類更容易處理的數(shù)據(jù)中來,從而達(dá)到控制復(fù)雜度的目標(biāo)。

 2. 基于數(shù)據(jù)模型編程

  • 基于Yang模型編程(DSL),YANG是一種語言,是用來建立數(shù)據(jù)模型的語言,可以通過定義業(yè)務(wù)數(shù)據(jù)模型,自動(dòng)生成對(duì)應(yīng)數(shù)據(jù)處理邏輯(比如參數(shù)校驗(yàn),范圍,存儲(chǔ)方式,權(quán)限控制等),典型的數(shù)據(jù)驅(qū)動(dòng)編程;

  • Linux內(nèi)核DTS設(shè)備樹模型,刪除大量hardcode,精簡(jiǎn)內(nèi)核驅(qū)動(dòng)代碼。

  • 基于xml,protobuf數(shù)據(jù)模型編程,界面顯示,web配置邏輯,RPC微服務(wù)等;

數(shù)據(jù)驅(qū)動(dòng)思考

  • 它不是一個(gè)全新的編程模型:它只是一種設(shè)計(jì)思路,而且歷史悠久,在unix/linux社區(qū)應(yīng)用很多;

  • 它不同于面向?qū)ο笤O(shè)計(jì)中的數(shù)據(jù):“數(shù)據(jù)驅(qū)動(dòng)編程中,數(shù)據(jù)不但表示了某個(gè)對(duì)象的狀態(tài),實(shí)際上還定義了程序的流程;OO看重的是封裝,而數(shù)據(jù)驅(qū)動(dòng)編程看重的是編寫盡可能少的代碼。”

  • 數(shù)據(jù)壓倒一切。如果選擇了正確的數(shù)據(jù)結(jié)構(gòu)并把一切組織的井井有條,正確的算法就不言自明。編程的核心是數(shù)據(jù)結(jié)構(gòu),而不是算法?!猂ob Pike

  • 程序員束手無策,只有跳脫代碼,直起腰,仔細(xì)思考數(shù)據(jù)才是最好的行動(dòng)。表達(dá)式編程的精髓。——Fred Brooks

  • 數(shù)據(jù)比程序邏輯更易駕馭。盡可能把設(shè)計(jì)的復(fù)雜度從代碼轉(zhuǎn)移至數(shù)據(jù)是個(gè)好實(shí)踐?!秛nix編程藝術(shù)》作者。

總結(jié)

設(shè)計(jì)模式(古典)主要針對(duì)OOP領(lǐng)域編程設(shè)計(jì)方法的抽象。這里的編程模型,主要是針對(duì)業(yè)務(wù)編程框架的抽象。

消息驅(qū)動(dòng)事件驅(qū)動(dòng),本身有很多相似地方,消息驅(qū)動(dòng)主要代表是經(jīng)典跨進(jìn)程通信架構(gòu),讓消息處理和函數(shù)調(diào)用一樣,邏輯依然可以保持清晰簡(jiǎn)單。而事件驅(qū)動(dòng)采取異步處理方式,最大化解耦,讓程序耦合更低,框架更易擴(kuò)展,兩種編程模型都有各自優(yōu)缺點(diǎn),只有根據(jù)具體的場(chǎng)景找到一種合適使用方法。

數(shù)據(jù)驅(qū)動(dòng)是一種新的編程思考,堅(jiān)持'data as program'準(zhǔn)則,把處理邏輯數(shù)據(jù)化,這樣可以通過不同數(shù)據(jù)配置來實(shí)現(xiàn)不同的邏輯,讓核心代碼更精煉簡(jiǎn)單,框架更易擴(kuò)展。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    91蜜臀精品一区二区三区| 欧美精品久久一二三区| 高清免费在线不卡视频| 91精品国产综合久久福利| 亚洲男人的天堂久久a| 国产一区欧美一区日本道| 二区久久久国产av色| 青青久久亚洲婷婷中文网| 精品欧美国产一二三区| 久热在线视频这里只有精品| 人妻乱近亲奸中文字幕| 日韩三级黄色大片免费观看 | 国产内射一级二级三级| 色哟哟精品一区二区三区| 日韩美女偷拍视频久久| 欧美人妻一区二区三区| 99视频精品免费视频播放| 久久精品国产亚洲av麻豆尤物| 亚洲熟妇中文字幕五十路| 久一视频这里只有精品| 免费高清欧美一区二区视频| 久久精品久久久精品久久| 亚洲国产一区精品一区二区三区色| 精品亚洲一区二区三区w竹菊| 亚洲中文字幕在线观看四区| 中文字幕亚洲精品人妻| 日韩精品中文字幕在线视频| 亚洲乱码av中文一区二区三区| 欧美精品女同一区二区| 91国内视频一区二区三区| 亚洲成人免费天堂诱惑| 99久久精品午夜一区| 天海翼高清二区三区在线| 亚洲精品中文字幕无限乱码| 人人爽夜夜爽夜夜爽精品视频| 国产欧美日韩在线精品一二区| 台湾综合熟女一区二区| 制服丝袜美腿美女一区二区| 高清一区二区三区四区五区| 国产精品一区二区三区黄色片| 亚洲午夜精品视频观看|