精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

【Tomcat源碼分析】從零開始理解 HTTP 請求處理

開發 前端
在 Endpoint.start()? 方法中,我們首先會調用 bind() 方法,完成 Socket 的綁定,確保 Connector 能夠監聽來自網絡的請求。接著,我們會創建工作者線程池,為后續處理請求提供充足的線程資源。隨后,我們會初始化連接 latch,用于限制請求的并發量,避免過多的請求涌入,造成系統崩潰。

前言

終于步入 Connector 的解析階段,這無疑是 Tomcat 架構中最為復雜的一環。作為連接器,它的職責顯而易見——連接。那么,它連接的究竟是什么呢?

Connector 宛如一座橋梁,將來自客戶端的請求,經過精心封裝成 Request 和 Response 對象,傳遞給 Container 進行處理。Container 完成業務邏輯后,Connector 再將處理后的結果,通過 Response 對象返回給遠方的客戶端。

要深入理解 Connector 的精髓,需要我們從四個關鍵問題出發,逐一探索。

  1. Connector 如何接收來自遠方的請求?
  2. 如何將這呼喚化作 Request 和 Response 的身影?
  3. 封裝后的 Request 和 Response 如何被遞交給 Container 處理?
  4. Container 處理完畢后,如何將結果托付給 Connector,并最終送回客戶端手中?

為了更好地理解 Connector 的內部運作,讓我們先來欣賞一幅 Connector 結構圖,它將幫助我們更直觀地感受其內部的精妙設計。

圖片圖片

【注意】:不同的協議和通信方式,將催生出不同的 ProtocolHandler 實現。在 Tomcat 8.5 版本中,ProtocolHandler 的類繼承關系圖譜如下:

圖片圖片

針對這幅類繼承層級圖,我們可以做如下解讀:

ajp 和 http11 代表著兩種截然不同的協議,而 nio、nio2 和 apr 則分別代表著三種不同的通信方式。值得注意的是,協議與通信方式并非相互獨立,它們可以靈活組合,以適應不同的場景需求。

ProtocolHandler 內部,包含著三個核心部件:Endpoint、Processor 和 Adapter,它們共同協作,完成請求的接收、處理和響應。

  • Endpoint 負責處理底層的 Socket 網絡連接,它就像是一位網絡守衛,負責迎接來自網絡的呼喚,并將其轉化為可供處理的 Socket 連接。Processor 則肩負著將 Endpoint 接收到的 Socket 封裝成 Request 對象的重任,它就像一位翻譯官,將網絡語言轉化為服務器可以理解的語言。Adapter 則充當著連接器,它將 Request 對象傳遞給 Container,以便 Container 進行具體的處理。
  • 由于 Endpoint 負責處理底層的 Socket 網絡連接,因此它需要實現 TCP/IP 協議,而 Processor 則需要實現 HTTP 協議,以解析 HTTP 請求。Adapter 則將請求適配到 Servlet 容器,使其能夠理解并處理來自外部的請求。
  • Endpoint 的抽象實現類 AbstractEndpoint 定義了 Acceptor、AsyncTimeout 兩個內部類和一個 Handler 接口。Acceptor 負責監聽來自網絡的請求,一旦有新的請求到來,便會將其捕獲。AsyncTimeout 則負責檢查異步 Request 的超時,確保請求在合理的時間內得到處理。Handler 則負責處理接收到的 Socket,它將調用 Processor 進行處理,將 Socket 轉換為 Request 對象,并最終傳遞給 Container。

至此,我們已經解開了 Connector 如何接收請求、如何將請求封裝成 Request 和 Response,以及封裝后的 Request 和 Response 如何被傳遞給 Container 進行處理這三個關鍵問題。而對于最后一個問題,即 Container 處理完后如何將結果返回給客戶端,我們將在深入了解 Container 的運作機制后自然明了,前面章節已對此進行了詳細的分析。

Connector 源碼分析入口

在 Service 的標準實現 StandardService 的源碼中,我們發現其 init()、start()、stop() 和 destroy() 方法分別會對 Connectors 的同名方法進行調用。值得注意的是,一個 Service 通常會對應多個 Connector,這意味著 Service 的生命周期管理會影響到所有與其關聯的 Connector。

Service.initInternal()

