<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          終于搞明白Tomcat的構(gòu)成組件,以及是如何運作的了!

          共 3067字,需瀏覽 7分鐘

           ·

          2022-05-11 09:02

          作者:VectorJin
          來源:juejin.cn/post/6844903473482317837


          Tomcat是什么

          開源的Java Web應(yīng)用服務(wù)器,實現(xiàn)Java EE(Java Platform Enterprise Edition)的部分技術(shù)規(guī)范,比如Java Servlet、Java Server Page、JSTL、Java WebSocket。Java EE是Sun公司為企業(yè)級應(yīng)用推出的標準平臺,定義了一系列用于企業(yè)級開發(fā)的技術(shù)規(guī)范,除了上述的之外,還有EJB、Java Mail、JPA、JTA、JMS 等,而這些都依賴具體容器的實現(xiàn)。

          Java EE應(yīng)用服務(wù)器對比

          上圖對比了Java EE容器的實現(xiàn)情況,Tomcat和Jetty都只提供了Java Web容器必需的Servlet和JSP規(guī)范,開發(fā)者要想實現(xiàn)其他的功能,需要自己依賴其他開源實現(xiàn)。

          Glassfish是由Sun公司推出,Java EE最新規(guī)范出來之后,首先會在Glassfish上進行實現(xiàn),所以是研究Java EE最新技術(shù)的首選。

          最常見的情況是使用Tomcat作為Java Web服務(wù)器,使用Spring提供的開箱即用的強大的功能,并依賴其他開源庫來完成負責的業(yè)務(wù)功能實現(xiàn)。

          Servlet容器

          Tomcat 組成如下圖主要Container和Connector以及相關(guān)組件構(gòu)成。

          Tomcat組成

          Server:指的就是整個Tomcat服務(wù)器,包含多組服務(wù),負責管理和啟動各個 Service,同時監(jiān)聽8005端口發(fā)過來的shutdown命令,用于關(guān)閉整個容器 ;

          Service:Tomcat封裝的、對外提供完整的、基于組件的 web 服務(wù), 包含 Connectors、Container 兩個核心組件,以及多個功能組件,各個 Service 之間是獨立的,但是共享同一 JVM 的資源 ;

          Connector:Tomcat與外部世界的連接器,監(jiān)聽固定端口接收外部請求,傳遞給Container,并將Container處理的結(jié)果返回給外部;

          Container:Catalina,Servlet容器,內(nèi)部多層容器組成,用于管理Servlet生命周期,調(diào)用Servlet相關(guān)方法。

          Loader:封裝了Java ClassLoader,用于Container加載類文件;

          Realm:Tomcat為web應(yīng)用程序提供訪問認證和角色管理的機制;

          JMX:Java SE 定義的技術(shù)規(guī)范,是一個為應(yīng)用程序、設(shè)備、系統(tǒng)等植入管理功能的框架,通過 JMX 可以遠程監(jiān)控 Tomcat 的運行狀態(tài);

          Jasper:Tomcat的Jsp解析引擎,用于將 Jsp 轉(zhuǎn)換成 Java 文件,并編譯成 class 文件。

          Session:負責管理和創(chuàng)建Session,以及Session 的持久化(可自定義),支持Session 的集群。

          Pipeline:在容器中充當管道的作用,管道中可以設(shè)置各種 valve(閥門),請求和響應(yīng)在經(jīng)由管道中各個閥門處理,提供了一種靈活可配置的處理請求和響應(yīng)的機制。

          Naming:命名服務(wù),JNDI, Java 命名和目錄接口,是一組在Java應(yīng)用中訪問命名和目錄服務(wù)的API。命名服務(wù)將名稱和對象聯(lián)系起來,使得我們可以用名稱訪問對象,目錄服務(wù)也是一種命名服務(wù),對象不但有名稱,還有屬性。Tomcat 中可以使用 JNDI 定義數(shù)據(jù)源、配置信息,用于開發(fā)與部署的分離。

          Container組成

          Container組成

          Engine:Servlet的頂層容器,包含一個或多個Host子容器;

          Host:虛擬主機,負責Web 應(yīng)用的部署和Context的創(chuàng)建;

          Context:Web應(yīng)用上下文,包含多個Wrapper,負責Web配置的解析、管理所有的Web資源;

          Wrapper:最底層的容器,是對 Servlet 的封裝,負責 Servlet 實例的創(chuàng)建、執(zhí)行和銷毀。

          生命周期管理?Tomcat 為了方便管理組件和容器的生命周期,定義了從創(chuàng)建、啟動到停止、銷毀共12種狀態(tài),Tomcat生命周期管理了內(nèi)部狀態(tài)變化的規(guī)則控制,組件和容器只需實現(xiàn)相應(yīng)的生命周期方法即可完成各生命周期內(nèi)的操作(initInternal、startInternal、stopInternal、 destroyInternal);

          比如,執(zhí)行初始化操作時,會判斷當前狀態(tài)是否New,如果不是則拋出生命周期異常;是的話,則設(shè)置當前狀態(tài)為Initializing,并執(zhí)行initInternal方法,由子類實現(xiàn),方法執(zhí)行成功則設(shè)置當前狀態(tài)為Initialized,執(zhí)行失敗則設(shè)置為Failed 狀態(tài);

          生命周期管理

          Tomcat的生命周期管理引入了事件機制,在組件或容器的生命周期狀態(tài)發(fā)生變化時會通知事件監(jiān)聽器,監(jiān)聽器通過判斷事件的類型來進行相應(yīng)的操作。事件監(jiān)聽器的添加可以在 server.xml 文件中進行配置;

          Tomcat各類容器的配置過程就是通過添加Listener的方式來進行的,從而達到配置邏輯與容器的解耦。如EngineConfig、HostConfig、ContextConfig。

          EngineConfig:主要打印啟動和停止日志;

          HostConfig:主要處理部署應(yīng)用,解析應(yīng)用META-INF/context.xml并創(chuàng)建應(yīng)用的 Context;

          ContextConfig:主要解析并合并web.xml,掃描應(yīng)用的各類 web 資源 (filter、servlet、listener);

          LifeCycle

          Tomcat 的啟動過程

          Tomcat的啟動過程

          啟動從Tomcat 提供的 start.sh 腳本開始,shell 腳本會調(diào)用Bootstrap 的 main 方法,實際調(diào)用了 Catalina 相應(yīng)的 load、start 方法。

          load 方法會通過 Digester 進行 config/server.xml 的解析,在解析的過程中會根據(jù) xml 中的關(guān)系和配置信息來創(chuàng)建容器,并設(shè)置相關(guān)的屬性。接著 Catalina 會調(diào)用StandardServer 的 init 和 start 方法進行容器的初始化和啟動。

          按照xml 的配置關(guān)系,server 的子元素是 service,service 的子元素是頂層容器 Engine,每層容器持有自己的子容器,而這些元素都實現(xiàn)了生命周期管理的各個方法,因此就很容易完成整個容器的啟動、關(guān)閉等生命周期的管理。

          StandardServer完成init和start方法調(diào)用后,會一直監(jiān)聽來自8005端口(可配置),如果接收到shutdown命令,則會退出循環(huán)監(jiān)聽,執(zhí)行后續(xù)的stop和destroy方法,完成Tomcat容器的關(guān)閉。

          同時也會調(diào)用 JVM 的Runtime.getRuntime()?.addShutdownHook方法,在虛擬機意外退出時來關(guān)閉容器。

          所有容器都是繼承自ContainerBase,基類中封裝了容器中的重復(fù)工作,負責啟動容器相關(guān)的組件Loader、Logger、Manager、Cluster、Pipeline,啟動子容器(線程池并發(fā)啟動子容器,通過線程池submit多個線程,調(diào)用后返回Future對象,線程內(nèi)部啟動子容器,接著調(diào)用Future對象的get方法來等待執(zhí)行結(jié)果)。

          List>?results?=?new?ArrayList>();
          for?(int?i?=?0;?i?????results.add(startStopExecutor.submit(new?StartChild(children[i])));
          }
          boolean?fail?=?false;
          for?(Future result :results)?{
          ????try?{
          ????????result.get();
          ????}?catch?(Exception?e)?{
          ????????log.error(sm.getString("containerBase.threadedStartFailed"),?e);
          ????????fail?=?true;
          ????}
          }
          復(fù)制代碼

          Web 應(yīng)用的部署方式?注:catalina.home:安裝目錄;catalina.base:工作目錄;默認值 user.dir

          • Server.xml配置Host元素,指定appBase屬性,默認$catalina.base/webapps/
          • Server.xml配置Context元素,指定docBase元素,指定web應(yīng)用的路徑
          • 自定義配置:在$catalina.base/EngineName/HostName/XXX.xml 配置 Context 元素

          HostConfig監(jiān)聽了StandardHost容器的事件,在start方法中解析上述配置文件:

          • 掃描 appbase 路徑下的所有文件夾和 war 包,解析各個應(yīng)用的META-INF/context.xml,并創(chuàng)建StandardContext,并將 Context 加入到 Host 的子容器中。
          • 解析$catalina.base/EngineName/HostName/下的所有 Context 配置,找到相應(yīng) web 應(yīng)用的位置,解析各個應(yīng)用的 META-INF/context.xml,并創(chuàng)建 StandardContext,并將 Context 加入到 Host 的子容器中。

          注:

          • HostConfig并沒有實際解析Context.xml,而是在ContextConfig中進行的。
          • HostConfig會定期檢查watched資源文件(context.xml 配置文件);

          ContextConfig 解析 context.xml 順序:

          • 先解析全局的配置 config/context.xml
          • 然后解析 Host 的默認配置 EngineName/HostName/context.xml.default
          • 最后解析應(yīng)用的 META-INF/context.xml

          ContextConfig 解析 web.xml 順序:

          • 先解析全局的配置 config/web.xml
          • 然后解析 Host 的默認配置 EngineName/HostName/web.xml.default 接著解析應(yīng)用的 MEB-INF/web.xml
          • 掃描應(yīng)用 WEB-INF/lib/下的 jar 文件,解析其中的 META-INF/web-fragment.xml 最后合并 xml 封裝成 WebXml,并設(shè)置 Context

          注:

          • 掃描 web 應(yīng)用和 jar 中的注解(Filter、Listener、Servlet)就是上述步驟中進行的。
          • 容器的定期執(zhí)行:backgroundProcess,由 ContainerBase 來實現(xiàn)的,并且只有在頂層容器中才會開啟線程。(backgroundProcessorDelay=10 標志位來控制)

          Servlet 生命周期

          Servlet生命周期

          Servlet是用Java編寫的服務(wù)器端程序。其主要功能在于交互式瀏覽和修改數(shù)據(jù),生成動態(tài)Web內(nèi)容。

          1. 請求到達Server端,Server根據(jù)url映射到相應(yīng)的Servlet;
          2. 判斷Servlet 實例是否存在,不存在則加載和實例化Servlet并調(diào)用init方法;
          3. Server 分別創(chuàng)建 Request 和 Response 對象,調(diào)用 Servlet 實例的 service 方法(service 方法內(nèi)部會根據(jù) http 請求方法類型調(diào)用相應(yīng)的doXXX方法);
          4. doXXX方法內(nèi)為業(yè)務(wù)邏輯實現(xiàn),從Request對象獲取請求參數(shù),處理完畢之后將結(jié)果通過response對象返回給調(diào)用方;
          5. 當Server不再需要Servlet時(一般當 Server 關(guān)閉時),Server調(diào)用Servlet 的 destroy() 方法。

          load on startup

          當值為 0 或者大于 0 時,表示容器在應(yīng)用啟動時就加載這個 servlet;;當是一個負數(shù)時或者沒有指定時,則指示容器在該servlet被選擇時才加載;;正數(shù)的值越小,啟動該 servlet 的優(yōu)先級越高;

          single thread model

          每次訪問Servlet,新建Servlet實體對象,但并不能保證線程安全,同時Tomcat 會限制Servlet 的實例數(shù)目。最佳實踐:不要使用該模型,Servlet 中不要有全局變量。

          請求處理過程

          請求處理過程
          1. 根據(jù) server.xml 配置的指定的connector以及端口監(jiān)聽http、或者ajp請求;
          2. 請求到來時建立連接,解析請求參數(shù),創(chuàng)建Request 和 Response 對象,調(diào)用頂層容器 pipeline 的 invoke 方法;
          3. 容器之間層層調(diào)用,最終調(diào)用業(yè)務(wù)Servlet 的 service 方法;
          4. Connector將response流中的數(shù)據(jù)寫到Socket中;

          Pipeline 與 Valve

          Pipeline與Value

          Pipeline 可以理解為現(xiàn)實中的管道,Valve 為管道中的閥門,Request 和 Response 對象在管道中經(jīng)過各個閥門的處理和控制。

          每個容器的管道中都有一個必不可少的basic valve,其他的都是可選的,basic valve 在管道中最后調(diào)用,同時負責調(diào)用子容器的第一個valve。

          Valve 中主要的三個方法:setNext、getNext、invoke;Valve之間的關(guān)系是單向鏈式結(jié)構(gòu),本身invoke 方法中會調(diào)用下一個Valve的 invoke 方法。

          各層容器對應(yīng)的 basic valve 分別是 StandardEngineValve、StandardHostValve、 StandardContextValve、StandardWrapperValve。

          JSP引擎

          JSP引擎

          JSP 生命周期

          • 編譯階段:Servlet容器編譯 servlet 源文件,生成 servlet 類;
          • 初始化階段:加載與JSP對應(yīng)的servlet 類,創(chuàng)建其實例,并調(diào)用它的初始化方法;
          • 執(zhí)行階段:調(diào)用與JSP對應(yīng)的servlet 實例的服務(wù)方法;
          • 銷毀階段:調(diào)用與JSP 對應(yīng)的servlet 實例的銷毀方法,然后銷毀 servlet 實例;

          JSP元素?

          代碼片段:<% 代碼片段 %>?

          JSP聲明:<%! declaration; [ declaration; ]+ ... %>?

          JSP表達式:<%= 表達式 %>?

          JSP注釋:<%-- 注釋 --%>?

          JSP指令:? ?<%@ directive attribute=“value” %>?

          JSP行為:? ??

          HTML元素:html/head/body/div/p/…?

          JSP隱式對象:request、response、out、session、application、config、 pageContext、page、Exception

          JSP 元素說明

          代碼片段:包含任意量的 Java 語句、變量、方法或表達式;?

          JSP 聲明:一個聲明語句可以聲明一個或多個變量、方法,供后面的 Java代碼使用;

          JSP 表達式:輸出 Java 表達式的值,String 形式;;

          JSP 注釋:為代碼作注釋以及將某段代碼注釋掉;

          JSP 指令:用來設(shè)置與整個 JSP 頁面相關(guān)的屬性, <%@ page ... %>定義頁面的依賴屬性,比如 language、contentType、errorPage、 isErrorPage、import、isThreadSafe、session 等等 <%@ include ... %>包含其他的 JSP 文件、HTML 文件或文本文件,是該 JSP 文件的一部分,會被同時編譯執(zhí)行 <%@ taglib ... %>引入標簽庫的定義,可以是自定義標簽;

          JSP行為:jsp:include、jsp:useBean、jsp:setProperty、jsp:getProperty、jsp:forward;

          JSP?解析過程

          JSP解析過程
          • 代碼片段:在_jspService()方法內(nèi)直接輸出;
          • JSP 聲明 在 servlet 類中進行輸出;
          • JSP 表達式在_jspService()方法內(nèi)直接輸出;
          • JSP 注釋直接忽略,不輸出;
          • JSP 指令根據(jù)不同指令進行區(qū)分,include:對引入的文件進行解析;page 相關(guān)的屬性會作為?JSP 的屬性,影響的是解析和請求處理時的行為;
          • JSP 行為不同的行為有不同的處理方式,jsp:useBean 為例,會從 pageContext 根據(jù) scope 的 類別獲取 bean 對象,如果沒有會創(chuàng)建 bean,同時存到相應(yīng) scope 的 pageContext 中。
          • HTML在_jspService()方法內(nèi)直接輸出
          • JSP 隱式對象在_jspService()方法會進行聲明,只能在方法中使用;

          Connector

          Connector

          HTTP:HTTP 是超文本傳輸協(xié)議,是客戶端瀏覽器或其他程序與Web服務(wù)器之間的應(yīng)用層通信協(xié)議。

          AJP:Apache JServ 協(xié)議(AJP)是一種二進制協(xié)議,專門代理從Web 服務(wù)器到位于后端的應(yīng)用程序服務(wù)器的入站請求。

          阻塞 IO

          阻塞IO

          非阻塞 IO

          非阻塞IO

          IO多路復(fù)用

          IO多路復(fù)用

          阻塞與非阻塞的區(qū)別在于進行讀操作和寫操作的系統(tǒng)調(diào)用時,如果此時內(nèi)核態(tài)沒有數(shù)據(jù)可讀或者沒有緩沖空間可寫時,是否阻塞。

          IO多路復(fù)用的好處在于可同時監(jiān)聽多個Socket的可讀和可寫事件,這樣就能使得應(yīng)用可以同時監(jiān)聽多個Socket,釋放了應(yīng)用線程資源。

          Tomcat各類Connector對比

          Tomcat各類Connector對比

          Connector的實現(xiàn)模式有三種,分別是BIO、NIO、APR,可以在server.xml中指定。

          • JIO:用java.io編寫的TCP模塊,阻塞IO;
          • NIO:用java.nio編寫的TCP模塊,非阻塞IO(IO多路復(fù)用);
          • APR:全稱Apache Portable Runtime,使用JNI的方式來進行讀取文件以及進行網(wǎng)絡(luò)傳輸;

          Apache Portable Runtime是一個高度可移植的庫,它是Apache HTTP Server 2.x的核心。APR具有許多用途,包括訪問高級IO功能(如sendfile,epoll和OpenSSL),操作系統(tǒng)級功能(隨機數(shù)生成,系統(tǒng)狀態(tài)等)和本地進程處理(共享內(nèi)存,NT管道和Unix套接字)。

          表格中字段含義說明:

          • Support Polling:是否支持基于IO多路復(fù)用的socket事件輪詢;
          • Polling Size:輪詢的最大連接數(shù);
          • Wait for next Request:在等待下一個請求時,處理線程是否釋放,BIO是沒有釋放的,所以在keep-alive=true的情況下處理的并發(fā)連接數(shù)有限;
          • Read Request Headers:由于request header數(shù)據(jù)較少,可以由容器提前解析完畢,不需要阻塞;
          • Read Request Body:讀取request body的數(shù)據(jù)是應(yīng)用業(yè)務(wù)邏輯的事情,同時Servlet的限制,是需要阻塞讀取的;
          • Write Response:跟讀取request body的邏輯類似,同樣需要阻塞寫;

          NIO處理相關(guān)類

          NIO處理相關(guān)類

          Acceptor線程負責接收連接,調(diào)用accept方法阻塞接收建立的連接,并對socket進行封裝成PollerEvent,指定注冊的事件為op_read,并放入到EventQueue隊列中,PollerEvent的run方法邏輯的是將Selector注冊到socket的指定事件;

          Poller線程從EventQueue獲取PollerEvent,并執(zhí)行PollerEvent的run方法,調(diào)用Selector的select方法,如果有可讀的Socket則創(chuàng)建Http11NioProcessor,放入到線程池中執(zhí)行;

          CoyoteAdapter是Connector到Container的適配器,Http11NioProcessor調(diào)用其提供的service方法,內(nèi)部創(chuàng)建Request和Response對象,并調(diào)用最頂層容器的Pipeline中的第一個Valve的invoke方法

          Mapper主要處理http url 到servlet的映射規(guī)則的解析,對外提供map方法。

          NIO Connector主要參數(shù)

          NIO?Connector主要參數(shù)

          Comet

          Comet是一種用于web的推送技術(shù),能使服務(wù)器實時地將更新的信息傳送到客戶端,而無須客戶端發(fā)出請求 在WebSocket出來之前,如果不使用comet,只能通過瀏覽器端輪詢Server來模擬實現(xiàn)服務(wù)器端推送。Comet支持servlet異步處理IO,當連接上數(shù)據(jù)可讀時觸發(fā)事件,并異步寫數(shù)據(jù)(阻塞)

          Comet

          Tomcat要實現(xiàn)Comet,只需繼承HttpServlet同時,實現(xiàn)CometProcessor接口

          • Begin:新的請求連接接入調(diào)用,可進行與Request和Response相關(guān)的對象初始化操作,并保存response對象,用于后續(xù)寫入數(shù)據(jù);
          • Read:請求連接有數(shù)據(jù)可讀時調(diào)用;
          • End:當數(shù)據(jù)可用時,如果讀取到文件結(jié)束或者response被關(guān)閉時則被調(diào)用;
          • Error:在連接上發(fā)生異常時調(diào)用,數(shù)據(jù)讀取異常、連接斷開、處理異常、socket超時;

          Note:

          • Read:在post請求有數(shù)據(jù),但在begin事件中沒有處理,則會調(diào)用read,如果read沒有讀取數(shù)據(jù),會觸發(fā)Error回調(diào),關(guān)閉socket;
          • End:當socket超時,并且response被關(guān)閉時也會調(diào)用;server被關(guān)閉時調(diào)用;
          • Error:除了socket超時不會關(guān)閉socket,其他都會關(guān)閉socket;
          • End和Error時間觸發(fā)時應(yīng)關(guān)閉當前comet會話,即調(diào)用CometEvent的close方法 Note:在事件觸發(fā)時要做好線程安全的操作;

          異步Servlet

          異步Servlet

          傳統(tǒng)流程:

          • 首先,Servlet 接收到請求之后,request數(shù)據(jù)解析;
          • 接著,調(diào)用業(yè)務(wù)接口的某些方法,以完成業(yè)務(wù)處理;
          • 最后,根據(jù)處理的結(jié)果提交響應(yīng),Servlet 線程結(jié)束。
          傳統(tǒng)流程

          異步處理流程:

          • 客戶端發(fā)送一個請求;
          • Servlet容器分配一個線程來處理容器中的一個servlet;
          • servlet調(diào)用request.startAsync(),保存AsyncContext,然后返回;
          • 任何方式存在的容器線程都將退出,但是response仍然保持開放;
          • 業(yè)務(wù)線程使用保存的AsyncContext來完成響應(yīng)(線程池);
          • 客戶端收到響應(yīng);

          Servlet 線程將請求轉(zhuǎn)交給一個異步線程來執(zhí)行業(yè)務(wù)處理,線程本身返回至容器,此時 Servlet 還沒有生成響應(yīng)數(shù)據(jù),異步線程處理完業(yè)務(wù)以后,可以直接生成響應(yīng)數(shù)據(jù)(異步線程擁有 ServletRequest 和 ServletResponse 對象的引用)。

          為什么web應(yīng)用中支持異步?

          推出異步,主要是針對那些比較耗時的請求:比如一次緩慢的數(shù)據(jù)庫查詢,一次外部REST API調(diào)用,,或者是其他一些I/O密集型操作。這種耗時的請求會很快的耗光Servlet容器的線程池,繼而影響可擴展性。

          Note:從客戶端的角度來看,Request仍然像任何其他的HTTP的request-response交互一樣,只是耗費了更長的時間而已。

          異步事件監(jiān)聽

          • onStartAsync:Request調(diào)用startAsync方法時觸發(fā);
          • onComplete:syncContext調(diào)用complete方法時觸發(fā);
          • onError:處理請求的過程出現(xiàn)異常時觸發(fā);
          • onTimeout:socket超時觸發(fā);

          Note : onError/ onTimeout觸發(fā)后,會緊接著回調(diào)onComplete onComplete 執(zhí)行后,就不可再操作request和response。


          往期推薦

          支付系統(tǒng)架構(gòu)設(shè)計詳解

          突發(fā)狀況,數(shù)據(jù)庫表被鎖,抓瞎了?

          使用HTTP Client踩到的一個坑,你一定要避免

          捕獲了一只發(fā)生概率小于萬分之一的Bug

          Java性能優(yōu)化的7個方向,不看你后悔!


          如果你覺得這篇文章不錯,那么,下篇通常會更好。備注“公眾號”添加微信好友(微信號:zhuan2quan)。

          ▲?按關(guān)注”程序新視界“,洞察技術(shù)內(nèi)幕
          瀏覽 41
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美色哟哟 | 国产主播在线播放 | 色婷婷在线播放 | 久久视频高清无码 | 中文字幕欧美高清 |