Tomcat源碼分析(2)-連接器Connector整體架構

来源:https://www.cnblogs.com/Brake/archive/2020/06/27/13195737.html
-Advertisement-
Play Games

在上一篇博文中,主要分析了tomcat的整體架構,通過上一篇的分析可以知道,tomcat主要有兩部分組成,分別為連接器(Connector)和容器(Container)。本文介紹連接器(Connector)。 一、Connector的主要功能 連接器主要用於對外交流,它負責接收外部的請求,然後把請求 ...


上一篇博文中,主要分析了tomcat的整體架構,通過上一篇的分析可以知道,tomcat主要有兩部分組成,分別為連接器(Connector)和容器(Container)。本文介紹連接器(Connector)。

一、Connector的主要功能

連接器主要用於對外交流,它負責接收外部的請求,然後把請求轉交給container進行處理。主要功能如下:

  1. 監聽網路請求、接受位元組流
  2. 根據應用層協議(HTTP or AJP)把接受到位元組流轉換成TomcatRequest
  3. 把TomcatReqeust轉換成ServletRequest
  4. 調用容器Servlet,得到ServletResponse
  5. 把ServletRespone轉換成TomcatResponse
  6. 把TomcatResponse轉化成位元組流,返回給瀏覽器

基於以上詳細分析可知Connector的主要功能可以抽象為三點

  • 網路監聽
  • 協議解析處理
  • 協議屏蔽轉換(tomcatRequest到servletReqeust轉換,servletResponse到tomcatResponse的轉換)

二、Connetor內部的組件

基於上述的分析,接下來具體看tomcat connector的代碼組件,主要有三個

  • Endpoint-用於網路監聽
  • Processor-用於協議解析處理
  • Adapter-用於轉換,解耦connector和container

tomcat的類設計中增加了一個ProtocolHandler, 把Endpoint和Processor,Adapter封裝到了一起。先看一個整體組件圖。

 三、Endpoint介紹

 Endpoint是通信節點,實現了TCP/IP協議,包含兩個核心組件:

  1. Acceptor,主要用於監聽socket鏈接請求,
  2. SocketProcessor,用於處理接收到的 Socket 請求,實現了runnable介面,在run方法中會調用processor對socket請求進行處理。

Endpoint核心介面

public abstract class AbstractEndpoint<S> {

    protected Acceptor[] acceptors;

    protected abstract SocketProcessorBase<S> createSocketProcessor(
            SocketWrapperBase<S> socketWrapper, SocketEvent event);

    protected SynchronizedStack<SocketProcessorBase<S>> processorCache;
    /**
     * External Executor based thread pool.
     */
    private Executor executor = null;
}

這裡面還有一個Executor, 這個是tomcat自己擴展的線程池。Acceptor監聽到socket請求後,創建SocketProcessor,由Executor來運行SocketProcessor。

Acceptor核心代碼:

    protected class Acceptor extends AbstractEndpoint.Acceptor {
        @Override
        public void run() {
            while (running) {
                state = AcceptorState.RUNNING;
                try {
                    //Accept the next incoming connection from the server
                    SocketChannel socket = serverSock.accept();//監聽請求
                    //setSocketOptions() will hand the socket off to an appropriate processor if successful
                    setSocketOptions(socket);//把請求傳給SocketProcessor
                } catch (Throwable t) {

                }
            }
        }
    }

setSocketOption最終會調用Endpoint的process方法。

Endpoint的process核心方法代碼如下:

    public boolean processSocket(SocketWrapperBase<S> socketWrapper,
                                 SocketEvent event, boolean dispatch) {
        SocketProcessorBase<S> sc = processorCache.pop();
        if (sc == null) {
            sc = createSocketProcessor(socketWrapper, event);//創建SocketProcessor
        } else {
            sc.reset(socketWrapper, event);
        }
        Executor executor = getExecutor();
        executor.execute(sc);//交給線程池進行處理
        return true;
    }

SocketProcessor的抽象類

public abstract class SocketProcessorBase<S> implements Runnable {

    protected SocketWrapperBase<S> socketWrapper;
    protected SocketEvent event;