@Override
protected void initInternal() throws LifecycleException {
    super.initInternal();

    if (engine != null) {
        engine.init();
    }

    // Initialize any Executors
    for (Executor executor : findExecutors()) {
        if (executor instanceof JmxEnabled) {
            ((JmxEnabled) executor).setDomain(getDomain());
        }
        executor.init();
    }

    // Initialize mapper listener
    mapperListener.init();

    // Initialize our defined Connectors
    synchronized (connectorsLock) {
        for (Connector connector : connectors) {
            try {
                connector.init();
            } catch (Exception e) {
                String message = sm.getString(
                        "standardService.connector.initFailed", connector);
                log.error(message, e);

                if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
                    throw new LifecycleException(message);
            }
        }
    }
}

Service.startInternal()

@Override
protected void startInternal() throws LifecycleException {
    if(log.isInfoEnabled())
        log.info(sm.getString("standardService.start.name", this.name));
    setState(LifecycleState.STARTING);

    // Start our defined Container first
    if (engine != null) {
        synchronized (engine) {
            engine.start();
        }
    }

    synchronized (executors) {
        for (Executor executor: executors) {
            executor.start();
        }
    }

    mapperListener.start();

    // Start our defined Connectors second
    synchronized (connectorsLock) {
        for (Connector connector: connectors) {
            try {
                // If it has already failed, don't try and start it
                if (connector.getState() != LifecycleState.FAILED) {
                    connector.start();
                }
            } catch (Exception e) {
                log.error(sm.getString(
                        "standardService.connector.startFailed",
                        connector), e);
            }
        }
    }
}

正如我們所知,Connector 實現了 Lifecycle 接口,這使得它成為一個擁有生命周期的組件。因此,Connector 的啟動邏輯入口自然而然地落在 init() 和 start() 方法之中。

Connector 構造方法

在深入分析 Connector 的啟動邏輯之前,不妨先來觀摩一下 server.xml 文件。這份文件如同 Tomcat 架構的藍圖,清晰地展現了各個組件之間的聯系和布局,為我們理解 Connector 的運作提供了一個宏觀的視角。

<?xml versinotallow='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />

  <GlobalNamingResources>
    <Resource name="UserDatabase" auth="Container"
              type="org.apache.catalina.UserDatabase"
              description="User database that can be updated and saved"
              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
              pathname="conf/tomcat-users.xml" />
  </GlobalNamingResources>

  <Service name="Catalina">
    <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

    <Engine name="Catalina" defaultHost="localhost">
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>

      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
               prefix="localhost_access_log" suffix=".txt"
               pattern="%h %l %u %t "%r" %s %b" />
      </Host>
    </Engine>
  </Service>
</Server>

在 server.xml 文件中,我們發現 Connector 擁有多個關鍵屬性,其中 port 和 protocol 尤為重要。默認情況下,server.xml 支持兩種協議:HTTP/1.1 和 AJP/1.3。HTTP/1.1 用于支持傳統的 HTTP 1.1 協議,而 AJP/1.3 則專門用于支持與 Apache 服務器的通信,為 Apache 服務器提供一個與 Tomcat 交互的橋梁。

現在,讓我們將目光轉向 Connector 的構造方法:

public Connector() {
    this(null); // 1. 無參構造方法,傳入參數為空協議,會默認使用`HTTP/1.1`
}

public Connector(String protocol) {
    setProtocol(protocol);
    // Instantiate protocol handler
    // 5. 使用protocolHandler的類名構造ProtocolHandler的實例
    ProtocolHandler p = null;
    try {
        Class<?> clazz = Class.forName(protocolHandlerClassName);
        p = (ProtocolHandler) clazz.getConstructor().newInstance();
    } catch (Exception e) {
        log.error(sm.getString(
                "coyoteConnector.protocolHandlerInstantiationFailed"), e);
    } finally {
        this.protocolHandler = p;
    }

    if (Globals.STRICT_SERVLET_COMPLIANCE) {
        uriCharset = StandardCharsets.ISO_8859_1;
    } else {
        uriCharset = StandardCharsets.UTF_8;
    }
}

