成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

架構(gòu)篇:Tomcat 高層組件構(gòu)建一個(gè)商業(yè)帝國(guó)

開發(fā) 前端
本篇作為 Tomcat 系列的第三篇,帶大家體會(huì) Tomcat 帝國(guó)是如何構(gòu)建的?高層組件如何管理組件的?連接器和容器是如何被啟動(dòng)和管理的?

[[407961]]

在Tomcat 架構(gòu)解析到設(shè)計(jì)思想借鑒中我們學(xué)到 Tomcat 的總體架構(gòu),學(xué)會(huì)從宏觀上怎么去設(shè)計(jì)一個(gè)復(fù)雜系統(tǒng),怎么設(shè)計(jì)頂層模塊,以及模塊之間的關(guān)系;

Tomcat 實(shí)現(xiàn)的 2 個(gè)核心功能:

處理 Socket 連接,負(fù)責(zé)網(wǎng)絡(luò)字節(jié)流與 Request 和 Response 對(duì)象的轉(zhuǎn)化。

加載并管理 Servlet ,以及處理具體的 Request 請(qǐng)求。

所以 Tomcat 設(shè)計(jì)了兩個(gè)核心組件連接器(Connector)和容器(Container),連接器負(fù)責(zé)對(duì)外交流,容器負(fù)責(zé)內(nèi)部處理。


Tomcat整體架構(gòu)

本篇作為 Tomcat 系列的第三篇,帶大家體會(huì) Tomcat 帝國(guó)是如何構(gòu)建的?高層組件如何管理組件的?連接器和容器是如何被啟動(dòng)和管理的?

Tomcat 啟動(dòng)流程:startup.sh -> catalina.sh start ->java -jar org.apache.catalina.startup.Bootstrap.main()

Tomcat 啟動(dòng)流程

Bootstrap、Catalina、Server、Service、 Engine 都承擔(dān)了什么責(zé)任?

單獨(dú)寫一篇介紹他們是因?yàn)槟憧梢钥吹竭@些啟動(dòng)類或者組件不處理具體請(qǐng)求,它們的任務(wù)主要是管理,管理下層組件的生命周期,并且給下層組件分配任務(wù),也就是把請(qǐng)求路由到負(fù)責(zé)干活兒的組件。

他們就像一個(gè)公司的高層,管理整個(gè)公司的運(yùn)作,將任務(wù)分配給專業(yè)的人。

我們?cè)谠O(shè)計(jì)軟件系統(tǒng)中,不可避免的會(huì)遇到需要一些管理作用的組件,就可以學(xué)習(xí)和借鑒 Tomcat 是如何抽象和管理這些組件的。

因此我把它們比作 Tomcat 的高層,同時(shí)愿干活的不再 996。

對(duì)了,因?yàn)槲⑿鸥牧送扑鸵?guī)則,推文不再按照時(shí)間線顯示,如果不想錯(cuò)過我的文章,請(qǐng)把公眾號(hào)設(shè)置『星標(biāo)』,經(jīng)常點(diǎn)贊,評(píng)論也可以防止失聯(lián),以及支持鼓勵(lì)我持續(xù)更新。

Bootstrap

當(dāng)執(zhí)行 startup.sh 腳本的時(shí)候,就會(huì)啟動(dòng)一個(gè) JVM 運(yùn)行 Tomcat 的啟動(dòng)類 Bootstrap 的 main 方法。

先看下他的成員變量窺探核心功能:

  1. public final class Bootstrap { 
  2.     ClassLoader commonLoader = null
  3.     ClassLoader catalinaLoader = null
  4.     ClassLoader sharedLoader = null
  5. } 

它的主要任務(wù)就是初始化 Tomcat 定義的類加載器,同時(shí)創(chuàng)建 Catalina 對(duì)象。

Bootstrap 就像一個(gè)大神,初始化了類加載器,加載萬物。

關(guān)于為何自定義各種類加載器詳情請(qǐng)查看碼哥的 Tomcat 架構(gòu)設(shè)計(jì)解析 類加載器部分。

初始化類加載器

WebAppClassLoader

假如我們?cè)?Tomcat 中運(yùn)行了兩個(gè) Web 應(yīng)用程序,兩個(gè) Web 應(yīng)用中有同名的 Servlet,但是功能不同,Tomcat 需要同時(shí)加載和管理這兩個(gè)同名的 Servlet類,保證它們不會(huì)沖突,因此 Web 應(yīng)用之間的類需要隔離。

