六 tomcat啟動源碼分析(三)--http請求nio處理

目標

分析tomcat處理一次http請求的nio過程模式,因為bio比較常見诅诱,網(wǎng)上好多分析資料可參考千贯。

分析

上一節(jié)屯仗,我們已經(jīng)分析了tomcat端啟動流程,最后Connector中啟動Endpoint進行端口監(jiān)聽搔谴,然后在mapperListener中存了相關各容器的映射關系魁袜,最后pipeline作為容器的調(diào)用管道鏈。按照這個流程敦第,我們分析調(diào)用源碼峰弹,進行debug,看下最終我的nio調(diào)用時序圖:


時序圖

簡單語言描述下:

  1. 請求進入NioEndPoint的內(nèi)部Acceptor接收請求芜果,根據(jù)nio的異步處理機制鞠呈,socket會作為一個pollerEvent事件存入隊列,poller會輪詢通過select進行選擇可讀事件
 protected class Acceptor implements Runnable {
        /**
         * The background thread that listens for incoming TCP/IP connections and
         * hands them off to an appropriate processor.
         */
        @Override
        public void run() {

            int errorDelay = 0;

            // Loop until we receive a shutdown command
            while (running) {
                   右钾。蚁吝。旱爆。。灭将。疼鸟。。庙曙。
                        Thread.sleep(1000);
                。浩淘。捌朴。。张抄。砂蔽。。
                        //每次用完一個新建
                        SocketChannel socket = null;
                        socket = serverSock.accept();
                   ..............
                    //此處去注冊任務了,然后關閉此socket
                        if (!setSocketOptions(socket)) {
                            try {
                                socket.socket().close();
                                socket.close();
                  署惯。左驾。。极谊。诡右。。轻猖。帆吻。。咙边。猜煮。。败许。王带。
        }//run
    }

對應的setSocketOptions方法

   protected boolean setSocketOptions(SocketChannel socket) {
            //獲取NioChannel,沒有空閑的則新建一個
            NioChannel channel = nioChannels.poll();
            市殷。愕撰。。被丧。盟戏。。甥桂。柿究。。黄选。           
          //注冊poller事件任務
            getPoller0().register(channel);
          蝇摸。婶肩。。貌夕。律歼。。啡专。
            return true;
    }
  1. selector找到待處理事件后開啟異步線程調(diào)用SocketProcessor進行處理险毁,SocketProcessor找到其對應的協(xié)議處理類封裝request,最后通過CoyoteAdapter進行適配處理
    看下Poller中監(jiān)聽run代碼
 public void run() {
            // Loop until we receive a shutdown command
            while (running) {
                try {
                    // Loop if endpoint is paused
                    while (paused && (!close) ) {
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            // Ignore
                        }
                    }
                    //判斷是否有待處理任務
                    boolean hasEvents = events();
                    //通過nio的selector選擇可讀事件们童,進行處理
                    Iterator<SelectionKey> iterator =
                        keyCount > 0 ? selector.selectedKeys().iterator() : null;
                    // Walk through the collection of ready keys and dispatch
                    // any active event.
                    while (iterator != null && iterator.hasNext()) {
                       
                            //處理此key
                            processKey(sk, attachment);
                   
        }

然后進入processKey處理數(shù)據(jù),此方法最終調(diào)用processSocket(channel, null, true)方法

    public boolean processSocket(NioChannel socket, SocketStatus status, boolean dispatch) {
        try {
            KeyAttachment attachment = (KeyAttachment)socket.getAttachment(false);
            attachment.setCometNotify(false); //will get reset upon next reg
            SocketProcessor sc = processorCache.poll();
            if ( sc == null ) sc = new SocketProcessor(socket,status);
            else sc.reset(socket,status);
            //獲取SocketProcessor處理器畔况,如果配置了getExecutor則在異步線程中進行處理,否則直接處理
            if ( dispatch && getExecutor()!=null ) getExecutor().execute(sc);
            else sc.run();
        } catch (RejectedExecutionException rx) {
            log.warn("Socket processing request was rejected for:"+socket,rx);
            return false;
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            // This means we got an OOM or similar creating a thread, or that
            // the pool and its queue are full
            log.error(sm.getString("endpoint.process.fail"), t);
            return false;
        }
        return true;
    }

在SocketProcessor的run方法中慧库,核心部分我們看到
state = (status==null)?handler.process(socket):handler.event(socket,status);
這個handler為Http11NioProtocol跷跪,根據(jù)我們配置的協(xié)議形成,看下其process方法,最終找到其對應方法SocketState state = processor.process(socket); ,此時processor為協(xié)議對應的處理器Http11NioProcessor,簡化此方法

 public SocketState process(NioChannel socket)
        throws IOException {
        RequestInfo rp = request.getRequestProcessor();
                齐板。吵瞻。。甘磨。橡羞。。宽档。尉姨。               
                  //處理request數(shù)據(jù),session吗冤,cookie等信息都在此時進行處理
                    prepareRequest();
                    //適配找到對應容器業(yè)務
                    adapter.service(request, response);
                又厉。。椎瘟。覆致。。肺蔚。
        }

    }
  1. CoyoteAdapter首先會解析request煌妈,最后調(diào)用pipeline調(diào)用鏈進入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);
        //處理request和response信息
          .............         
      //此處會調(diào)用pipeline逐級調(diào)用進入engine、host宣羊、context璧诵、wrapper        connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

              ...............
              //成功后,返回信息
                response.finishResponse();
                if (postParseSuccess) {
                    // Log only if processing was invoked.
                    // If postParseRequest() failed, it has already logged it.
                    ((Context) request.getMappingData().context).logAccess(
                            request, response,
                            System.currentTimeMillis() - req.getStartTime(),
                            false);
                }
                req.action(ActionCode.POST_REQUEST , null);
            }

     仇冯。之宿。。苛坚。比被。色难。。等缀。枷莉。
        }

    }
  1. 在pipeline層層傳遞下最后進入StandardWrapperValve找到最終的servlet,匹配path對應的filter尺迂,包裝filer鏈笤妙,調(diào)用filter的doFilter方法,最后進入servlet執(zhí)行業(yè)務
    public final void invoke(Request request, Response response)
        throws IOException, ServletException {

       噪裕。危喉。。州疾。。
        //找到對應的servlet
        StandardWrapper wrapper = (StandardWrapper) getContainer();
        Servlet servlet = null;
        Context context = (Context) wrapper.getParent();
        
        皇拣。严蓖。。氧急。颗胡。。吩坝。毒姨。。
        //形成過濾器調(diào)用鏈
        ApplicationFilterFactory factory =
            ApplicationFilterFactory.getInstance();
        ApplicationFilterChain filterChain =
            factory.createFilterChain(request, wrapper, servlet);
        
       钉寝。弧呐。。嵌纲。俘枫。。逮走。

    }

  1. servlet執(zhí)行完后鸠蚪,CoyoteAdapter會調(diào)用finishResponse方法關閉輸出流,返回客戶端师溅。