@Deprecated
public void setProtocol(String protocol) {
    boolean aprConnector = AprLifecycleListener.isAprAvailable() &&
            AprLifecycleListener.getUseAprConnector();

    // 2. `HTTP/1.1`或`null`,protocolHandler使用`org.apache.coyote.http11.Http11NioProtocol`,不考慮apr
    if ("HTTP/1.1".equals(protocol) || protocol == null) {
        if (aprConnector) {
            setProtocolHandlerClassName("org.apache.coyote.http11.Http11AprProtocol");
        } else {
            setProtocolHandlerClassName("org.apache.coyote.http11.Http11NioProtocol");
        }
    }
    // 3. `AJP/1.3`,protocolHandler使用`org.apache.coyote.ajp.AjpNioProtocol`,不考慮apr
    else if ("AJP/1.3".equals(protocol)) {
        if (aprConnector) {
            setProtocolHandlerClassName("org.apache.coyote.ajp.AjpAprProtocol");
        } else {
            setProtocolHandlerClassName("org.apache.coyote.ajp.AjpNioProtocol");
        }
    }
    // 4. 其他情況,使用傳入的protocol作為protocolHandler的類名
    else {
        setProtocolHandlerClassName(protocol);
    }
}

在 Connector 的構造方法中,我們發現它主要完成了以下幾項工作:

  • 當傳入的參數為空協議時,它會默認使用 HTTP/1.1 協議。
  • 當傳入的協議為 HTTP/1.1 或 null 時,它會選擇 org.apache.coyote.http11.Http11NioProtocol 作為 ProtocolHandler,并忽略 apr 選項。
  • 當傳入的協議為 AJP/1.3 時,它會選擇 org.apache.coyote.ajp.AjpNioProtocol 作為 ProtocolHandler,同樣忽略 apr 選項。
  • 對于其他情況,它會直接使用傳入的 protocol 作為 ProtocolHandler 的類名。
  • 最后,它會使用 ProtocolHandler 的類名來構造 ProtocolHandler 的實例。

Connector.initInternal()

@Override
protected void initInternal() throws LifecycleException {
    super.initInternal();

    // Initialize adapter
    // 1. 初始化adapter
    adapter = new CoyoteAdapter(this);
    protocolHandler.setAdapter(adapter);

    // Make sure parseBodyMethodsSet has a default
    // 2. 設置接受body的method列表,默認為POST
    if (null == parseBodyMethodsSet) {
        setParseBodyMethods(getParseBodyMethods());
    }

    if (protocolHandler.isAprRequired() && !AprLifecycleListener.isAprAvailable()) {
        throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoApr",
                getProtocolHandlerClassName()));
    }
    if (AprLifecycleListener.isAprAvailable() && AprLifecycleListener.getUseOpenSSL() &&
            protocolHandler instanceof AbstractHttp11JsseProtocol) {
        AbstractHttp11JsseProtocol<?> jsseProtocolHandler =
                (AbstractHttp11JsseProtocol<?>) protocolHandler;
        if (jsseProtocolHandler.isSSLEnabled() &&
                jsseProtocolHandler.getSslImplementationName() == null) {
            // OpenSSL is compatible with the JSSE configuration, so use it if APR is available
            jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());
        }
    }

    // 3. 初始化protocolHandler
    try {
        protocolHandler.init();
    } catch (Exception e) {
        throw new LifecycleException(
                sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
    }
}

Connector 的 init() 方法主要完成了三項重要的初始化工作:

  • 初始化 adapter:Adapter 負責將請求傳遞給 Container,因此需要在 init() 方法中完成初始化,以便后續能夠正常地將請求傳遞給 Container 進行處理。
  • 設置接受 body 的 method 列表:默認情況下,Connector 只允許 POST 方法提交 body 數據,但在某些情況下,可能需要允許其他方法提交 body 數據,因此需要在 init() 方法中設置允許提交 body 的方法列表。
  • 初始化 protocolHandler:ProtocolHandler 是 Connector 的核心組件,負責處理請求和響應,因此需要在 init() 方法中完成 protocolHandler 的初始化,以便后續能夠正常地處理請求和響應。

從 ProtocolHandler 的類繼承層級關系圖 中,我們可以看到 ProtocolHandler 的子類都必須實現 AbstractProtocol 抽象類。而 protocolHandler.init(); 方法的具體實現則取決于具體的 ProtocolHandler 子類,它會根據不同的協議和通信方式進行相應的初始化操作。

代碼正是在這個抽象類里面。我們來分析一下。