Tomcat 的解決方案是自定義一個(gè)類加載器 WebAppClassLoader, 并且給每個(gè) Web 應(yīng)用創(chuàng)建一個(gè)類加載器實(shí)例。

我們知道,Context 容器組件對(duì)應(yīng)一個(gè) Web 應(yīng)用,因此,每個(gè) Context容器負(fù)責(zé)創(chuàng)建和維護(hù)一個(gè) WebAppClassLoader加載器實(shí)例。

這背后的原理是,不同的加載器實(shí)例加載的類被認(rèn)為是不同的類,即使它們的類名相同。

Tomcat 的自定義類加載器 WebAppClassLoader打破了雙親委托機(jī)制,它首先自己嘗試去加載某個(gè)類,如果找不到則通過 ExtClassLoader 加載 JRE 核心類防止黑客攻擊,無法加載再代理給 AppClassLoader 加載器,其目的是優(yōu)先加載 Web 應(yīng)用自己定義的類。

具體實(shí)現(xiàn)就是重寫 ClassLoader的兩個(gè)方法:findClass和 loadClass。

SharedClassLoader

假如兩個(gè) Web 應(yīng)用都依賴同一個(gè)第三方的 JAR 包,比如 Spring,那 Spring的 JAR 包被加載到內(nèi)存后,Tomcat要保證這兩個(gè) Web 應(yīng)用能夠共享,也就是說 Spring的 JAR 包只被加載一次。

SharedClassLoader 就是 Web 應(yīng)用共享的類庫(kù)的加載器,專門加載 Web 應(yīng)用共享的類。

如果 WebAppClassLoader自己沒有加載到某個(gè)類,就會(huì)委托父加載器 SharedClassLoader去加載這個(gè)類,SharedClassLoader會(huì)在指定目錄下加載共享類,之后返回給 WebAppClassLoader,這樣共享的問題就解決了。

CatalinaClassloader

如何隔離 Tomcat 本身的類和 Web 應(yīng)用的類?

要共享可以通過父子關(guān)系,要隔離那就需要兄弟關(guān)系了。

兄弟關(guān)系就是指兩個(gè)類加載器是平行的,它們可能擁有同一個(gè)父加載器,基于此 Tomcat 又設(shè)計(jì)一個(gè)類加載器 CatalinaClassloader,專門來加載 Tomcat 自身的類。

這樣設(shè)計(jì)有個(gè)問題,那 Tomcat 和各 Web 應(yīng)用之間需要共享一些類時(shí)該怎么辦呢?

老辦法,還是再增加一個(gè) CommonClassLoader,作為 CatalinaClassloader和 SharedClassLoader 的父加載器。

CommonClassLoader能加載的類都可以被 CatalinaClassLoader和 SharedClassLoader 使用。

Catalina

Tomcat 是一個(gè)公司,Catalina 就好像是一個(gè)創(chuàng)始人。因?yàn)樗?fù)責(zé)組建團(tuán)隊(duì),創(chuàng)建 Server 以及所有子組件。

Catalina 的主要任務(wù)就是創(chuàng)建 Server,解析 server.xml 把里面配置的各個(gè)組件創(chuàng)建出來,并調(diào)用每個(gè)組件的 init和 start方法,將整個(gè) Tomcat 啟動(dòng),這樣整個(gè)公司就在正常運(yùn)作了。

我們可以根據(jù) Tomcat 配置文件來直觀感受下:

  1. <Server port="8005" shutdown="SHUTDOWN"> // 頂層組件,可包含多個(gè) Service,代表一個(gè) Tomcat 實(shí)例 
  2.  
  3.   <Service name="Catalina">  // 頂層組件,包含一個(gè) Engine ,多個(gè)連接器 
  4.     <Connector port="8080" protocol="HTTP/1.1" 
  5.                connectionTimeout="20000" 
  6.                redirectPort="8443" /> 
  7.  
  8.     <!-- Define an AJP 1.3 Connector on port 8009 --> 
  9.     <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />  // 連接器 
  10.  
  11.  // 容器組件:一個(gè) Engine 處理 Service 所有請(qǐng)求,包含多個(gè) Host 
  12.     <Engine name="Catalina" defaultHost="localhost"
  13.    // 容器組件:處理指定Host下的客戶端請(qǐng)求, 可包含多個(gè) Context 
  14.       <Host name="localhost"  appBase="webapps" 
  15.             unpackWARs="true" autoDeploy="true"
  16.    // 容器組件:處理特定 Context Web應(yīng)用的所有客戶端請(qǐng)求 
  17.    <Context></Context> 
  18.       </Host> 
  19.     </Engine> 
  20.   </Service> 
  21. </Server> 

