<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    放翁(文初)的一畝三分地

      BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      210 隨筆 :: 1 文章 :: 320 評論 :: 0 Trackbacks
     

    Author:放翁(文初)

    Date: 2010/11/23

    Email:fangweng@taobao.com

    mblog: http://t.sina.com.cn/fangweng

    blog: http://blog.csdn.net/cenwenchu79/

             這篇文章將會從問題,技術(shù)背景,設(shè)計實現(xiàn),代碼范例這些角度去談基于管道化和事件驅(qū)動模型的Web請求處理,其中的一些描述和例子也許不是很恰當,也希望得到更多的反饋。

     

    業(yè)務(wù)架構(gòu)設(shè)計:

             基于上述問題,通過兩步走來解決。首先采用支持打破傳統(tǒng)http request生命周期管理的Web容器(很多人說可以自己寫,其實Web容器寫起來并不是最麻煩的,如何做好兼容和照顧好每一個細節(jié)才是漫長發(fā)展的道路)。其次在容器新的線程生命周期管理基礎(chǔ)上封裝業(yè)務(wù)框架,為開發(fā)者屏蔽底層異步化和事件驅(qū)動模式帶來的復(fù)雜流程管理內(nèi)容。

                                                     

    Pipe Service Framework

    基礎(chǔ)管道體系:

             很多時候設(shè)計和實現(xiàn)都會有很多細節(jié)上的差異,而這些差異往往是在事實驗證后對體系的一種修訂,也許修訂后的結(jié)構(gòu)不如修訂前的清晰和優(yōu)雅,但是確實在性能和結(jié)構(gòu)上找到了平衡點,下面就看看兩個基礎(chǔ)管道體系的設(shè)計,后一個是前一個的演進。

                                                        

    流程與角色說明:

             角色分成:Container(傳統(tǒng)的容器)dispatcher(任務(wù)派發(fā)線程數(shù)量根據(jù)性能要求可以是1-m個),job pool(存儲任務(wù)數(shù)據(jù)的本地緩存),event queue(任務(wù)狀態(tài)發(fā)生變化的事件存儲隊列),pipe register center(管道鏈注冊中心,根據(jù)job的自描述信息給出相關(guān)處理的單個管道或者管道鏈),thread pool(用于處理業(yè)務(wù)請求的線程池)

             流程描述如下:

    1. 容器解析請求數(shù)據(jù)。

    2. 創(chuàng)建任務(wù)并存儲到job pool

    3. 發(fā)送job執(zhí)行消息到消息隊列。

    4. 釋放容器線程,掛起請求資源。

    5. Dispatcher阻塞方式的從event queue獲取事件消息。

    6. 如果是刪除任務(wù)事件消息,則將剩余未發(fā)送數(shù)據(jù)flush到客戶端,結(jié)束本次Http會話。(刪除任務(wù)消息是在任務(wù)走完所有管道或者任務(wù)執(zhí)行超時或者任務(wù)執(zhí)行失敗產(chǎn)生)

    7. 如果是執(zhí)行任務(wù)消息事件,則從job pool獲取任務(wù)數(shù)據(jù)。

    8. 根據(jù)任務(wù)信息去pipe register center獲取pipe或者pipe chain

    9. 將任務(wù)數(shù)據(jù)和管道信息發(fā)送給線程池。

    10.              線程池分配線程執(zhí)行任務(wù),如果當前pipe chain執(zhí)行后并沒有完成job,則將job信息存儲到job pool。(這塊后面可以參看一下job 執(zhí)行邏輯圖)

    11.              如果沒有執(zhí)行完畢,則可以創(chuàng)建一個或者多個執(zhí)行事件激發(fā)下一次的處理,如果執(zhí)行完畢,則創(chuàng)建一個刪除任務(wù)消息激發(fā)任務(wù)結(jié)束處理。

    問題:

    1. 規(guī)范化帶來的消息事件過多,線程切換消耗的問題。

    2. Dispatcher自身任務(wù)是否繁重導(dǎo)致處理速度變慢。同時兩套線程池管理麻煩(如果Dispatcher的個數(shù)為M也就可以看作另一個線程池)。

    細節(jié):

    1. 利用容器本身支持請求掛起的方式,將容器線程池和業(yè)務(wù)線程池分割開來。

    2. 如果所有子任務(wù)都是串行化且沒有一個子任務(wù)是由外部系統(tǒng)來實施狀態(tài)遷移,則可以在一個線程中完成所有子任務(wù),減少線程切換和事件分發(fā)帶來的消耗。最極端是退化到任務(wù)交由容器線程一并完成。

    3. 當允許并行多個子任務(wù)執(zhí)行時,只需要在并行子任務(wù)執(zhí)行前的那個任務(wù)完成后,分發(fā)多個任務(wù)執(zhí)行事件,并且任務(wù)執(zhí)行事件指定要求處理的Pipe,就可以讓分發(fā)器將當前任務(wù)分發(fā)給多個線程并行執(zhí)行子任務(wù),后續(xù)詳細介紹子任務(wù)并行處理的過程。

    4. Job會被多線程訪問,因此必要的屬性需要做成線程安全的。另一種模式就是抓取job的數(shù)據(jù)是個快照(clone),在結(jié)果產(chǎn)生后再鎖住合并。


                                                  


    角色和流程說明:

             上圖角色將線程池和消息隊列做了合并,去掉了dispatcherevent queue合并到了 Thread Pool中。

    1. 容器解析請求參數(shù)。

    2. 創(chuàng)建任務(wù)并放置到任務(wù)緩存中。

    3. 發(fā)送執(zhí)行任務(wù)事件到線程池。

    4. 釋放容器線程資源。

    5. 線程池從自身事件隊列中獲取事件。

    6. 如果是刪除事件,則直接刪除任務(wù),并發(fā)送數(shù)據(jù)到客戶端,結(jié)束本地會話。

    7. 如果是執(zhí)行事件,則從pipe register center獲取pipe或者pipe chain

    8. 本地執(zhí)行pipe或者pipe chain

    9. 更新job 數(shù)據(jù)到緩存。

    10.              創(chuàng)建執(zhí)行或者刪除消息事件到本地線程池隊列或者直接連續(xù)執(zhí)行。

    差異:

    1. 將分發(fā)器的功能散落到各個實際業(yè)務(wù)操作線程上,提升處理效率。(增加了對于消息隊列的競爭,不過這個代價不是很大)

    2. 線程可以連續(xù)執(zhí)行子任務(wù),減少任務(wù)事件數(shù)量,減少線程切換代價。(類似于自旋鎖的方式,自己可以盡量的完成可以完成的任務(wù),帶來的問題就是對于不同任務(wù)多階段并行執(zhí)行的策略有所減弱)

    細節(jié):

             和第一種模式一樣,可以退化這個模型到傳統(tǒng)的一個web容器線程處理所有的子任務(wù),減少線程切換代價。

    四種方式的子任務(wù)執(zhí)行說明:

                                                

    傳統(tǒng)的串行化任務(wù)執(zhí)行模式,這種模式下可以交由單個線程全部執(zhí)行,減少線程切換代價,另一方面假如3這個環(huán)節(jié)將會等待外部系統(tǒng)來更新狀態(tài)并繼續(xù)執(zhí)行,那么到2執(zhí)行完畢可以將job放入緩沖區(qū),不產(chǎn)生事件消息,等外部操作完成后,創(chuàng)建執(zhí)行事件消息,激發(fā)后續(xù)管道執(zhí)行任務(wù)。(這種方式可以直接利用容器的掛起,來釋放容器線程,而后續(xù)操作交由后臺業(yè)務(wù)線程池執(zhí)行)

    這里有點說明一下,也是很多朋友問起的,關(guān)于上下文,原來的模式中上下文一種方式是通過方法參數(shù)不斷傳遞,另一種方式保存在ThreadLocal中,而現(xiàn)在因為要切換線程可能就需要做拷貝或者線程之間傳遞。在后面幾種模式中都建議直接將狀態(tài)存儲在本地緩存中共享,帶來的問題就是多線程安全,一種方式是都獲取此對象,然后操作時候做鎖,一種是獲得對象快照,然后合并結(jié)果時鎖定。(這還是取決于多個線程之間處理是否需要看到對方的數(shù)據(jù)變化)

                                                     

             34兩個任務(wù)可以并行完成,同時任何一個完成即可進入5,此時在2完成后,將會產(chǎn)生兩個執(zhí)行任務(wù)消息,并且自描述后續(xù)的Pipe,此時兩個線程可以分別執(zhí)行34,任何一個完畢后創(chuàng)建執(zhí)行消息,激發(fā)任務(wù)處理進入到5流程中。(當發(fā)現(xiàn)已經(jīng)進入5狀態(tài)時,則忽略某個過期任務(wù)消息)

                                                       

             與上一個圖的區(qū)別就是,34將不再是二選一,而是必須全執(zhí)行完畢后才可以進入下一個階段,因此job在執(zhí)行后會先判斷是否被并行的另一個任務(wù)執(zhí)行過,確定全部都Ready,則發(fā)起創(chuàng)建執(zhí)行消息。(在完成3或者4后都會判斷當前合并結(jié)果是否符合進入下一環(huán)節(jié)的要求,符合再發(fā)起新的執(zhí)行任務(wù)消息)

                                                   

             此圖是23兩種方案的結(jié)合,因此參照3的做法完成。

    支持異步化請求處理模型:

             上面的管道模型是較為通用的模型,但考慮到TOP現(xiàn)有業(yè)務(wù)狀況和資源消耗在上述框架下定制了簡單的異步支持模型:

                                                          

    角色及流程說明:

             App第三方ISV軟件,Container Web容器,PipeManager管道注冊管理者(區(qū)別于通用的管道注冊中心在于他對于所有請求都只管理一套Pipe Chain,由他將請求數(shù)據(jù)傳入,并管理整個子任務(wù)的執(zhí)行和分發(fā)),AsynTaskChecker是異步執(zhí)行任務(wù)狀態(tài)變更事件的檢查者(類似于前面的事件分發(fā)器角色),ResultQueue保存事件及事件所帶的上下文,workerThead是工作線程池。

    1. 應(yīng)用發(fā)起服務(wù)請求。

    2. 容器調(diào)用管道管理器去執(zhí)行任務(wù)管道鏈。(解析參數(shù)通過Lazy方式解析字節(jié)流被離散放到了各個管道環(huán)節(jié)中)

    3. 檢查容器是否對異步支持。(便于多容器兼容)

    4. 創(chuàng)建上下文和輸入輸出對象(輸入輸出是管道基本傳遞參數(shù),后面給出類圖結(jié)構(gòu)可知,上下文則是放置在ThreadLocal的數(shù)據(jù),在多個管道邏輯中共享)。

    5. 設(shè)置管道鏈執(zhí)行的起始點(為了異步化后再次進入管道鏈無需重新執(zhí)行前面執(zhí)行過的管道作處理)。

    6. 循環(huán)執(zhí)行管道鏈。

    如有異步管道在管道鏈中:

    a)         復(fù)制管道上下文,保存當前執(zhí)行的管道位置。

    b)         掛起請求,釋放容器線程資源。

    c)         創(chuàng)建線程執(zhí)行異步化管道。

    d)         保存任務(wù)到隊列,等待外部處理結(jié)束改變?nèi)蝿?wù)狀態(tài)。

    e)         推出循環(huán)執(zhí)行后續(xù)管道

    7. 判斷是否是異步執(zhí)行后的重入,如果是則提交異步結(jié)束事件,讓容器在這次管道鏈執(zhí)行后自動提交數(shù)據(jù)到客戶端,結(jié)束本地Http請求會話。

    8. 釋放上下文等線程本地資源。

    9. 返回容器,容器判斷是否有掛起請求,如果請求結(jié)束則返回結(jié)果到客戶端。

    10.              容器自檢查從掛起到當前是否處于執(zhí)行超時(每次掛起請求就會產(chǎn)生一個超時事件,容器循環(huán)的校驗這些事件)

    11.              AsynTaskChecker循環(huán)的檢查隊列中的任務(wù)是否已經(jīng)完成,如果狀態(tài)變更為完成,則提交到給線程池繼續(xù)執(zhí)行后續(xù)的管道鏈。(處于性能考慮,可以將未完成的對象先不放入隊列,等到后端服務(wù)處理完畢再放入,這樣AsynTaskChecker消耗會大大降低,任務(wù)超時完全交給容器來處理,不由業(yè)務(wù)方來處理

    細節(jié):

             主要目的是將容器和業(yè)務(wù)線程池分開,這樣業(yè)務(wù)線程池可以采用后面提到的權(quán)重線程池,通過對權(quán)重線程池的權(quán)重模型設(shè)置來滿足根據(jù)業(yè)務(wù)或者根據(jù)服務(wù)健康狀況來不均衡的分配線程執(zhí)行不同的業(yè)務(wù)請求。

             后端系統(tǒng)的NIO異步方式能夠利用操作系統(tǒng)的中斷來激發(fā)改變對象狀態(tài),節(jié)省前端業(yè)務(wù)線程等待消耗。(如果后端是非異步化的操作,那么執(zhí)行線程只是從容器線程變?yōu)榱藰I(yè)務(wù)線程,當然可以讓業(yè)務(wù)線程更加輕量)

             系統(tǒng)中盡量減少線程切換(能夠一個線程干完的,盡量一個線程執(zhí)行多個子任務(wù)),盡量減少內(nèi)存拷貝復(fù)用對象(當然復(fù)用的代價就是同步問題,因此取決于數(shù)據(jù)操作沖突的概率選擇使用快照還是引用)。


                                                    

             上圖的設(shè)計省略了隊列和檢查者,直接交由業(yè)務(wù)線程阻塞方式等待返回,并直接執(zhí)行后續(xù)的管道,其實也就是對第一種場景的簡化,在后端服務(wù)非異步方式的情況下,推薦這種方式。

    總的來說,任務(wù)切割執(zhí)行在設(shè)計上會覺得很清晰,但是還是要看整體處理時間的分布,如果整個事務(wù)處理消耗的時間很短,那么切割帶來的復(fù)雜度和內(nèi)部消耗就會得不償失,采用簡單的方式來實現(xiàn)可以滿足業(yè)務(wù)上的需求(分離容器和業(yè)務(wù)線程,根據(jù)業(yè)務(wù)需求和系統(tǒng)動態(tài)性能決定線程資源分配),也能保證性能。

    權(quán)重線程池:

             將請求全程處理從容器線程池分離到業(yè)務(wù)線程池后,可以使用帶權(quán)重的線程池來動態(tài)調(diào)整請求線程資源分配,下面是一個簡單的權(quán)重線程池的實現(xiàn)。

    目標:執(zhí)行的任務(wù)實現(xiàn)接口getkey來用于判斷是否有空余線程可以執(zhí)行請求處理任務(wù)。資源被分成兩種:默認全局可使用資源,給特定請求預(yù)留資源。配置分成兩種,限制最大使用線程數(shù),預(yù)留特定請求的線程數(shù)。

                                                    

             上圖是簡單的請求任務(wù)執(zhí)行流程圖,不多解釋了。下圖是狀態(tài)轉(zhuǎn)換圖:

                                                    

             Waitdoing的轉(zhuǎn)換和initdoing的轉(zhuǎn)換一樣,就沒有重復(fù)畫了。內(nèi)部的一些標識解釋(totalCounter全局的計數(shù)器,maxThreadPoolSize線程池最大線程數(shù),defaultCounter是沒有設(shè)置預(yù)留或者限制的請求的計數(shù)器,defaultThresholdmaxThreadPoolSize – sum(預(yù)留線程)keyCounter表示設(shè)置了預(yù)留或者限制的請求自身標識(自身標識通過getkey接口獲得)計數(shù)器,leave表示某一類請求設(shè)置的預(yù)留的數(shù)值,limit表示某一類請求設(shè)置的限制的數(shù)值)

    上圖中大括號中的是場景描述,例如:{Limit Mode}keyCounter <= limit && defaultCounter <= defaultThreshold表示在設(shè)置了限制模式的場景下符合當前請求類型計數(shù)器(當前請求類型通過請求實現(xiàn)getkey接口返回數(shù)據(jù)來區(qū)別)小于限制且默認計數(shù)器小于默認閥值時狀態(tài)轉(zhuǎn)變。

    一點小技巧:在存儲預(yù)留和限制的閥值時,因為存儲在一個map中,通過將閥值設(shè)置為負數(shù)來區(qū)分開,這樣節(jié)省了區(qū)分閥值類型的工作。(這點可以在很多場景中考慮,比如說有多個類型的數(shù)據(jù)配置需要存儲,可以通過數(shù)據(jù)區(qū)間的劃分來判斷是什么類型的,提高判斷效率)

    Comet Push Framework:

             服務(wù)端實現(xiàn):這期做了很簡單的服務(wù)端實現(xiàn),也是為了驗證原型,標準的REST實現(xiàn)。

                                                    

             POST操作,用于新增資源,操作后得到資源返回,會話非長連接。


                                                          

             GET操作,獲得當前請求的資源,會被加入到資源關(guān)注者列表中,保持長連接,用于資源變更后推送變更后的資源對象。

                                                       

             PUT或者Delete操作,短鏈接,同時產(chǎn)生變化事件,交由后臺線程執(zhí)行通知動作。

                                                        

             批量執(zhí)行通知消息。

    1. ResourceBoard阻塞式的從隊列中獲取事件通知。

    2. 創(chuàng)建臨時事件存儲Map

    3. 如果存在通知事件,判斷是否屬于刪除事件(此類事件發(fā)生在異常發(fā)生或者正常結(jié)束),如果是刪除事件,立刻提交給后臺線程池執(zhí)行刪除動作。(刪除動作就是獲取刪除資源的follow列表,然后關(guān)閉所有follow的長連接)

    4. 如果屬于修改事件,判斷當前資源的刪除事件是否已經(jīng)保存在臨時存儲Map中,如果有就不再加入修改事件直接忽略,否則就放入Map

    5. 判斷當前循環(huán)累積事件是否超過一定時間或者存儲的消息量已經(jīng)超過一定值,如果是就跳出循環(huán),如果否,則繼續(xù)從隊列中獲取數(shù)據(jù)循環(huán)判斷,直到隊列為空。

    6. 批量執(zhí)行臨時存儲中的事件消息,如果是修改,則獲取資源的follows來推送變更后的數(shù)據(jù)。

    細節(jié):

        內(nèi)部對于follow的有效性管理是在發(fā)送數(shù)據(jù)時判斷的,如果出錯就會產(chǎn)生刪除事件。

    對于消息批量處理主要是針對數(shù)據(jù)不斷被修改,合并這些無用消息而作,但是某些場景也許就需要所有的修改痕跡,那就不能簡單合并,因此資源需要提供類似合并的接口實現(xiàn)來保證獲取的正確性。

             問題:

           海量長連接的支持。

           采用簡單的Http InnerFrame + js實現(xiàn)客戶端增量展現(xiàn)會使得頁面數(shù)據(jù)越來越多,到一定程度需要放棄連接重新建立follow,減輕客戶端和服務(wù)端雙重壓力。XHR的方式在各種瀏覽器中支持的不一致。


         代碼實現(xiàn),
    Demo及測試效果

    待續(xù)….

    posted on 2010-11-25 14:44 岑文初 閱讀(4113) 評論(7)  編輯  收藏

    評論

    # re: 基于管道化和事件驅(qū)動模型的Web請求處理(二) 2010-11-25 15:11 BucketLI
    我主要有3個問題
    1.并行任務(wù)執(zhí)行的線程池是自己實現(xiàn)的還是使用Concurrent包的線程池?然后線程池的飽和拒絕策略采用的是什么策略?因為Concurrent包中的線程池如果不擴展沒有如同數(shù)據(jù)庫連接池的等待超時策略,一旦滿了就立刻執(zhí)行飽和拒絕策略里面的行為,所以你實現(xiàn)的線程池(如果是的話)飽和拒絕策略是否加入了超時特性,或者干脆使用主線程執(zhí)行?

    2.并行執(zhí)行任務(wù)結(jié)果需要同步合并的場景中,如果其中某個并行任務(wù)失敗,是強行取消其他正常任務(wù),并且回收資源,還是讓其執(zhí)行完畢?

    3.另外通過不斷輪詢隊列中的任務(wù)是否完成與通過CountDownLatch的通知,哪種會比較強?至少后者我需要維護閉鎖在異常情景下的行為,否則會導(dǎo)致內(nèi)存泄露,感覺比較復(fù)雜(這是我們一個場景實現(xiàn)的方式).  回復(fù)  更多評論
      

    # re: 基于管道化和事件驅(qū)動模型的Web請求處理(二) 2010-11-25 20:04 岑文初
    @BucketLI
    1.有權(quán)重的的線程池是基于concurrent上封裝的,自己嘗試過封裝,性能差不多,就沒什么意思了,現(xiàn)在配置maxpoolsize和minpoolsize是一樣大的,這樣當線程池到了上限后就放入隊列,隊列滿了就丟棄,如果需要加入超時特性,其實就把線程池自帶的隊列中的數(shù)據(jù)封裝帶有時間戳,然后定時輪詢清理掉過期任務(wù)。或者采用時間槽的概念,比如分成6個時間槽,每隔10分鐘切換一個時間槽,那么當你任務(wù)20分鐘是容忍極限的話,那么在每次切換時間槽的時候清除相隔2個位置的隊列。
    2.這個還是看你自己的策略,簡單來說,如果一個必選步驟錯了,就產(chǎn)生結(jié)束任務(wù)的事件,有后臺線程執(zhí)行刪除任務(wù)操作。
    3.CountDownLatch你是用線程阻塞?為每一個任務(wù)都分配一個?我不是很清楚你的做法,但是個人感覺CountDownLatch用于任務(wù)的join啟動和結(jié)束比較好,你也可以描述一下你們的做法?  回復(fù)  更多評論
      

    # re: 基于管道化和事件驅(qū)動模型的Web請求處理(二) 2010-11-26 09:41 BucketLI
    @岑文初
    1.這個問題主要在壓力測試的時候發(fā)現(xiàn)的,比如并行的任務(wù)線程池大小為10,隊列為20,也就是同時能夠提交的任務(wù)最大30個,后面SUBMIT或者EXECUTE被拒絕(直接拒絕策略),這確實是符合要求的,但有一個問題是,假設(shè)一次請求生成5個左右的并行任務(wù),只要其中一個任務(wù)失敗就這次請求失敗,因為線程池一直處于忙碌狀態(tài),所以這5個任務(wù)很有可能被部分拒絕,實際上是數(shù)量>=2的并行任務(wù)都有可能被部分拒絕導(dǎo)致這次請求失敗. 所以我的策略改成主線程來執(zhí)行了,但我認為有個等待超時會更好.

    3.因為主線程得收集到并行任務(wù)執(zhí)行完畢的所有結(jié)果才能進行下一步操作(也就是屏障),有N個并行任務(wù),我就實現(xiàn)一個CountDown N次的閉鎖,然后主線程提交完任務(wù)后await,任務(wù)持有這個閉鎖,執(zhí)行完任務(wù)就 CountDown一下,直到CountDown完畢.異常處理是線程池任務(wù)持有主線程實例,發(fā)生異常的時候interrupt主線程,主線程響應(yīng)這個中斷異常,cancel其他任務(wù)以及清理資源.  回復(fù)  更多評論
      

    # re: 基于管道化和事件驅(qū)動模型的Web請求處理(二) 2010-11-26 18:33 anderslin
    后端異步執(zhí)行部分感覺太麻煩了,不知道是否有考慮過采用spring batch作為后端執(zhí)行方案?  回復(fù)  更多評論
      

    # re: 基于管道化和事件驅(qū)動模型的Web請求處理(二) 2010-11-29 19:09 單飛
    有點像工作流,干嗎不用jbpm解決流程呢?  回復(fù)  更多評論
      

    # re: 基于管道化和事件驅(qū)動模型的Web請求處理(二) 2010-12-01 09:55 darren
    請問你的sequence圖,用那個uml工具畫的,rose沒法畫循環(huán),你的可以。  回復(fù)  更多評論
      

    # re: 基于管道化和事件驅(qū)動模型的Web請求處理(二) 2010-12-01 21:00 岑文初
    EA@darren
      回復(fù)  更多評論
      


    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 亚洲毛片αv无线播放一区| 亚洲日韩精品无码专区加勒比| 亚欧色视频在线观看免费| 亚洲欧洲日产国码久在线| 一本色道久久综合亚洲精品| 7x7x7x免费在线观看| 免费国产污网站在线观看不要卡 | 亚洲AV色香蕉一区二区| 免费看的成人yellow视频| a级毛片无码免费真人久久| 国产亚洲精品VA片在线播放| 永久亚洲成a人片777777| 大地资源二在线观看免费高清| eeuss免费天堂影院| 国产精品亚洲自在线播放页码| 久久国产成人亚洲精品影院| 1024免费福利永久观看网站| 精品久久久久久国产免费了| 亚洲精品天堂在线观看| 精品亚洲综合久久中文字幕| 日本高清免费中文字幕不卡| 97在线视频免费公开观看| 一区二区三区在线免费| 亚洲无码一区二区三区| 亚洲色av性色在线观无码| 亚洲片一区二区三区| 日本无卡码免费一区二区三区| 久久国产乱子伦免费精品| 好吊色永久免费视频大全| 亚洲精品无码成人片久久不卡 | 91亚洲精品视频| 亚洲色成人网站WWW永久| 国产人成免费视频| 久久久久久99av无码免费网站| 一区二区三区四区免费视频 | sihu国产精品永久免费| 久久亚洲中文字幕无码| 亚洲制服丝袜第一页| 91情国产l精品国产亚洲区| 亚洲日韩中文字幕在线播放| 亚洲AV无码乱码在线观看|