知識點:

  1. tomcat中nio的實現(xiàn)茅信,加深對nio概念理解

  2. 適配器模式進行接口適配

3.pipeline和filter等責任鏈模式的使用

參考資料:https://blog.csdn.net/sunyunjie361/article/details/60126398

目錄:?tomcat 源碼學習系列
上一篇: ? tomcat啟動源碼分析(二)--入口代碼calatina啟動介紹
下一篇:? tomcat啟動源碼分析(三)--http請求nio處理

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市墓臭,隨后出現(xiàn)的幾起案子蘸鲸,更是在濱河造成了極大的恐慌,老刑警劉巖起便,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棚贾,死亡現(xiàn)場離奇詭異窖维,居然都是意外死亡,警方通過查閱死者的電腦和手機妙痹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進店門铸史,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人怯伊,你說我怎么就攤上這事琳轿。” “怎么了耿芹?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵崭篡,是天一觀的道長。 經(jīng)常有香客問我吧秕,道長琉闪,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任砸彬,我火速辦了婚禮沪蓬,結果婚禮上扼褪,老公的妹妹穿的比我還像新娘嵌溢。我一直安慰自己特碳,他們只是感情好,可當我...
    茶點故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布增蹭。 她就那樣靜靜地躺著滴某,像睡著了一般。 火紅的嫁衣襯著肌膚如雪滋迈。 梳的紋絲不亂的頭發(fā)上霎奢,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天,我揣著相機與錄音杀怠,去河邊找鬼椰憋。 笑死,一個胖子當著我的面吹牛赔退,可吹牛的內(nèi)容都是我干的橙依。 我是一名探鬼主播,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼硕旗,長吁一口氣:“原來是場噩夢啊……” “哼窗骑!你這毒婦竟也來了?” 一聲冷哼從身側響起漆枚,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤创译,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后墙基,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體软族,經(jīng)...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡刷喜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了立砸。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片掖疮。...
    茶點故事閱讀 38,643評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖颗祝,靈堂內(nèi)的尸體忽然破棺而出浊闪,到底是詐尸還是另有隱情,我是刑警寧澤螺戳,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布搁宾,位于F島的核電站,受9級特大地震影響倔幼,放射性物質發(fā)生泄漏盖腿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一损同、第九天 我趴在偏房一處隱蔽的房頂上張望奸忽。 院中可真熱鬧,春花似錦揖庄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至富俄,卻和暖如春禁炒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背霍比。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工幕袱, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人悠瞬。 一個月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓们豌,卻偏偏與公主長得像,于是被迫代替她去往敵國和親浅妆。 傳聞我的和親對象是個殘疾皇子望迎,可洞房花燭夜當晚...
    茶點故事閱讀 43,509評論 2 348

推薦閱讀更多精彩內(nèi)容