作為創(chuàng)始人,Catalina 還需要處理公司的各種異常情況,比如有人搶公章(執(zhí)行了 Ctrl + C 關(guān)閉 Tomcat)。

Tomcat 要如何清理資源呢?

通過向 JVM 注冊(cè)一個(gè)「關(guān)閉鉤子」,具體關(guān)鍵邏輯詳見

org.apache.catalina.startup.Catalina#start 源碼:

  1. Server 不存在則解析 server.xml 創(chuàng)建;
  2. 創(chuàng)建失敗則報(bào)錯(cuò);
  3. 啟動(dòng) Server;
  4. 創(chuàng)建并注冊(cè)「關(guān)閉鉤子」;
  5. await 方法監(jiān)聽停止請(qǐng)求。
  1. /** 
  2.    * Start a new server instance. 
  3.    */ 
  4.   public void start() { 
  5.  
  6.       // 如果 Catalina 持有的 Server 為空則解析 server.xml 創(chuàng)建 
  7.       if (getServer() == null) { 
  8.           load(); 
  9.       } 
  10.  
  11.       if (getServer() == null) { 
  12.           log.fatal("Cannot start server. Server instance is not configured."); 
  13.           return
  14.       } 
  15.  
  16.       // Start the new server 
  17.       try { 
  18.           getServer().start(); 
  19.       } catch (LifecycleException e) { 
  20.           // 省略部分代碼 
  21.       } 
  22.  
  23.       // 創(chuàng)建鉤子并注冊(cè) 
  24.       if (useShutdownHook) { 
  25.           if (shutdownHook == null) { 
  26.               shutdownHook = new CatalinaShutdownHook(); 
  27.           } 
  28.           Runtime.getRuntime().addShutdownHook(shutdownHook); 
  29.       } 
  30.  
  31.       // 監(jiān)聽停止請(qǐng)求,內(nèi)部調(diào)用 Server 的 stop 
  32.       if (await) { 
  33.           await(); 
  34.           stop(); 
  35.       } 
  36.   } 

當(dāng)我們需要在 JVM 關(guān)閉做一些清理工作,比如將緩存數(shù)據(jù)刷到磁盤或者清理一些文件,就可以向 JVM 注冊(cè)一個(gè)「關(guān)閉鉤子」。

它其實(shí)就是一個(gè)線程,當(dāng) JVM 停止前嘗試執(zhí)行這個(gè)線程的 run 方法。

org.apache.catalina.startup.Catalina.CatalinaShutdownHook

  1. protected class CatalinaShutdownHook extends Thread { 
  2.  
  3.       @Override 
  4.       public void run() { 
  5.           try { 
  6.               if (getServer() != null) { 
  7.                   Catalina.this.stop(); 
  8.               } 
  9.           } catch (Throwable ex) { 
  10.             // 省略部分代碼.... 
  11.           } 
  12.       } 
  13.   } 

其實(shí)就是執(zhí)行了 Catalina 的 stop 方法,通過它將整個(gè) Tomcat 停止。

Server

Server 組件的職責(zé)就是管理 Service 組件,負(fù)責(zé)調(diào)用持有的 Service 的 start 方法。

他就像是公司的 CEO,負(fù)責(zé)管理多個(gè)事業(yè)部,每個(gè)事業(yè)部就是一個(gè) Service。

它管理兩個(gè)部門:

  • Connector 連接器:對(duì)外市場(chǎng)營(yíng)銷部,推廣吹牛寫 PPT 的。
  • Container 容器:研發(fā)部門,沒有性生活的 996 。

實(shí)現(xiàn)類是 org.apache.catalina.core.StandardServer,Server 繼承 org.apache.catalina.util.LifecycleMBeanBase,所以他的生命周期也被統(tǒng)一管理,Server 的子組件是 Service,所以還需要管理 Service 的生命周期。

也就是說在啟動(dòng)和關(guān)閉 Server 的時(shí)候會(huì)分別先調(diào)用 Service 的 啟動(dòng)和停止方法。

