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

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

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

    瘋狂

    STANDING ON THE SHOULDERS OF GIANTS
    posts - 481, comments - 486, trackbacks - 0, articles - 1
      BlogJava :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

    Netty優(yōu)雅退出機(jī)制和原理(轉(zhuǎn))

    Posted on 2016-07-13 09:39 瘋狂 閱讀(1316) 評論(0)  編輯  收藏 所屬分類: netty
    轉(zhuǎn)載自:http://www.infoq.com/cn/articles/netty-elegant-exit-mechanism-and-principles

    1.進(jìn)程的優(yōu)雅退出

    1.1.Kill -9 PID帶來的問題

    在Linux上通常會通過kill -9 pid的方式強(qiáng)制將某個進(jìn)程殺掉,這種方式簡單高效,因此很多程序的停止腳本經(jīng)常會選擇使用kill -9 pid的方式。

    無論是Linux的Kill -9 pid還是windows的taskkill /f /pid強(qiáng)制進(jìn)程退出,都會帶來一些副作用:對應(yīng)用軟件而言其效果等同于突然掉電,可能會導(dǎo)致如下一些問題:

    1. 緩存中的數(shù)據(jù)尚未持久化到磁盤中,導(dǎo)致數(shù)據(jù)丟失;
    2. 正在進(jìn)行文件的write操作,沒有更新完成,突然退出,導(dǎo)致文件損壞;
    3. 線程的消息隊列中尚有接收到的請求消息還沒來得及處理,導(dǎo)致請求消息丟失;
    4. 數(shù)據(jù)庫操作已經(jīng)完成,例如賬戶余額更新,準(zhǔn)備返回應(yīng)答消息給客戶端時,消息尚在通信線程的發(fā)送隊列中排隊等待發(fā)送,進(jìn)程強(qiáng)制退出導(dǎo)致應(yīng)答消息沒有返回給客戶端,客戶端發(fā)起超時重試,會帶來重復(fù)更新問題;
    5. 其它問題等...

    1.2.JAVA優(yōu)雅退出

    Java的優(yōu)雅停機(jī)通常通過注冊JDK的ShutdownHook來實現(xiàn),當(dāng)系統(tǒng)接收到退出指令后,首先標(biāo)記系統(tǒng)處于退出狀態(tài),不再接收新的消息,然后將積壓的消息處理完,最后調(diào)用資源回收接口將資源銷毀,最后各線程退出執(zhí)行。

    通常優(yōu)雅退出需要有超時控制機(jī)制,例如30S,如果到達(dá)超時時間仍然沒有完成退出前的資源回收等操作,則由停機(jī)腳本直接調(diào)用kill -9 pid,強(qiáng)制退出。

    2. 如何實現(xiàn)Netty的優(yōu)雅退出

    要實現(xiàn)Netty的優(yōu)雅退出,首先需要了解通用Java進(jìn)程的優(yōu)雅退出如何實現(xiàn)。下面我們先講解下優(yōu)雅退出的實現(xiàn)原理,并結(jié)合實際代碼進(jìn)行講解。最后看下如何實現(xiàn)Netty的優(yōu)雅退出。

    2.0.1. 信號簡介

    信號是在軟件層次上對中斷機(jī)制的一種模擬,在原理上,一個進(jìn)程收到一個信號與處理器收到一個中斷請求可以說是一樣的,它是進(jìn)程間一種異步通信的機(jī)制。以Linux的kill命令為例,kill -s SIGKILL pid (即kill -9 pid) 立即殺死指定pid的進(jìn)程,SIGKILL就是發(fā)送給pid進(jìn)程的信號。

    信號具有平臺相關(guān)性,Linux平臺支持的一些終止進(jìn)程信號如下所示:

     

    信號名稱

    用途

    SIGKILL

    終止進(jìn)程,強(qiáng)制殺死進(jìn)程

    SIGTERM

    終止進(jìn)程,軟件終止信號

    SIGTSTP

    停止進(jìn)程,終端來的停止信號

    SIGPROF

    終止進(jìn)程,統(tǒng)計分布圖用計時器到時

    SIGUSR1

    終止進(jìn)程,用戶定義信號1

    SIGUSR2

    終止進(jìn)程,用戶定義信號2

    SIGINT

    終止進(jìn)程,中斷進(jìn)程

    SIGQUIT

    建立CORE文件終止進(jìn)程,并且生成core文件

    Windows平臺存在一些差異,它的一些信號舉例如下:SIGINT(Ctrl+C中斷)、SIGILL、SIGTERM (kill發(fā)出的軟件終止)、SIGBREAK (Ctrl+Break中斷)。

    信號選擇:為了不干擾正常信號的運作,又能模擬Java異步通知,在Linux上我們需要先選定一種特殊的信號。通過查看信號列表上的描述,發(fā)現(xiàn) SIGUSR1 和 SIGUSR2 是允許用戶自定義的信號,我們可以選擇SIGUSR2,為了測試方便,在Windows上我們可以選擇SIGINT。

    2.0.2. Java程序的優(yōu)雅退出

    首先看下通用的Java進(jìn)程優(yōu)雅退出的流程圖:

    第一步,應(yīng)用進(jìn)程啟動的時候,初始化Signal實例,它的代碼示例如下:

    Signal sig = new Signal(getOSSignalType());

    其中Signal構(gòu)造函數(shù)的參數(shù)為String字符串,也就是2.1.1小節(jié)中介紹的信號量名稱。

    第二步,根據(jù)操作系統(tǒng)的名稱來獲取對應(yīng)的信號名稱,代碼如下:

    private String getOSSignalType()    {        return System.getProperties().getProperty("os.name"). 		 toLowerCase().startsWith("win") ? "INT" : "USR2";     }

    判斷是否是windows操作系統(tǒng),如果是則選擇SIGINT,接收Ctrl+C中斷的指令;否則選擇USR2信號,接收SIGUSR2(等價于kill -12 pid)指令。

    第三步,將實例化之后的SignalHandler注冊到JDK的Signal,一旦Java進(jìn)程接收到kill -12 或者 Ctrl+C則回調(diào)handle接口,代碼示例如下:

    Signal.handle(sig, shutdownHandler);

    其中shutdownHandler實現(xiàn)了SignalHandler接口的handle(Signal sgin)方法,代碼示例如下:

    第四步,在接收到信號回調(diào)的handle接口中,初始化JDK的ShutdownHook線程,并將其注冊到Runtime中,示例代碼如下:

    private void invokeShutdownHook()  { 	Thread t = new Thread(new ShutdownHook(), "ShutdownHook-Thread"); 	Runtime.getRuntime().addShutdownHook(t);  }

    第五步,接收到進(jìn)程退出信號后,在回調(diào)的handle接口中執(zhí)行虛擬機(jī)的退出操作,示例代碼如下:

    Runtime.getRuntime().exit(0);

    虛擬機(jī)退出時,底層會自動檢測用戶是否注冊了ShutdownHook任務(wù),如果有,則會自動將ShutdownHook線程拉起,執(zhí)行它的Run方法,用戶只需要在ShutdownHook中執(zhí)行資源釋放操作即可,示例代碼如下:

    class ShutdownHook implements Runnable { 	@Override 	public void run() { 		System.out.println("ShutdownHook execute start..."); 		System.out.print("Netty NioEventLoopGroup shutdownGracefully..."); 		try { 			TimeUnit.SECONDS.sleep(10);//模擬應(yīng)用進(jìn)程退出前的處理操作 		} catch (InterruptedException e) { 				e.printStackTrace(); 		} 		System.out.println("ShutdownHook execute end..."); 	System.out.println("Sytem shutdown over, the cost time is 10000MS"); 		} }

    下面我們在Windows環(huán)境中對通用的Java優(yōu)雅退出程序進(jìn)行測試,打開CMD控制臺,拉起待測試程序,如下所示:

    啟動進(jìn)程:

    查看線程信息,發(fā)現(xiàn)注冊的ShutdownHook線程沒有啟動,符合預(yù)期:

    在控制臺執(zhí)行Ctrl+C,使進(jìn)程退出,示例如下:

    如上圖所示,我們定義的ShutdownHook線程在JVM退出時被執(zhí)行,作為測試程序,它休眠10S之后退出,控制臺打印的相關(guān)信息如下:

    下面我們總結(jié)下通用的Java程序優(yōu)雅退出的技術(shù)要點:

    2.0.3. Netty的優(yōu)雅退出

    在實際項目中,Netty作為高性能的異步NIO通信框架,往往用作基礎(chǔ)通信框架負(fù)責(zé)各種協(xié)議的接入、解析和調(diào)度等,例如在RPC和分布式服務(wù)框架中,往往會使用Netty作為內(nèi)部私有協(xié)議的基礎(chǔ)通信框架。

    當(dāng)應(yīng)用進(jìn)程優(yōu)雅退出時,作為通信框架的Netty也需要優(yōu)雅退出,主要原因如下:

    1. 盡快的釋放NIO線程、句柄等資源;
    2. 如果使用flush做批量消息發(fā)送,需要將積攢在發(fā)送隊列中的待發(fā)送消息發(fā)送完成;
    3. 正在write或者read的消息,需要繼續(xù)處理;
    4. 設(shè)置在NioEventLoop線程調(diào)度器中的定時任務(wù),需要執(zhí)行或者清理。

    下面我們看下Netty優(yōu)雅退出涉及的主要操作和資源對象:

    Netty的優(yōu)雅退出總結(jié)起來有三大步操作:

    1. 把NIO線程的狀態(tài)位設(shè)置成ST_SHUTTING_DOWN狀態(tài),不再處理新的消息(不允許再對外發(fā)送消息);
    2. 退出前的預(yù)處理操作:把發(fā)送隊列中尚未發(fā)送或者正在發(fā)送的消息發(fā)送完、把已經(jīng)到期或者在退出超時之前到期的定時任務(wù)執(zhí)行完成、把用戶注冊到NIO線程的退出Hook任務(wù)執(zhí)行完成;
    3. 資源的釋放操作:所有Channel的釋放、多路復(fù)用器的去注冊和關(guān)閉、所有隊列和定時任務(wù)的清空取消,最后是NIO線程的退出。

    下面我們具體看下如何實現(xiàn)Netty的優(yōu)雅退出:

    Netty優(yōu)雅退出的接口和總?cè)肟谠贓ventLoopGroup,調(diào)用它的shutdownGracefully方法即可,相關(guān)代碼如下:

    bossGroup.shutdownGracefully();  workerGroup.shutdownGracefully();

    除了無參的shutdownGracefully方法,還可以指定退出的超時時間和周期,相關(guān)接口定義如下:

    EventLoopGroup的shutdownGracefully工作原理下個章節(jié)做詳細(xì)講解,結(jié)合Java通用的優(yōu)雅退出機(jī)制,即可實現(xiàn)Netty的優(yōu)雅退出,相關(guān)偽代碼如下:

    //統(tǒng)一定義JVM退出事件,并將JVM退出事件作為主題對進(jìn)程內(nèi)部發(fā)布 //所有需要優(yōu)雅退出的消費者訂閱JVM退出事件主題 //監(jiān)聽JVM退出的ShutdownHook被啟動之后,發(fā)布JVM退出事件 //消費者監(jiān)聽到JVM退出事件,開始執(zhí)行自身的優(yōu)雅退出 //如果所有的非守護(hù)線程都成功完成優(yōu)雅退出,進(jìn)程主動退出 //如果到了退出的超時時間仍然沒正常退出,則由停機(jī)腳本通過kill -9 pid強(qiáng)殺進(jìn)程,強(qiáng)制退出

    總結(jié)一下:JVM的ShutdownHook被觸發(fā)之后,調(diào)用所有EventLoopGroup實例的shutdownGracefully方法進(jìn)行優(yōu)雅退出。由于Netty自身對優(yōu)雅退出有較完善的支持,所以實現(xiàn)起來相對比較簡單。

    2.0.4. 一些誤區(qū)

    在實際工作中,由于對優(yōu)雅退出和資源釋放的原理不太清楚,或者對Netty的接口不太了解,很容易把優(yōu)雅退出和資源釋放混淆,導(dǎo)致出現(xiàn)各種問題。

    如下案例:本意是想把某個Channel關(guān)閉,但是卻調(diào)用了Channel關(guān)聯(lián)的EventLoop的shutdownGracefully,導(dǎo)致把EventLoop線程和注冊在該線程持有的多路復(fù)用器上所有的Channel都關(guān)閉了,錯誤代碼如下所示:

    ctx.channel().eventLoop().shutdownGracefully();

    正確的做法如下所示:調(diào)用channel的close方法,關(guān)閉鏈路,釋放與該Channel相關(guān)的資源:

    ctx.channel().close();

    除非是整個進(jìn)程優(yōu)雅退出,一般情況下不會調(diào)用EventLoopGroup和EventLoop的shutdownGracefully方法,更多的是鏈路channel的關(guān)閉和資源釋放。

    3. Netty優(yōu)雅退出原理分析

    Netty優(yōu)雅退出涉及到線程組、線程、鏈路、定時任務(wù)等,底層實現(xiàn)細(xì)節(jié)非常復(fù)雜,下面我們就層層分解,通過源碼來剖析它的實現(xiàn)原理。

    3.1. NioEventLoopGroup

    NioEventLoopGroup實際是NioEventLoop的線程組,它的優(yōu)雅退出比較簡單,直接遍歷EventLoop數(shù)組,循環(huán)調(diào)用它們的shutdownGracefully方法,源碼如下:

    3.2. NioEventLoop

    調(diào)用NioEventLoop的shutdownGracefully方法,首先就是要修改線程狀態(tài)為正在關(guān)閉狀態(tài),它的實現(xiàn)在父類SingleThreadEventExecutor中,它們的繼承關(guān)系如下:

    SingleThreadEventExecutor的shutdownGracefully代碼比較簡單,就是修改線程的狀態(tài)位,需要注意的是修改時需要對并發(fā)調(diào)用做判斷,如果是由NioEventLoop自身調(diào)用,則不需要加鎖,否則需要加鎖,代碼如下:

    解釋下為什么要加鎖,因為shutdownGracefully是public的方法,任何能夠獲取到NioEventLoop的代碼都可以調(diào)用它,在Netty中,業(yè)務(wù)代碼通常不需要直接獲取NioEventLoop并操作它,但是Netty對NioEventLoop做了比較厚的封裝,它不僅僅只能讀寫消息,還能夠執(zhí)行定時任務(wù),并作為線程池執(zhí)行用戶自定義Task。因此在Channel中將獲取NioEventLoop的方法開放了出來,這就意味著用戶只要能夠獲取到Channel,理論上就會存在并發(fā)執(zhí)行shutdownGracefully的可能,因此在優(yōu)雅退出的時候做了并發(fā)保護(hù)。

    完成狀態(tài)修改之后,剩下的操作主要在NioEventLoop中進(jìn)行,代碼如下:

    我們繼續(xù)看下closeAll的實現(xiàn),它的原理是把注冊在selector上的所有Channel都關(guān)閉,但是有些Channel正在發(fā)送消息,暫時還不能關(guān),需要稍后再執(zhí)行,核心代碼如下:

    循環(huán)調(diào)用Channel Unsafe的close方法,下面我們跳轉(zhuǎn)到Unsafe中,對close方法進(jìn)行分析。

    3.3. AbstractUnsafe

    AbstractUnsafe的close方法主要做了如下幾件事:

    1.判斷當(dāng)前該鏈路是否有消息正在發(fā)送,如果有則將關(guān)閉操作封裝成Task放到eventLoop中稍后再執(zhí)行:

    2.將發(fā)送隊列清空,不再允許發(fā)送新的消息:

    3.調(diào)用SocketChannel的close方法,關(guān)閉鏈路:

    4.調(diào)用pipeline的fireChannelInactive,觸發(fā)鏈路關(guān)閉通知事件:

    5.最后是調(diào)用deregister,從多路復(fù)用器上取消SelectionKey:

    至此,優(yōu)雅退出流程已經(jīng)完成,這是否意味著NioEventLoop線程可以退出了,其實并非如此。

    在此處,只是做了Channel的關(guān)閉和從Selector上的去注冊,總結(jié)如下:

    1. 通過inFlush0來判斷當(dāng)前是否正在發(fā)送消息,如果是,則不執(zhí)行Channel關(guān)閉動作,放入NIO線程的任務(wù)隊列中稍后再執(zhí)行close()操作;
    2. 因為已經(jīng)不允許新的發(fā)送消息加入,一旦發(fā)送操作完成,就執(zhí)行鏈路關(guān)閉、觸發(fā)鏈路關(guān)閉事件和從Selector上取消注冊操作。

    之前已經(jīng)說了,NioEventLoop除了I/O讀寫之外,還兼具定時任務(wù)執(zhí)行、關(guān)閉ShutdownHook的執(zhí)行等,如果此時有到期的定時任務(wù),即使Chanel已經(jīng)關(guān)閉,但是仍然需要繼續(xù)執(zhí)行,線程不能退出。下面我們具體分析下TaskQueue的處理流程。

    3.4. TaskQueue

    NioEventLoop執(zhí)行完closeAll()操作之后,需要調(diào)用confirmShutdown看是否真的能夠退出,它的處理邏輯如下:

    1.執(zhí)行TaskQueue中排隊的Task,代碼如下:

    2.執(zhí)行注冊到NioEventLoop中的ShutdownHook,代碼如下:

    3.判斷是否到達(dá)優(yōu)雅退出的指定超時時間,如果達(dá)到或者過了超時時間,則立即退出,代碼如下:

    4.如果沒到達(dá)指定的超時時間,暫時不退出,每隔100MS檢測下是否有新的任務(wù)加入,有則繼續(xù)執(zhí)行:

    在confirmShutdown方法中,夾雜了一些對已經(jīng)廢棄的shutdown()方法的處理,例如:

    調(diào)用新的shutdownGracefully系列方法,該判斷條件是永遠(yuǎn)都不會成立的,因此對于已經(jīng)廢棄的shutdown相關(guān)的處理邏輯,不再詳細(xì)分析。

    到此為止,confirmShutdown方法講解完畢,confirmShutdown返回true,則NioEventLoop線程正式退出,Netty的優(yōu)雅退出完成,代碼如下:

    3.5. 疑問解答

    3.5.1. runAllTasks重復(fù)執(zhí)行問題

    在NioEventLoop的run方法中,已經(jīng)調(diào)用了runAllTasks方法,為何緊隨其后,在confirmShutdown中有繼續(xù)調(diào)用runAllTasks方法呢,疑問代碼如下:

    原因主要有兩個:

    1.為了防止定時任務(wù)Task或者用戶自定義的線程Task的執(zhí)行過多占用NioEventLoop線程的調(diào)度資源,Netty對NioEventLoop線程I/O操作和非I/O操作時間做了比例限制,即限制非I/O操作的執(zhí)行時間,如上圖紅框中代碼所示。有了執(zhí)行時間限制,因此可能會導(dǎo)致已經(jīng)到期的定時任務(wù)、普通任務(wù)沒有執(zhí)行完,需要等待下次Selector輪詢繼續(xù)執(zhí)行。在線程退出之前,需要對本該執(zhí)行但是沒有執(zhí)行完成的Task進(jìn)行掃尾處理,所以在confirmShutdown中再次調(diào)用了runAllTasks方法;

    2.在調(diào)用runAllTasks方法之后,執(zhí)行confirmShutdown之前,用戶向NioEventLoop中添加了新的普通任務(wù)或者定時任務(wù),因此需要在退出之前再次遍歷并處理一遍Task Queue。

    3.5.2. 優(yōu)雅退出是否能夠保證所有在通信線程排隊的消息全部發(fā)送出去

    實際是無法保證的,它只能保證如果現(xiàn)在正在發(fā)送消息過程中,調(diào)用了優(yōu)雅退出方法,此時不會關(guān)閉鏈路,繼續(xù)發(fā)送,如果發(fā)送操作完成,無論是否還有消息尚未發(fā)送出去,在下一輪Selector的輪詢中,鏈路將會關(guān)閉,沒有發(fā)送完成的消息將會被丟棄,甚至是半包消息。它的處理原理圖如下:

    它的原理比較復(fù)雜,現(xiàn)對主要邏輯處理進(jìn)行解讀:

    1. 調(diào)用優(yōu)雅退出之后,是否關(guān)閉鏈路,判斷標(biāo)準(zhǔn)是inFlush0是否為true,如果為False,則會執(zhí)行鏈路關(guān)閉操作;
    2. 如果用戶是類似批量發(fā)送,例如每達(dá)到N條或者定時觸發(fā)flush操作,則在此期間調(diào)用優(yōu)雅退出方法,inFlush0為False,鏈路關(guān)閉,積壓的待發(fā)送消息會被丟棄掉;
    3. 如果優(yōu)雅退出時鏈路正好在發(fā)送消息過程中,則它不會立即退出,等待發(fā)送完成之后,下次Selector輪詢的時候才退出。在這種場景下,又有兩種可能的場景:

    場景A:如果一次把積壓的消息全部發(fā)送完,沒有發(fā)生寫半包,則不會發(fā)生消息丟失;

    場景B:如果一次沒有把消息發(fā)送完成,此時Netty會監(jiān)聽寫事件,觸發(fā)Selector的下一次輪詢并發(fā)送消息,代碼如下:

    Selector輪詢時,首先處理讀寫事件,然后再處理定時任務(wù)和普通任務(wù),因此在鏈路關(guān)閉之前,還有最后一次繼續(xù)發(fā)送的機(jī)會,代碼如下:

    如果非常不幸,再次發(fā)送仍然沒有把積壓的消息全部發(fā)送完畢,再次發(fā)生了寫半包,那無論是否有積壓消息,執(zhí)行AbstractUnsafe.close的Task還是會把鏈路給關(guān)閉掉,原因是只要完成一次消息發(fā)送操作,Netty就會把inFlush0置為false,代碼如下:

    鏈路關(guān)閉之后,所有尚未發(fā)送的消息都將被丟棄。

    可能有些讀者會有疑問,如果在第二次發(fā)送之后,執(zhí)行AbstractUnsafe.close之前,業(yè)務(wù)正好又調(diào)用了flush操作,inFlush0是否會被修改成True呢?這個是不可能的,因為從Netty 4.X之后線程模型發(fā)生了變更,flush操作不是由用戶線程執(zhí)行,而是由Channel對應(yīng)的NioEventLoop線程執(zhí)行,所以在兩者之間不會發(fā)生inFlush0被修改的情況。

    Netty 4.X之后的線程模型如下所示:

    另外,由于優(yōu)雅退出有超時時間,如果在超時時間內(nèi)沒有完成積壓消息的發(fā)送,也會發(fā)生消息丟棄的情況。

    對于上述場景,需要應(yīng)用層來保證相關(guān)的可靠性,或者對Netty的優(yōu)雅退出機(jī)制進(jìn)行優(yōu)化。

    4. 作者簡介

    李林鋒,2007年畢業(yè)于東北大學(xué),2008年進(jìn)入華為公司從事電信軟件的設(shè)計和開發(fā)工作,有多年Java NIO、平臺中間件設(shè)計和開發(fā)經(jīng)驗,精通Netty、Mina、分布式服務(wù)框架等,《Netty權(quán)威指南》、《分布式服務(wù)框架原理與實踐》作者。目前從事云平臺相關(guān)的架構(gòu)和設(shè)計工作。


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


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 亚洲国产精品久久人人爱| caoporn国产精品免费| 久久精品国产亚洲AV久| 亚洲短视频在线观看| 日本久久久免费高清| 小日子的在线观看免费| 全免费a级毛片免费看| 最近免费中文字幕大全高清大全1| 国产亚洲精品成人久久网站| 亚洲综合无码一区二区| 国产成人免费a在线视频app| 国产精品色午夜视频免费看| 国产成人精品免费视频大全五级| 国产成人免费永久播放视频平台| 国产免费卡一卡三卡乱码| 免费看美女裸露无档网站| 成人免费AA片在线观看| 免费看的一级毛片| 国产免费观看青青草原网站| 免费成人av电影| 日韩免费一区二区三区| 国产午夜影视大全免费观看| 亚洲人成无码网WWW| 精品国产_亚洲人成在线高清| 亚洲激情视频在线观看| 在线日韩日本国产亚洲| 亚洲AV无码一区东京热久久 | 99re6在线精品免费观看| 久久这里只精品国产免费10| 麻豆高清免费国产一区| 暖暖日本免费在线视频 | 久久九九兔免费精品6| 夜夜嘿视频免费看| 亚洲精品线路一在线观看| 国产免费爽爽视频免费可以看| 免费少妇a级毛片人成网| 亚洲av一综合av一区| 亚洲一线产区二线产区区| 亚洲乱码一二三四区麻豆| 国产成人亚洲精品播放器下载| 99免费精品视频|