@Override
public void init() throws Exception {
    if (getLog().isInfoEnabled()) {
        getLog().info(sm.getString("abstractProtocolHandler.init", getName()));
    }

    if (oname == null) {
        // Component not pre-registered so register it
        oname = createObjectName();
        if (oname != null) {
            Registry.getRegistry(null, null).registerComponent(this, oname, null);
        }
    }

    if (this.domain != null) {
        rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
        Registry.getRegistry(null, null).registerComponent(
                getHandler().getGlobal(), rgOname, null);
    }

    // 1. 設置endpoint的名字,默認為:http-nio-{port}
    String endpointName = getName();
    endpoint.setName(endpointName.substring(1, endpointName.length()-1));
    endpoint.setDomain(domain);

    // 2. 初始化endpoint
    endpoint.init();
}

接下來,讓我們一同探究 Endpoint.init() 方法的內部。它位于 AbstractEndpoint 抽象類中,采用模板方法模式,巧妙地將核心邏輯委托給子類的 bind() 方法。

public abstract void bind() throws Exception;
public abstract void unbind() throws Exception;
public abstract void startInternal() throws Exception;
public abstract void stopInternal() throws Exception;

public void init() throws Exception {
    // 執行bind()方法
    if (bindOnInit) {
        bind();
        bindState = BindState.BOUND_ON_INIT;
    }
    if (this.domain != null) {
        // Register endpoint (as ThreadPool - historical name)
        oname = new ObjectName(domain + ":type=ThreadPool,name=\"" + getName() + "\"");
        Registry.getRegistry(null, null).registerComponent(this, oname, null);

        ObjectName socketPropertiesOname = new ObjectName(domain +
                ":type=ThreadPool,name=\"" + getName() + "\",subType=SocketProperties");
        socketProperties.setObjectName(socketPropertiesOname);
        Registry.getRegistry(null, null).registerComponent(socketProperties, socketPropertiesOname, null);

        for (SSLHostConfig sslHostConfig : findSslHostConfigs()) {
            registerJmx(sslHostConfig);
        }
    }
}

繼續追尋著代碼的蹤跡,我們終于來到了 bind() 方法,它揭示了 Connector 初始化的精髓所在。關鍵的代碼片段 serverSock.socket().bind(addr, getAcceptCount());用于 將 ServerSocket 綁定到指定的 IP 地址和端口

@Override
public void bind() throws Exception {

    if (!getUseInheritedChannel()) {
        serverSock = ServerSocketChannel.open();
        socketProperties.setProperties(serverSock.socket());
        InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
        //綁定ServerSocket到指定的IP和端口
        serverSock.socket().bind(addr,getAcceptCount());
    } else {
        // Retrieve the channel provided by the OS
        Channel ic = System.inheritedChannel();
        if (ic instanceof ServerSocketChannel) {
            serverSock = (ServerSocketChannel) ic;
        }
        if (serverSock == null) {
            throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));
        }
    }

    serverSock.configureBlocking(true); //mimic APR behavior

    // Initialize thread count defaults for acceptor, poller
    if (acceptorThreadCount == 0) {
        // FIXME: Doesn't seem to work that well with multiple accept threads
        acceptorThreadCount = 1;
    }
    if (pollerThreadCount <= 0) {
        //minimum one poller thread
        pollerThreadCount = 1;
    }
    setStopLatch(new CountDownLatch(pollerThreadCount));

    // Initialize SSL if needed
    initialiseSsl();

    selectorPool.open();
}

至此,我們已將 Connector 的 init() 方法剖析完畢,接下來,讓我們將目光轉向 start() 方法。start() 方法的核心邏輯,僅僅是簡潔的一行代碼:調用 ProtocolHandler.start() 方法,將 Connector 的啟動大任委托給 ProtocolHandler。

Connector.startInternal()