這就是設(shè)計(jì)思想呀,抽象出生命周期 Lifecycle 接口,體現(xiàn)出接口隔離原則,將生命周期的相關(guān)功能內(nèi)聚。

我們接著看 Server 如何管理 Service 的,核心源碼如下org.apache.catalina.core.StandardServer#addService:

  1. public void addService(Service service) { 
  2.  
  3.         service.setServer(this); 
  4.  
  5.         synchronized (servicesLock) { 
  6.             // 創(chuàng)建 長(zhǎng)度 +1 的數(shù)組 
  7.             Service results[] = new Service[services.length + 1]; 
  8.             // 將舊的數(shù)據(jù)復(fù)制到新數(shù)組 
  9.             System.arraycopy(services, 0, results, 0, services.length); 
  10.             results[services.length] = service; 
  11.             services = results; 
  12.             // 啟動(dòng) Service 組件 
  13.             if (getState().isAvailable()) { 
  14.                 try { 
  15.                     service.start(); 
  16.                 } catch (LifecycleException e) { 
  17.                     // Ignore 
  18.                 } 
  19.             } 
  20.  
  21.             // 發(fā)送事件 
  22.             support.firePropertyChange("service"null, service); 
  23.         } 
  24.  
  25.     } 

在添加 Service 過程中動(dòng)態(tài)拓展數(shù)組長(zhǎng)度,為了節(jié)省內(nèi)存。

除此之外,Server 組件還有一個(gè)重要的任務(wù)是啟動(dòng)一個(gè) Socket 來監(jiān)聽停止端口,這就是為什么你能通過 shutdown 命令來關(guān)閉 Tomcat。

不知道你留意到?jīng)]有,上面 Caralina 的啟動(dòng)方法的最后一行代碼就是調(diào)用了 Server 的 await 方法。

在 await 方法里會(huì)創(chuàng)建一個(gè) Socket 監(jiān)聽 8005 端口,并在一個(gè)死循環(huán)里接收 Socket 上的連接請(qǐng)求,如果有新的連接到來就建立連接,然后從 Socket 中讀取數(shù)據(jù);如果讀到的數(shù)據(jù)是停止命令“SHUTDOWN”,就退出循環(huán),進(jìn)入 stop 流程。

Service

他的職責(zé)就是管理 Connector 連接器 和 頂層容器 Engine,會(huì)分別調(diào)用他們的 start 方法。至此,整個(gè) Tomcat 就算啟動(dòng)完成了。

Service 就是事業(yè)部的話事人,管理兩個(gè)職能部門對(duì)外推廣部(連接器),對(duì)內(nèi)研發(fā)部(容器)。

Service 組件的實(shí)現(xiàn)類是org.apache.catalina.core.StandardService,直接看關(guān)鍵的成員變量。

  1. public class StandardService extends LifecycleMBeanBase implements Service { 
  2.     // 名字 
  3.     private String name = null
  4.      
  5.     // 所屬的 Server 實(shí)例 
  6.     private Server server = null
  7.   
  8.     // 連接器數(shù)組 
  9.     protected Connector connectors[] = new Connector[0]; 
  10.     private final Object connectorsLock = new Object(); 
  11.   
  12.     // 對(duì)應(yīng)的 Engine 容器 
  13.     private Engine engine = null
  14.      
  15.     // 映射器及其監(jiān)聽器 
  16.     protected final Mapper mapper = new Mapper(); 
  17.     protected final MapperListener mapperListener = new MapperListener(this); 
  18. } 

繼承 LifecycleMBeanBase 而 LifecycleMBeanBase 又繼承 LifecycleBase,這里實(shí)際上是模板方法模式的運(yùn)用,org.apache.catalina.util.LifecycleBase#init,org.apache.catalina.util.LifecycleBase#start,org.apache.catalina.util.LifecycleBase#stop 分別是對(duì)應(yīng)的模板方法,內(nèi)部定義了整個(gè)算法流程,子類去實(shí)現(xiàn)自己內(nèi)部具體變化部分,將變與不變抽象出來實(shí)現(xiàn)開閉原則設(shè)計(jì)思路。

那為什么還有一個(gè) MapperListener?這是因?yàn)?Tomcat 支持熱部署,當(dāng) Web 應(yīng)用的部署發(fā)生變化時(shí),Mapper 中的映射信息也要跟著變化,MapperListener 就是一個(gè)監(jiān)聽器,它監(jiān)聽容器的變化,并把信息更新到 Mapper 中,這是典型的觀察者模式。

