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

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

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

    hk2000c技術(shù)專欄

    技術(shù)源于哲學(xué),哲學(xué)來源于生活 關(guān)心生活,關(guān)注健康,關(guān)心他人

      BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      111 隨筆 :: 1 文章 :: 28 評論 :: 0 Trackbacks
    您認(rèn)為把 NIO 和 Servlet API 組合在一起是不可能的?請?jiān)俸煤孟胍幌隆T诒疚闹校琂ava 開發(fā)人員 Taylor Cowan 向您展示了如何把生產(chǎn)者/消費(fèi)者模型應(yīng)用到消費(fèi)者非阻塞 I/O,從而輕松地讓 Servlet API 全新地兼容 NIO。在這個(gè)過程中,您將會(huì)看到采用了什么來創(chuàng)建實(shí)際的基于 Servlet 并實(shí)現(xiàn)了 NIO 的 Web 服務(wù)器;您也將發(fā)現(xiàn)在企業(yè)環(huán)境中,那個(gè)服務(wù)器是如何以標(biāo)準(zhǔn)的 Java I/O 服務(wù)器(Tomcat 5.0)為基礎(chǔ)而創(chuàng)建的。

    NIO 是帶有 JDK 1.4 的 Java 平臺的最有名(如果不是最出色的)的添加部分之一。下面的許多文章闡述了 NIO 的基本知識及如何利用非阻塞通道的好處。但它們所遺漏的一件事正是,沒有充分地展示 NIO 如何可以提高 J2EE Web 層的可伸縮性。對于企業(yè)開發(fā)人員來說,這些信息特別密切相關(guān),因?yàn)閷?shí)現(xiàn) NIO 不像把少數(shù)幾個(gè) import 語句改變成一個(gè)新的 I/O 包那樣簡單。首先,Servlet API 采用阻塞 I/O 語義,因此默認(rèn)情況下,它不能利用非阻塞 I/O。其次,不像 JDK 1.0 中那樣,線程不再是“資源獨(dú)占”(resource hog),因此使用較少的線程不一定表明服務(wù)器可以處理更多的客戶機(jī)。

    在本文中,為了創(chuàng)建基于 Servlet 并實(shí)現(xiàn)了 NIO 的 Web 服務(wù)器,您將學(xué)習(xí)如何解決 Servlet API 與非阻塞 I/O 的不配合問題。我們將會(huì)看到在多元的 Web 服務(wù)器環(huán)境中,這個(gè)服務(wù)器是如何針對標(biāo)準(zhǔn) I/O 服務(wù)器(Tomcat 5.0)進(jìn)行伸縮的。為符合企業(yè)中生存期的事實(shí),我們將重點(diǎn)放在當(dāng)保持 socket 連接的客戶機(jī)數(shù)量以指數(shù)級增長時(shí),NIO 與標(biāo)準(zhǔn) I/O 相比較的情況如何。

    注意,本文針對某些 Java 開發(fā)人員,他們已經(jīng)熟悉了 Java 平臺上 I/O 編程的基礎(chǔ)知識。有關(guān)非阻塞 I/O 的介紹,請參閱 參考資料 部分。

    線程不再昂貴

    大家都知道,線程是比較昂貴的。在 Java 平臺的早期(JDK 1.0),線程的開銷是一個(gè)很大負(fù)擔(dān),因此強(qiáng)制開發(fā)人員自定義生成解決方案。一個(gè)常見的解決方案是使用 VM 啟動(dòng)時(shí)創(chuàng)建的線程池,而不是按需創(chuàng)建每個(gè)新線程。盡管最近在 VM 層上提高了線程的性能,但標(biāo)準(zhǔn) I/O 仍然要求分配惟一的線程來處理每個(gè)新打開的 socket。就短期而言,這工作得相當(dāng)不錯(cuò),但當(dāng)線程的數(shù)量增加超過了 1K,標(biāo)準(zhǔn) I/O 的不足就表現(xiàn)出來了。由于要在線程間進(jìn)行上下文切換,因此 CPU 簡直變成了超載。

    由于 JDK 1.4 中引入了 NIO,企業(yè)開發(fā)人員最終有了“單線程”模型的一個(gè)內(nèi)置解決方案:多元 I/O 使得固定數(shù)量的線程可以服務(wù)不斷增長的用戶數(shù)量。

    多路復(fù)用(Multiplexing)指的是通過一個(gè)載波來同時(shí)發(fā)送多個(gè)信號或流。當(dāng)使用手機(jī)時(shí),日常的多路復(fù)用例子就發(fā)生了。無線頻率是稀有的資源,因此無線頻率提供商使用多路復(fù)用技術(shù)通過一個(gè)頻率發(fā)送多個(gè)呼叫。在一個(gè)例子中,把呼叫分成一些段,然后給這些段很短的持續(xù)時(shí)間,并在接收端重新裝配。這就叫做 時(shí)分多路復(fù)用(time-division multiplexing),即 TDM。

    在 NIO 中,接收端相當(dāng)于“選擇器”(參閱 java.nio.channels.Selector )。不是處理呼叫,選擇器是處理多個(gè)打開的 socket。就像在 TDM 中那樣,選擇器重新裝配從多個(gè)客戶機(jī)寫入的數(shù)據(jù)段。這使得服務(wù)器可以用單個(gè)線程管理多個(gè)客戶機(jī)。





    回頁首


    Servlet API 和 NIO

    對于 NIO,非阻塞讀寫是必要的,但它們并不是完全沒有麻煩。除了不會(huì)阻塞之外,非阻塞讀不能給呼叫方任何保證。客戶機(jī)或服務(wù)器應(yīng)用程序可能讀取完整信息、部分消息或者根本讀取不到消息。另外,非阻塞讀可能讀取到太多的消息,從而強(qiáng)制為下一個(gè)呼叫準(zhǔn)備一個(gè)額外的緩沖區(qū)。最后,不像流那樣,讀取了零字節(jié)并不表明已經(jīng)完全接收了消息。

    這些因素使得沒有輪詢就不可能實(shí)現(xiàn)甚至是簡單的 readline 方法。所有的 servlet 容器必須在它們的輸入流上提供 readline 方法。因此,許多開發(fā)人員放棄了創(chuàng)建基于 Servlet 并實(shí)現(xiàn)了 NIO 的 Web 應(yīng)用程序服務(wù)器。不過這里有一個(gè)解決方案,它組合了 Servlet API 和 NIO 的多元 I/O 的能力。

    在下面的幾節(jié)中,您將學(xué)習(xí)如何使用 java.io.PipedInputPipedOutputStream 類來把生產(chǎn)者/消費(fèi)者模型應(yīng)用到消費(fèi)者非阻塞 I/O。當(dāng)讀取非阻塞通道時(shí),把它寫到正由第二個(gè)線程消費(fèi)的管道。注意,這種分解映射線程不同于大多數(shù)基于 Java 的客戶機(jī)/服務(wù)器應(yīng)用程序。這里,我們讓一個(gè)線程單獨(dú)負(fù)責(zé)處理非阻塞通道(生產(chǎn)者),讓另一個(gè)線程單獨(dú)負(fù)責(zé)把數(shù)據(jù)作為流消費(fèi)(消費(fèi)者)。管道也為應(yīng)用程序服務(wù)器解決了非阻塞 I/O 問題,因?yàn)?servlet 在消費(fèi) I/O 時(shí)將采用阻塞語義。





    回頁首


    示例服務(wù)器

    示例服務(wù)器展示了 Servlet API 和 NIO 不兼容的生產(chǎn)者/消費(fèi)者解決方案。該服務(wù)器與 Servlet API 非常相似,可以為成熟的基于 NIO 應(yīng)用程序服務(wù)器提供 POC (proof of concept),是專門編寫來衡量 NIO 相對于標(biāo)準(zhǔn) Java I/O 的性能的。它處理簡單的 HTTP get 請求,并支持來自客戶機(jī)的 Keep-Alive 連接。這是重要的,因?yàn)槎嗦窂?fù)用 I/O 只證明在要求服務(wù)器處理大量打開的 scoket 連接時(shí)是有意的。

    該服務(wù)器被分成兩個(gè)包: org.sse.serverorg.sse.http 包中有提供主要 服務(wù)器 功能的類,比如如下的一些功能:接收新客戶機(jī)連接、閱讀消息和生成工作線程以處理請求。 http 包支持 HTTP 協(xié)議的一個(gè)子集。詳細(xì)闡述 HTTP 超出了本文的范圍。有關(guān)實(shí)現(xiàn)細(xì)節(jié),請從 參考資料 部分下載代碼示例。

    現(xiàn)在讓我們來看一下 org.sse.server 包中一些最重要的類。





    回頁首


    Server 類

    Server 類擁有多路復(fù)用循環(huán) —— 任何基于 NIO 服務(wù)器的核心。在清單 1 中,在服務(wù)器接收新客戶機(jī)或檢測到正把可用的字節(jié)寫到打開的 socket 前, select() 的調(diào)用阻塞了。這與標(biāo)準(zhǔn) Java I/O 的主要區(qū)別是,所有的數(shù)據(jù)都是在這個(gè)循環(huán)中讀取的。通常會(huì)把從特定 socket 中讀取字節(jié)的任務(wù)分配給一個(gè)新線程。使用 NIO 選擇器事件驅(qū)動(dòng)方法,實(shí)際上可以用單個(gè)線程處理成千上萬的客戶機(jī),不過,我們還會(huì)在后面看到線程仍有一個(gè)角色要扮演。

    每個(gè) select() 調(diào)用返回一組事件,指出新客戶機(jī)可用;新數(shù)據(jù)準(zhǔn)備就緒,可以讀取;或者客戶機(jī)準(zhǔn)備就緒,可以接收響應(yīng)。server 的 handleKey() 方法只對新客戶機(jī)( key.isAcceptable() )和傳入數(shù)據(jù) ( key.isReadable() ) 感興趣。到這里,工作就結(jié)束了,轉(zhuǎn)入 ServerEventHandler 類。


    清單 1. Server.java 選擇器循環(huán)
    public void listen() {
                SelectionKey key = null;
                try {
                while (true) {
                selector.select();
                Iterator it = selector.selectedKeys().iterator();
                while (it.hasNext()) {
                key = (SelectionKey) it.next();
                handleKey(key);
                it.remove();
                }
                }
                } catch (IOException e) {
                key.cancel();
                } catch (NullPointerException e) {
                // NullPointer at sun.nio.ch.WindowsSelectorImpl, Bug: 4729342
                e.printStackTrace();
                }
                }
                





    回頁首


    ServerEventHandler 類

    ServerEventHandler 類響應(yīng)服務(wù)器事件。當(dāng)新客戶機(jī)變?yōu)榭捎脮r(shí),它就實(shí)例化一個(gè)新的 Client 對象,該對象代表了那個(gè)客戶機(jī)的狀態(tài)。數(shù)據(jù)是以非阻塞方式從通道中讀取的,并被寫到 Client 對象中。 ServerEventHandler 對象也維護(hù)請求隊(duì)列。為了處理(消費(fèi))隊(duì)列中的請求,生成了不定數(shù)量的工作線程。在傳統(tǒng)的生產(chǎn)者/消費(fèi)者方式下,為了在隊(duì)列變?yōu)榭諘r(shí)線程會(huì)阻塞,并在新請求可用時(shí)線程會(huì)得到通知,需要寫 Queue

    為了支持等待的線程,在清單 2 中已經(jīng)重寫了 remove() 方法。如果列表為空,就會(huì)增加等待線程的數(shù)量,并阻塞當(dāng)前線程。它實(shí)質(zhì)上提供了非常簡單的線程池。


    清單 2. Queue.java
    public class Queue extends LinkedList
                {
                private int waitingThreads = 0;
                public synchronized void insert(Object obj)
                {
                addLast(obj);
                notify();
                }
                public synchronized Object remove()
                {
                if ( isEmpty() ) {
                try	{ waitingThreads++; wait();}
                catch (InterruptedException e)  {Thread.interrupted();}
                waitingThreads--;
                }
                return removeFirst();
                }
                public boolean isEmpty() {
                return 	(size() - waitingThreads <= 0);
                }
                }
                

    工作線程的數(shù)量與 Web 客戶機(jī)的數(shù)量無關(guān)。不是為每個(gè)打開的 socket 分配一個(gè)線程,相反,我們把所有請求放到一個(gè)由一組 RequestHandlerThread 實(shí)例所服務(wù)的通用隊(duì)列中。理想情況下,線程的數(shù)量應(yīng)該根據(jù)處理器的數(shù)量和請求的長度或持續(xù)時(shí)間進(jìn)行調(diào)整。如果請求通過資源或處理需求花了很長時(shí)間,那么通過添加更多的線程,可以提高感知到的服務(wù)質(zhì)量。

    注意,這不一定提高整體的吞吐量,但確實(shí)改善了用戶體驗(yàn)。即使在超載的情況下,也會(huì)給每個(gè)線程一個(gè)處理時(shí)間片。這一原則同樣適用于基于標(biāo)準(zhǔn) Java I/O 的服務(wù)器;不過這些服務(wù)器是受到限制的,因?yàn)闀?huì) 要求 它們?yōu)槊總€(gè)打開的 socket 連接分配一個(gè)線程。NIO 服務(wù)器完全不用擔(dān)心這一點(diǎn),因此它們可以擴(kuò)展到大量用戶。最后的結(jié)果是 NIO 服務(wù)器仍然需要線程,只是不需要那么多。





    回頁首


    請求處理

    Client 類有兩個(gè)用途。首先,通過把傳入的非阻塞 I/O 轉(zhuǎn)換成可由 Servlet API 消費(fèi)的阻塞 InputStream ,它解決了阻塞/非阻塞問題。其次,它管理特定客戶機(jī)的請求狀態(tài)。因?yàn)楫?dāng)全部讀取消息時(shí),非阻塞通道沒有給出任何提示,所以強(qiáng)制我們在協(xié)議層處理這一情況。 Client 類在任意指定的時(shí)刻都指出了它是否正在參與進(jìn)行中的請求。如果它準(zhǔn)備處理新請求, write() 方法就會(huì)為請求處理而將該客戶機(jī)排到隊(duì)列中。如果它已經(jīng)參與了請求,它就只是使用 PipedInputStreamPipedOutputStream 類把傳入的字節(jié)轉(zhuǎn)換成一個(gè) InputStream

    圖 1 展示了兩個(gè)線程圍繞管道進(jìn)行交互。主線程把從通道讀取的數(shù)據(jù)寫到管道中。管道把相同的數(shù)據(jù)作為 InputStream 提供給消費(fèi)者。管道的另一個(gè)重要特性是:它是進(jìn)行緩沖處理的。如果沒有進(jìn)行緩沖處理,主線程在嘗試寫到管道時(shí)就會(huì)阻塞。因?yàn)橹骶€程單獨(dú)負(fù)責(zé)所有客戶機(jī)間的多路復(fù)用,因此我們不能讓它阻塞。


    圖 1. PipedInput/OutputStream
    關(guān)系的圖形表示

    Client 自己排隊(duì)后,工作線程就可以消費(fèi)它了。 RequestHandlerThread 類承擔(dān)了這個(gè)角色。至此,我們已經(jīng)看到主線程是如何連續(xù)地循環(huán)的,它要么接受新客戶機(jī),要么讀取新的 I/O。工作線程循環(huán)等待新請求。當(dāng)客戶機(jī)在請求隊(duì)列上變?yōu)榭捎脮r(shí),它就馬上被 remove() 方法中阻塞的第一個(gè)等待線程所消費(fèi)。


    清單 3. RequestHandlerThread.java
    public void run() {
                while (true) {
                Client client = (Client) myQueue.remove();
                try {
                for (; ; ) {
                HttpRequest req = new HttpRequest(client.clientInputStream,
                myServletContext);
                HttpResponse res = new HttpResponse(client.key);
                defaultServlet.service(req, res);
                if (client.notifyRequestDone())
                break;
                }
                } catch (Exception e) {
                client.key.cancel();
                client.key.selector().wakeup();
                }
                }
                }
                

    然后該線程創(chuàng)建新的 HttpRequestHttpResponse 實(shí)例,并調(diào)用 defaultServlet 的 service 方法。注意, HttpRequest 是用 Client 對象的 clientInputStream 屬性構(gòu)造的。 PipedInputStream 就是負(fù)責(zé)把非阻塞 I/O 轉(zhuǎn)換成阻塞流。

    從現(xiàn)在開始,請求處理就與您在 J2EE Servlet API 中期望的相似。當(dāng)對 servlet 的調(diào)用返回時(shí),工作線程在返回到池中之前,會(huì)檢查是否有來自相同客戶機(jī)的另一個(gè)請求可用。注意,這里用到了單詞 池 (pool)。事實(shí)上,線程會(huì)對隊(duì)列嘗試另一個(gè) remove() 調(diào)用,并變成阻塞,直到下一個(gè)請求可用。





    回頁首


    運(yùn)行示例

    示例服務(wù)器實(shí)現(xiàn)了 HTTP 1.1 協(xié)議的一個(gè)子集。它處理普通的 HTTP get 請求。它帶有兩個(gè)命令行參數(shù)。第一個(gè)指定端口號,第二個(gè)指定 HTML 文件所駐留的目錄。在解壓文件后, 切換到項(xiàng)目目錄,然后執(zhí)行下面的命令,注意要把下面的 webroot 目錄替換為您自己的目錄:

    java -cp bin org.sse.server.Start 8080
                "C:\mywebroot"
                

    還請注意,服務(wù)器并沒有實(shí)現(xiàn)目錄清單,因此必須指定有效的 URL 來指向您的 webroot 目錄下的文件。





    回頁首


    性能結(jié)果

    示例 NIO 服務(wù)器是在重負(fù)載下與 Tomcat 5.0 進(jìn)行比較的。選擇 Tomcat 是因?yàn)樗腔跇?biāo)準(zhǔn) Java I/O 的純 Java 解決方案。為了提高可伸縮性,一些高級的應(yīng)用程序服務(wù)器是用 JNI 本機(jī)代碼優(yōu)化的,因此它們沒有提供標(biāo)準(zhǔn) I/O 和 NIO 之間的很好比較。目標(biāo)是要確定 NIO 是否給出了大量的性能優(yōu)勢,以及是在什么條件下給出的。

    如下是一些說明:

    • Tomcat 是用最大的線程數(shù)量 2000 來配置的,而示例服務(wù)器只允許用 4 個(gè)工作線程運(yùn)行。
    • 每個(gè)服務(wù)器是針對相同的一組簡單 HTTP get 測試的,這些 HTTP get 基本上由文本內(nèi)容組成。

    • 把加載工具(Microsoft Web Application Stress Tool)設(shè)置為使用“Keep-Alive”會(huì)話,導(dǎo)致了大約要為每個(gè)用戶分配一個(gè) socket。然后它導(dǎo)致了在 Tomcat 上為每個(gè)用戶分配一個(gè)線程,而 NIO 服務(wù)器用固定數(shù)量的線程來處理相同的負(fù)載。

    圖 2 展示了在不斷增加負(fù)載下的“請求/秒”率。在 200 個(gè)用戶時(shí),性能是相似的。但當(dāng)用戶數(shù)量超過 600 時(shí),Tomcat 的性能開始急劇下降。這最有可能是由于在這么多的線程間切換上下文的開銷而導(dǎo)致的。相反,基于 NIO 的服務(wù)器的性能則以線性方式下降。記住,Tomcat 必須為每個(gè)用戶分配一個(gè)線程,而 NIO 服務(wù)器只配置有 4 個(gè)工作線程。


    圖 2. 請求/秒
    關(guān)系的圖形表示

    圖 3 進(jìn)一步顯示了 NIO 的性能。它展示了操作的 Socket 連接錯(cuò)誤數(shù)/分鐘。同樣,在大約 600 個(gè)用戶時(shí),Tomcat 的性能急劇下降,而基于 NIO 的服務(wù)器的錯(cuò)誤率保持相對較低。


    圖 3. Socket 連接錯(cuò)誤數(shù)/分鐘
    關(guān)系的圖形表式




    回頁首



    結(jié)束語

    在本文中您已經(jīng)學(xué)習(xí)了,實(shí)際上可以使用 NIO 編寫基于 Servlet 的 Web 服務(wù)器,甚至可以啟用它的非阻塞特性。對于企業(yè)開發(fā)人員來說,這是好消息,因?yàn)樵谄髽I(yè)環(huán)境中,NIO 比標(biāo)準(zhǔn) Java I/O 更能夠進(jìn)行伸縮。不像標(biāo)準(zhǔn)的 Java I/O,NIO 可以用固定數(shù)量的線程處理許多客戶機(jī)。當(dāng)基于 Servlet 的 NIO Web 服務(wù)器用來處理保持和擁有 socket 連接的客戶機(jī)時(shí),會(huì)獲得更好的性能。



    參考資料

    • 您可以參閱本文在 developerWorks 全球站點(diǎn)上的 英文原文.

    • 下載本文中使用的 源代碼



    • 看看“ Merlin 給 Java 平臺帶來了非阻塞 I/O”( developerWorks,2002 年 3 月),獲得 NIO 語義的進(jìn)一步知識。



    • 綜合的 developerWorks 教程“ NIO 入門”( developerWorks,2003 年 11 月)從高級的概念到底層的編程細(xì)節(jié),詳細(xì)論及了 NIO 庫。



    • Merlin Hughes 的由兩部分組成的文章“ Turning streams inside out”( developerWorks,2002 年 9 月)為 Java I/O(標(biāo)準(zhǔn)版和 NIO 版)的一些普遍的挑戰(zhàn)提供了制作精巧的設(shè)計(jì)解決方案。



    • 為獲取有關(guān) Java I/O 問題的一些背景知識,請參閱 Allen Holub 的“ 關(guān)于解決 Java 編程語言線程問題的建議 ”( developerWorks,2000 年 10 月)。



    • 訪問 NIO 主頁,從資源中學(xué)習(xí)非阻塞 I/O。



    • JavaNIO.info 是查找有關(guān) NIO 的資源的理想地方。



    • 為從書本系統(tǒng)學(xué)習(xí) NIO,請參閱該領(lǐng)域的經(jīng)典著作:Ron Hitchens 的 Java NIO(O'Reilly & Associates,2002 年)。



    • 在 developerWorks Java 技術(shù)專區(qū)可以找到有關(guān) Java 編程各個(gè)方面的文章。



    • 訪問 Developer Bookstore,獲取技術(shù)書籍的完整列表,其中包括數(shù)百本 Java 相關(guān)的圖書



    • 也請參閱 Java 技術(shù)專區(qū)教程頁,從 developerWorks 獲取免費(fèi)的 Java 專門教程的完整列表。




    關(guān)于作者

     

    Taylor Cowan 是一位軟件工程師,也是一位專攻 J2EE 的自由撰稿人。他從 North Texas 大學(xué)的計(jì)算機(jī)科學(xué)專業(yè)獲得了碩士學(xué)位,另外,他還從 Jazz Arranging 獲得了音樂學(xué)士學(xué)位。

    posted on 2008-01-03 22:58 hk2000c 閱讀(436) 評論(0)  編輯  收藏 所屬分類: Java 技術(shù)
    主站蜘蛛池模板: 91网站免费观看| 小草在线看片免费人成视久网| 国产又黄又爽又猛免费app| 99人中文字幕亚洲区| 无码精品人妻一区二区三区免费看| 亚洲欧洲自拍拍偷精品 美利坚| 国产精品亚洲а∨无码播放不卡 | 18禁超污无遮挡无码免费网站国产| 亚洲网红精品大秀在线观看| 久久青草免费91线频观看不卡| 国产AV无码专区亚洲A∨毛片| 99久久免费国产精精品| 久久国产亚洲精品麻豆| 日本在线免费观看| 亚洲网站视频在线观看| 日韩吃奶摸下AA片免费观看| 亚洲AV永久无码精品放毛片| 无码欧精品亚洲日韩一区夜夜嗨 | 久久亚洲精品无码AV红樱桃| 91嫩草免费国产永久入口| 亚洲国产成人久久77| 成人人免费夜夜视频观看| 亚洲av日韩aⅴ无码色老头| 成人亚洲网站www在线观看| 国产美女视频免费观看的网站| 亚洲小说区图片区另类春色| 午夜免费啪视频在线观看 | 亚洲精品无码不卡在线播放HE| 久久精品视频免费看| 激情综合亚洲色婷婷五月APP| 日韩免费观看一级毛片看看| xxxxxx日本处大片免费看| 亚洲va在线va天堂va不卡下载| 99精品视频在线观看免费专区| 亚洲色欲色欱wwW在线| 亚洲精品无码av天堂| 9277手机在线视频观看免费| 亚洲日韩看片无码电影| 亚洲精品自产拍在线观看| 成人免费一级毛片在线播放视频 | 99精品视频在线视频免费观看|