@Override
protected void startInternal() throws LifecycleException {

    // Validate settings before starting
    if (getPort() < 0) {
        throw new LifecycleException(sm.getString(
                "coyoteConnector.invalidPort", Integer.valueOf(getPort())));
    }

    setState(LifecycleState.STARTING);

    try {
        protocolHandler.start();
    } catch (Exception e) {
        throw new LifecycleException(
                sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
    }
}

現在,讓我們深入 ProtocolHandler.start() 方法,探索啟動過程中的關鍵步驟。它首先會調用 Endpoint.start() 方法,啟動 Endpoint,以便監聽來自網絡的請求。接著,它會開啟異步超時線程,負責監控異步請求的超時情況。該線程的執行單元為 AsyncTimeout。

@Override
public void start() throws Exception {
    if (getLog().isInfoEnabled()) {
        getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
    }

    // 1. 調用`Endpoint.start()`方法
    endpoint.start();

    // Start async timeout thread
    // 2. 開啟異步超時線程,線程執行單元為`Asynctimeout`
    asyncTimeout = new AsyncTimeout();
    Thread timeoutThread = new Thread(asyncTimeout, getNameInternal() + "-AsyncTimeout");
    int priority = endpoint.getThreadPriority();
    if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) {
        priority = Thread.NORM_PRIORITY;
    }
    timeoutThread.setPriority(priority);
    timeoutThread.setDaemon(true);
    timeoutThread.start();
}

現在,我們將注意力集中在 Endpoint.start() 方法,它負責啟動 Endpoint,為 Connector 迎接來自網絡的請求做好準備。

public final void start() throws Exception {
    // 1. `bind()`已經在`init()`中分析過了
    if (bindState == BindState.UNBOUND) {
        bind();
        bindState = BindState.BOUND_ON_START;
    }
    startInternal();
}

@Override
public void startInternal() throws Exception {
    if (!running) {
        running = true;
        paused = false;

        processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                socketProperties.getProcessorCache());
        eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                        socketProperties.getEventCache());
        nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                socketProperties.getBufferPool());

        // Create worker collection
        // 2. 創建工作者線程池
        if ( getExecutor() == null ) {
            createExecutor();
        }

        // 3. 初始化連接latch,用于限制請求的并發量
        initializeConnectionLatch();

        // Start poller threads
        // 4. 開啟poller線程。poller用于對接受者線程生產的消息(或事件)進行處理,poller最終調用的是Handler的代碼
        pollers = new Poller[getPollerThreadCount()];
        for (int i=0; i<pollers.length; i++) {
            pollers[i] = new Poller();
            Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i);
            pollerThread.setPriority(threadPriority);
            pollerThread.setDaemon(true);
            pollerThread.start();
        }
        // 5. 開啟acceptor線程
        startAcceptorThreads();
    }
}

protected final void startAcceptorThreads() {
    int count = getAcceptorThreadCount();
    acceptors = new Acceptor[count];

    for (int i = 0; i < count; i++) {
        acceptors[i] = createAcceptor();
        String threadName = getName() + "-Acceptor-" + i;
        acceptors[i].setThreadName(threadName);
        Thread t = new Thread(acceptors[i], threadName);
        t.setPriority(getAcceptorThreadPriority());
        t.setDaemon(getDaemon());
        t.start();
    }
}

在 Endpoint.start() 方法中,我們首先會調用 bind() 方法,完成 Socket 的綁定,確保 Connector 能夠監聽來自網絡的請求。接著,我們會創建工作者線程池,為后續處理請求提供充足的線程資源。隨后,我們會初始化連接 latch,用于限制請求的并發量,避免過多的請求涌入,造成系統崩潰。

接下來,我們會創建一個輪詢 Poller 線程,負責處理來自 Acceptor 線程的事件,并將處理后的事件傳遞給 Handler。Poller 線程會調用 Handler 的代碼進行處理,最終完成對請求的處理。最后,我們會創建一個 Acceptor 線程,專門負責監聽網絡請求,并將接收到的請求傳遞給 Poller 線程進行處理。

至此,我們已將 Connector 源碼入口的分析告一段落,揭開了 Connector 啟動過程的神秘面紗。接下來我們將繼續深入探索 Connector 的請求邏輯,深入理解 Connector 如何接收請求,如何將請求封裝成 Request 和 Response 對象,以及如何將這些對象傳遞給 Container 進行處理。讓我們一起探索 Tomcat 的內部世界。


責任編輯:武曉燕 來源: 碼上遇見你
相關推薦

2024-10-05 00:00:06

HTTP請求處理容器

2019-01-18 12:39:45

云計算PaaS公有云

2018-09-14 17:16:22

云計算軟件計算機網絡

2024-11-27 16:25:54

JVMJIT編譯機制

2024-11-18 17:31:27

2015-11-17 16:11:07

Code Review