    @Override
    public final void run() {
        synchronized (socketWrapper) {
            if (socketWrapper.isClosed()) {
                return;
            }
            doRun();
        }
    }

    protected abstract void doRun();
}

SocketProcessor類

  protected class SocketProcessor extends SocketProcessorBase<NioChannel> {

        public SocketProcessor(SocketWrapperBase<NioChannel> socketWrapper, SocketEvent event) {
            super(socketWrapper, event);
        }

        @Override
        protected void doRun() {
            NioChannel socket = socketWrapper.getSocket();
            SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());
            getHandler().process(socketWrapper, SocketEvent.CONNECT_FAIL);
        }
    }

上面類中getHandler().process的具體實現。(Handler的介面由Endpoint的內部類進行定義。

    protected static class ConnectionHandler<S> implements AbstractEndpoint.Handler<S> {
        private final Map<S, Processor> connections = new ConcurrentHashMap<>();

        @Override
        public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {

            S socket = wrapper.getSocket();

            Processor processor = connections.get(socket);
            return processor.process(wrapper, status);
        }
    }

至此、請求已經成功傳給可processor。

三、processor、adapter

processor是應用層協議比如HTTP的處理。他負責把請求傳給adapter。核心代碼如下。

    @Override
    public SocketState service(SocketWrapperBase<?> socketWrapper)
            throws IOException {

        try {
            getAdapter().service(request, response);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

adapter解耦了connector和container的關係,主要負責把tomcatRequest轉換為servletRequest,然後最終調用container,核心代碼如下。

    public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
            throws Exception {
        Request request = (Request) req.getNote(ADAPTER_NOTES);
        Response response = (Response) res.getNote(ADAPTER_NOTES);
        if (request == null) {
            // Create objects
            request = connector.createRequest();
            request.setCoyoteRequest(req);
            response = connector.createResponse();
            response.setCoyoteResponse(res);
        }
        // Calling the container
        connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
    }

至此請求到達了container,我們的servlet會對進行業務邏輯處理。

四、細化流程

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • tomcat伺服器源碼解讀,整體結構梳理,開源server,java servlet容器 ...
  • 跟大多數編程語言一樣,python中的迴圈有兩種: while迴圈和for迴圈 首先,介紹一下while迴圈,結合案例做一些練習。 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++while迴圈語法結構:while ...
  • 今天博主再給大家分享一個小項目:MiNi圖書管理系統。用的是Java語言開發的,代碼不多,大概260行左右吧,系統是實現圖書的新增圖書、刪除圖書、借閱圖書、歸還圖書、查看圖書等簡單的功能(後附源代碼)! 首先展示一下運行界面效果圖:運行代碼後,會在控制台顯示如下界面: 然後讓用戶選擇,如果用戶不小心 ...
  • import pandas a=pandas.read_excel() def abc(x): return ','.join(x.values) b=a.groupby(['列名'1])['列名2'].apply(abc) c=b.reset_index() print(c) ...
  • WebSocket 非同步風格伺服器 WebSocket\Server 繼承自 Http\Server,所以 Http\Server 提供的所有 API 和配置項都可以使用。 # ws_server.php class WebSocket { public $server; public functi ...
  • 一.環境要求 安裝java 1.8 以上 命令行運行 java -version 返回版本大於1.8 如果沒有,請安裝java 1.8 二.下載與安裝 下載apktool_x.x.x.jar到本地 官網下載或者 鏡像下載 重命名下載的apktool_x.x.x.jar,改名為apktool.jar ...
  • MongoSpark為入口類,調用MongoSpark.load,該方法返回一個MongoRDD類對象,Mongo Spark Connector框架本質上就是一個大號的自定義RDD,加了些自定義配置、適配幾種分區器規則、Sql的數據封裝等等,個人認為相對核心的也就是分區器的規則實現;弄清楚了其分析 ...
  • 前言 本文的文字及圖片來源於網路,僅供學習、交流使用,不具有任何商業用途,版權歸原作者所有,如有問題請及時聯繫我們以作處理。 最近一直在關註百度明星吧,發現很多有趣的帖子,於是我就想用python把這些帖子都爬下來,並對內容進行分析。 本文的知識點: 介紹了mysql資料庫內容插入及提取的簡單應用; ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...