作為“管理”角色的組件,最重要的是維護(hù)其他組件的生命周期。

此外在啟動(dòng)各種組件時(shí),要注意它們的依賴關(guān)系,也就是說,要注意啟動(dòng)的順序。我們來看看 Service 啟動(dòng)方法:

  1. protected void startInternal() throws LifecycleException { 
  2.   
  3.     //1. 觸發(fā)啟動(dòng)監(jiān)聽器 
  4.     setState(LifecycleState.STARTING); 
  5.   
  6.     //2. 先啟動(dòng) Engine,Engine 會(huì)啟動(dòng)它子容器 
  7.     if (engine != null) { 
  8.         synchronized (engine) { 
  9.             engine.start(); 
  10.         } 
  11.     } 
  12.      
  13.     //3. 再啟動(dòng) Mapper 監(jiān)聽器 
  14.     mapperListener.start(); 
  15.   
  16.     //4. 最后啟動(dòng)連接器,連接器會(huì)啟動(dòng)它子組件,比如 Endpoint 
  17.     synchronized (connectorsLock) { 
  18.         for (Connector connector: connectors) { 
  19.             if (connector.getState() != LifecycleState.FAILED) { 
  20.                 connector.start(); 
  21.             } 
  22.         } 
  23.     } 

這里啟動(dòng)順序也很講究,Service 先啟動(dòng)了 Engine 組件,再啟動(dòng) Mapper 監(jiān)聽器,最后才是啟動(dòng)連接器。

這很好理解,因?yàn)閮?nèi)層組件啟動(dòng)好了才能對(duì)外提供服務(wù),產(chǎn)品沒做出來,市場(chǎng)部也不能瞎忽悠,研發(fā)好了才能啟動(dòng)外層的連接器組件。

而 Mapper 也依賴容器組件,容器組件啟動(dòng)好了才能監(jiān)聽它們的變化,因此 Mapper 和 MapperListener 在容器組件之后啟動(dòng)。

組件停止的順序跟啟動(dòng)順序正好相反的,也是基于它們的依賴關(guān)系。

Engine

他就是一個(gè)研發(fā)部的頭頭,是最頂層的容器組件。繼承 Container,所有的容器組件都繼承 Container,這里實(shí)際上運(yùn)用了組合模式統(tǒng)一管理。