2018-04-18 07:01:59

Docker容器虛擬機

2024-12-06 17:02:26

2020-07-02 15:32:23

Kubernetes容器架構

2021-10-29 08:07:30

Java timeout Java 基礎

2023-11-14 16:14:49

2010-05-26 17:35:08

配置Xcode SVN

2024-05-15 14:29:45

2023-11-09 23:45:01

Pytorch目標檢測

2015-10-15 14:16:24

2024-04-10 07:48:41

搜索引擎場景

2011-04-06 15:55:50

開發webOS程序webOS

2024-11-28 10:35:47

2024-11-18 16:37:35

JMMJava內存模型

2024-03-01 19:53:37

PyBuilderPython開發
點贊
收藏

51CTO技術棧公眾號

久久美女艺术照精彩视频福利播放| 久久久久久久久国产一区| 日韩欧美综合在线视频| 日韩精品伦理第一区| 中文字幕有码无码人妻av蜜桃| 天天综合一区| 精品视频在线播放免| 国内国产精品天干天干| 国内高清免费在线视频| 久久久精品日韩欧美| 成人免费观看网址| 久久一区二区三区视频| 国产精品久久占久久| 亚洲国产精彩中文乱码av| 天美星空大象mv在线观看视频| 影音先锋男人资源在线| 国产亲近乱来精品视频 | 欧美aa视频| 亚洲色图欧美在线| 欧美日韩综合另类| 国产1区在线观看| 久久精品国产秦先生| 欧美亚洲国产日韩2020| 99鲁鲁精品一区二区三区| 亚洲另类春色校园小说| 欧美成人bangbros| 久久成年人网站| 欧美va在线观看| 黄色一区二区在线观看| 蜜桃视频一区二区在线观看| 日本在线www| 国产欧美精品一区| 蜜桃网站成人| 天堂av一区二区三区| 国产激情一区二区三区桃花岛亚洲| 国产精品第8页| 国产成人免费看| 亚洲三级网站| 久久久亚洲精选| 欧美被狂躁喷白浆精品| 香蕉视频官网在线观看日本一区二区| 一区二区在线视频| 久久久久亚洲av无码专区桃色| 999久久精品| 欧美一级二级三级蜜桃| 久久精品一卡二卡| 国产精一区二区| 日韩一区二区视频| 久久久久久无码精品人妻一区二区| 日韩一级视频| 欧美电影一区二区三区| а 天堂 在线| 日韩精品一级| 日韩欧美一区二区视频| 无码人妻少妇色欲av一区二区| 成人精品视频在线观看| 制服丝袜激情欧洲亚洲| 日韩在线不卡一区| 国产精品视频一区二区三区综合| 这里只有精品99re| 极品人妻一区二区| 国产精品超碰| 日韩久久免费视频| brazzers精品成人一区| 视频一区在线观看| 自拍亚洲一区欧美另类| 午夜国产福利视频| 欧美在线高清| 久久久免费观看视频| 伊人久久综合视频| 亚洲永久视频| 国产有码一区二区| 精品国产av一区二区三区| 成人午夜免费电影| 日本不卡在线播放| 黄色网在线看| 午夜视黄欧洲亚洲| 青青青在线视频免费观看| 日韩第二十一页| 欧美va在线播放| 免费黄色在线视频| 久久久久免费av| 久久久久亚洲精品| 无码人妻精品一区二区三区不卡 | 夜夜嗨av一区二区三区网页 | 国产在线播放一区三区四| 5g国产欧美日韩视频| 五月天婷婷视频| 国产色一区二区| 男人日女人的bb| 中国色在线日|韩| 欧美精品久久一区二区三区| 国产精品嫩草69影院| 伊人久久大香线蕉无限次| 日韩中文在线观看| 全部毛片永久免费看| 精品一区二区三区久久| 精品久久中出| 2024最新电影免费在线观看| 欧美性猛交xxxx乱大交蜜桃| 亚洲高清在线不卡| 亚洲性视频大全| 九九热视频这里只有精品| 欧美a视频在线观看| 国产精品一二三在| 日韩精品一区二区三区四区五区 | 日韩av中文| 香蕉乱码成人久久天堂爱免费| 美女少妇一区二区| 欧美电影完整版在线观看| x99av成人免费| www毛片com| 成人午夜电影小说| 亚洲一区二区三区午夜| 一区二区电影免费观看| 日韩美女主播在线视频一区二区三区| 亚洲а∨天堂久久精品2021| 亚洲高清激情| 亚洲xxxx在线| 欧美精品电影| 欧美在线短视频| 亚洲黄色免费在线观看| 欧美日韩一卡| 亚洲最大av在线| 免费黄色在线| 欧美视频自拍偷拍| 小早川怜子久久精品中文字幕| 亚洲三级色网| 激情视频一区二区| 不卡av免费观看| 日韩一区二区麻豆国产| 成人精品一二三区| 免费观看日韩av| 日产精品高清视频免费| 松下纱荣子在线观看| 亚洲成人久久久| 国产无遮挡又黄又爽在线观看| 国产精品99久久久久久久女警| 亚洲欧美综合一区| 欧美大片免费高清观看| 国产丝袜一区视频在线观看| 国产情侣在线视频| www.亚洲国产| 337p粉嫩大胆噜噜噜鲁| 欧美亚洲大陆| 青青草一区二区| 日本护士...精品国| 欧美午夜精品久久久久久久| 播金莲一级淫片aaaaaaa| 99在线|亚洲一区二区| 精品无码久久久久国产| 亚洲十八**毛片| 亚洲色图13p| 在线免费一区二区| 国产精品国产a级| 亚洲精品国产久| 影音先锋中文字幕一区| 狼狼综合久久久久综合网| 另类图片综合电影| 视频在线观看99| 国产丝袜视频在线观看| 亚洲在线观看免费视频| 欧美大喷水吹潮合集在线观看| 亚洲国产高清视频| 欧美lavv| 亚洲综合伊人| 久久久综合av| 国产精品秘入口| 欧美电影影音先锋| 天天操天天射天天爽| 久久色.com| wwwwwxxxx日本| 欧美视频网站| 久久亚洲综合网| 日韩护士脚交太爽了| 欧美激情视频一区二区| 四虎在线观看| 欧美日韩激情在线| jizz国产免费| 中日韩av电影| 久久久久久婷婷| 男人的j进女人的j一区| 91亚洲精品国产| 在线看成人短视频| 亚洲影视九九影院在线观看| 久热在线观看视频| 日日骚久久av| 天天操天天射天天| 欧美日韩精品免费观看视频 | 在线视频一区二区免费| 亚洲成人生活片| 91网站最新网址| 男人午夜视频在线观看| 一区二区国产在线观看| 在线看视频不卡| 色婷婷久久久| 91福利入口| 成人日韩在线观看| 国内精品久久影院| 欧美69xxxx| 亚洲美女av在线| 国产黄色片免费观看| 欧洲一区二区三区在线| 国产小视频在线观看免费| 中文字幕第一页久久| www国产视频| 国产一区在线看| 在线免费观看av的网站| 99热免费精品在线观看| 九一免费在线观看| 日韩亚洲一区在线| 久久综合入口| 精品成人自拍视频| 91成人伦理在线电影| 国产欧美在线观看免费| 日产精品99久久久久久| 96av在线| 欧美激情乱人伦| av在线免费播放| 色琪琪综合男人的天堂aⅴ视频| 男女av在线| 亚洲福利视频在线| 亚洲AV无码精品自拍| 欧美久久一二区| 亚洲一级视频在线观看| 91久久人澡人人添人人爽欧美 | 国产视频一区三区| 久久这里只有精品8| 欧美激情aⅴ一区二区三区| 亚洲一区二区在线看| 精品国产一区二区三区| 麻豆蜜桃91| 美女一区2区| 久草一区二区| 色爱综合av| 久久国产精品高清| 欧美一级二级三级视频| 国产视频一区二区不卡| 国产suv精品一区| 99九九视频| 澳门成人av| 国产另类自拍| 久久久久观看| 免费不卡亚洲欧美| 国产99亚洲| 日韩av一区二区三区在线观看| 沈樵精品国产成av片| 日韩久久不卡| 999精品视频| 国产对白在线播放| 国产高清久久| 久久久久久久久久伊人| 国内精品福利| 国产二区视频在线播放| 亚洲永久在线| 色综合色综合色综合色综合| 美女诱惑一区二区| 亚洲综合在线一区二区| 国产精品99久久不卡二区| 美女搡bbb又爽又猛又黄www| www..com久久爱| 亚洲自拍偷拍一区二区 | 亚洲v日韩v欧美v综合| 成人毛片在线| 国产内射老熟女aaaa| 在线亚洲激情| 日韩一区二区三区不卡视频| 激情偷乱视频一区二区三区| 日韩av成人网| 久久网站热最新地址| 日本午夜精品视频| 一区二区视频在线看| 亚洲一区欧美在线| 欧美午夜在线观看| 国产激情久久久久久熟女老人av| 亚洲精品久久久久久久久久久| 国模吧精品人体gogo| 久久精品国产综合| 高潮在线视频| 国产精品自产拍在线观| 午夜精品在线| 日韩欧美视频一区二区三区四区| 香蕉视频国产精品 | 精品无码av在线| 91久久久免费一区二区| www.五月婷婷| 亚洲图片在区色| 色呦呦在线观看视频| 欧美在线播放视频| 国产午夜久久av| 免费国产在线精品一区二区三区| 香蕉久久网站| 国产男女激情视频| 成人一区二区视频| 国产91在线播放九色| 天天亚洲美女在线视频| 一级片视频网站| 亚洲美女www午夜| 三级网站视频在在线播放| 国产精品成人一区二区| 成人爽a毛片| 在线看视频不卡| 久久中文精品| 中文字幕乱码一区| 亚洲精品美腿丝袜| 一区二区三区在线免费观看视频 | 午夜视频在线看| 97超级碰碰碰久久久| 麻豆视频久久| 亚洲成人18| 麻豆久久婷婷| 亚洲中文字幕无码一区| ...xxx性欧美| 小泽玛利亚一区二区三区视频| 精品国产成人在线影院| 老司机免费在线视频| 国产精品1234| 免费黄色成人| 日韩亚洲欧美视频| 国产成人日日夜夜| 人人澡人人澡人人看| 欧美午夜一区二区| 日本一区高清| 欧美亚洲另类激情另类| 成人动态视频| 少妇久久久久久被弄到高潮| 国产一区二区三区国产| 亚洲熟女少妇一区二区| 色一情一伦一子一伦一区| 午夜视频在线免费播放| 国精产品一区一区三区有限在线| 久久久久毛片免费观看| 黄色www在线观看| 精一区二区三区| 自拍偷拍第9页| 欧美日韩国产一级二级| 日本美女在线中文版| 国产欧美日韩高清| 日韩片欧美片| a在线观看免费视频| 国产欧美一二三区| 中文字幕乱码无码人妻系列蜜桃| 亚洲天堂av电影| 日韩制服一区| 一本久道久久综合| 精品一区二区三区不卡| 三级黄色录像视频| 91精品国产高清一区二区三区蜜臀| 日p在线观看| 亚洲自拍小视频免费观看| 欧美日韩 国产精品| 最新国产精品自拍| 香蕉av福利精品导航| 水中色av综合| 日韩免费精品视频| 成人同人动漫免费观看 | 成人噜噜噜噜| 国产美女永久无遮挡| 99re成人在线| 色老头在线视频| 色狠狠久久aa北条麻妃| 成人在线精品| 亚洲国产精品无码av| 2021国产精品久久精品| www.五月婷婷.com| 久久视频在线播放| 少妇精品在线| 国产亚洲欧美在线视频| 日本一区二区在线不卡| 97人妻精品一区二区三区动漫| 久久资源免费视频| 欧美巨大xxxx| 三上悠亚av一区二区三区| 亚洲精品一二三区| 性xxxx视频| 国产日韩av在线播放| 国色天香一区二区| 亚洲av综合一区二区| 欧美日本韩国一区二区三区视频 | 色天天综合色天天久久| 欧美极品另类| 久久99九九| 国产综合色精品一区二区三区| 日韩av片在线播放| 日韩在线视频观看正片免费网站| 伊人久久影院| 91网址在线播放| 亚洲一区自拍偷拍| 国产大片在线免费观看| 国产精品一区免费观看| 免费高清在线一区| 男人的天堂一区二区| 久久精品视频中文字幕| 群体交乱之放荡娇妻一区二区| 国产传媒免费观看| 欧美午夜片欧美片在线观看| www国产在线观看| 日韩欧美第二区在线观看| 成人免费看的视频| 一级黄在线观看|