他的實(shí)現(xiàn)類是 org.apache.catalina.core.StandardEngine,繼承 ContainerBase。

  1. public class StandardEngine extends ContainerBase implements Engine { 

他的子容器是 Host,所以持有 Host 容器數(shù)組,這個(gè)屬性每個(gè)容器都會(huì)存在,所以放在抽象類中

  1. protected final HashMap<String, Container> children = new HashMap<>(); 

ContainerBase 用 HashMap 保存了它的子容器,并且 ContainerBase 還實(shí)現(xiàn)了子容器的“增刪改查”,甚至連子組件的啟動(dòng)和停止都提供了默認(rèn)實(shí)現(xiàn),比如 ContainerBase 會(huì)用專門的線程池來啟動(dòng)子容器。

org.apache.catalina.core.ContainerBase#startInternal

  1. // Start our child containers, if any 
  2. Container children[] = findChildren(); 
  3. List<Future<Void>> results = new ArrayList<>(); 
  4. for (Container child : children) { 
  5.   results.add(startStopExecutor.submit(new StartChild(child))); 

Engine 在啟動(dòng) Host 子容器時(shí)就直接重用了這個(gè)方法。

容器組件最重要的功能是處理請(qǐng)求,而 Engine 容器對(duì)請(qǐng)求的“處理”,其實(shí)就是把請(qǐng)求轉(zhuǎn)發(fā)給某一個(gè) Host 子容器來處理,具體是通過 Valve 來實(shí)現(xiàn)的。

每一個(gè)容器組件都有一個(gè) Pipeline,而 Pipeline 中有一個(gè)基礎(chǔ)閥(Basic Valve),透過構(gòu)造方法創(chuàng)建 Pipeline。

  1. public StandardEngine() { 
  2.  
  3.     super(); 
  4.     pipeline.setBasic(new StandardEngineValve()); 
  5.     // 省略部分代碼 
  6.  

Engine 容器的基礎(chǔ)閥定義如下:

  1. final class StandardEngineValve extends ValveBase { 
  2.   
  3.     public final void invoke(Request request, Response response) 
  4.       throws IOException, ServletException { 
  5.    
  6.       // 拿到請(qǐng)求中的 Host 容器 
  7.       Host host = request.getHost(); 
  8.       if (host == null) { 
  9.           return
  10.       } 
  11.    
  12.       // 調(diào)用 Host 容器中的 Pipeline 中的第一個(gè) Valve 
  13.       host.getPipeline().getFirst().invoke(request, response); 
  14.   } 
  15.    

這個(gè)基礎(chǔ)閥實(shí)現(xiàn)非常簡(jiǎn)單,就是把請(qǐng)求轉(zhuǎn)發(fā)到 Host 容器。

從代碼中可以看到,處理請(qǐng)求的 Host 容器對(duì)象是從請(qǐng)求中拿到的,請(qǐng)求對(duì)象中怎么會(huì)有 Host 容器呢?

這是因?yàn)檎?qǐng)求到達(dá) Engine 容器中之前,Mapper 組件已經(jīng)對(duì)請(qǐng)求進(jìn)行了路由處理,Mapper 組件通過請(qǐng)求的 URL 定位了相應(yīng)的容器,并且把容器對(duì)象保存到了請(qǐng)求對(duì)象中。

本文轉(zhuǎn)載自微信公眾號(hào)「 碼哥字節(jié)」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系 碼哥字節(jié)公眾號(hào)。

 

責(zé)任編輯:姜華 來源: 碼哥字節(jié)
相關(guān)推薦

2018-09-13 14:18:20

C語言Java程序員

2021-02-03 11:44:15

NoSQL關(guān)系數(shù)據(jù)庫(kù)

2010-12-07 16:53:43

商業(yè)智能

2009-04-20 23:29:12

Oracle收購(gòu)Sun甲骨文

2019-10-09 16:14:30

Web服務(wù)器Tomcat

2011-08-25 17:19:34

甲骨文商務(wù)智能保險(xiǎn)行業(yè)

2019-12-09 15:00:48

TomcatServlet容器

2022-02-10 07:03:32

流量應(yīng)用架構(gòu)數(shù)據(jù)交換

2018-01-31 15:45:07

前端Vue.js組件

2015-08-11 14:09:44

2018-12-29 08:15:28

Tomcat應(yīng)用部署

2024-08-26 10:31:23

2009-12-09 16:20:37

Linux操作系統(tǒng)

2018-05-08 14:20:03

騰訊阿里互聯(lián)網(wǎng)

2020-11-09 06:38:00

ninja構(gòu)建方式構(gòu)建系統(tǒng)

2018-06-24 16:39:28

Tomcat異常線程

2019-09-29 15:25:13

CockroachDBGoJavaScript

2023-01-03 12:30:25

架構(gòu)CPUGPU

2009-09-21 09:27:58

MonoTouch

2010-06-10 09:26:22

Java 7
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 中文字幕视频一区二区 | 九色网址 | 国产精品高 | 亚洲天天干| 热99精品视频 | 欧美性猛交一区二区三区精品 | 欧美综合在线观看 | 国产精品久久久久久久久久了 | 久久久久久久久久久蜜桃 | 欧美日韩视频一区二区 | 国产蜜臀97一区二区三区 | 91精品久久久久久久久久小网站 | 精品av | 国产乱一区二区三区视频 | 久久久无码精品亚洲日韩按摩 | 国产精品久久久久久久久久久久久 | 欧美日韩免费一区二区三区 | 免费三级av| 日本成人福利视频 | 国产在线1| 久久伊人亚洲 | 久久久久久免费免费 | 亚洲图片一区二区三区 | 亚洲欧美精品 | 国产精品亚洲一区 | 麻豆一区一区三区四区 | 中文字幕视频在线看 | 国产精品视频久久久久久 | 久久久久久免费毛片精品 | 日韩美av | 美女视频一区二区三区 | 99国产精品99久久久久久粉嫩 | 视频一区二区中文字幕日韩 | 999久久久久久久久6666 | 超碰在线播 | 国产成人一区二区三区精 | 免费黄色a级毛片 | 成人做爰9片免费看网站 | 97国产精品视频人人做人人爱 | 欧美成人h版在线观看 | 国产亚洲一区在线 |