一個http請求在play框架中的前世此生

最近研究了一下Play框架底層對於http請求的處理流程,總結以下。(注:Play版本的是1.2.4,其它版本的實現可能會有所不一樣)html

 


如上圖,一個http請求在play框架中,要被三個不一樣的模塊依次處理,而後獲得最終的response返回給客戶端。json

首先是底層的netty server,Play的底層是基於netty server的,netty採用NIO的多路複用策略,接收客戶端的http請求,並託管給Play框架處理。Play中負責處理netty傳入的請求的組件是Invoker,Invoker維護了一個線程池,每一個請求由線程池中的一個空閒線程處理。Invoker會經過router來肯定最終處理請求的用戶自定義的Controller中的Action方法,接着就是咱們熟悉的Action方法處理邏輯,一般會在這裏返回json、html等類型的響應數據,這些數據最終仍是會交給netty server寫到netty的NIO channel中,返回給客戶端。下面來經過代碼來詳細看一下這個過程。bootstrap

 

Play框架的啓動類是play.server.Server,在main方法中能夠看到Play啓動了一個netty server來接收請求:框架

       

ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(tcp

               Executors. newCachedThreadPool(), Executors.newCachedThreadPool())ide

       );this

       try {線程

           if (httpPort != -1) {netty

               bootstrap.setPipelineFactory(new HttpServerPipelineFactory());router

               bootstrap.bind(new InetSocketAddress(address, httpPort));

               bootstrap.setOption("child.tcpNoDelay", true);

               //省略的代碼

           }

       } catch (ChannelException e) {

           //異常處理代碼

       }

 

再看一下HttpServerPipelineFactory這個類,它構造一個ChannelPipeline做爲啓動的netty server的請求執行pipeline,server收到的每個請求都會被pipeline中定義的handler處理,該factory返回的pipeline實例的最後一個handler是PlayHandler,正是經過PlayHandler將netty請求託管給Play框架處理。

 

//PlayHandler代碼

 @Override

  public void messageReceived(final ChannelHandlerContext ctx, final MessageEvent messageEvent) throws Exception {

       //省略的代碼

      // Deleguate to Play framework

       Invoker. invoke(new NettyInvocation(request, response, ctx, nettyRequest, messageEvent));

  }

 

前面說到Invoker維護着一個線程池,invoke方法會把NettyInvocation(實現了Runnable接口)提交給線程池處理。NettyInvocation有這麼幾個關鍵的方法:init()、execute()、onSuccess()。

1.init()

inti方法之因此關鍵,是由於調用了Router路由控制類來決定請求接下來的走向。

 

try {

    Router. routeOnlyStatic(request);

    super.init();

} catch (NotFound nf) {

    // 404響應

    serve404(nf, ctx, request, nettyRequest);

    return false ;

} catch (RenderStatic rs) { 

    // 靜態資源響應

    serveStatic(rs, ctx, request, response, nettyRequest, this .event );

    return false ;

}

 

 若是Router判斷請求的uri是一個靜態資源,就在這裏返回響應了。若是Router判斷uri發現不能匹配任何一個Controller Action或者靜態資源,則會拋出NotFound異常,返回一個404響應。


2.execute()

對於Router能正確匹配上Controller Action的請求,會進入到execute方法。

        

@Override

public void execute () throws Exception {

    //省略的代碼

    ActionInvoker. invoke(request, response);

}

 

在這裏請求又被交給ActionInvoker這個組件處理,就像命名的含義同樣,ActionInvoker方法的實現根據Router中uri所匹配的Action方法名,經過反射的方式,調用咱們定義在Controller的public static void修飾符的action方法,執行業務邏輯。

3.onSuccess()

在execute方法中,若是action中的業務邏輯執行,沒有產生任何異常,則會進入到onSuccess方法。若是產生了異常,ActionInvoker會負責生成一個500響應返回給客戶端。

        

@Override

        public void onSuccess () throws Exception {

            super.onSuccess();

            if (response .chunked ) {

                closeChunked( request, response, ctx, nettyRequest);

            } else {

                copyResponse( ctx, request, response, nettyRequest);

            }

        }

 

copyResponse將會使用底層netty server的channel,將action方法產生的json、html等類型的響應數據返回給客戶端,到此一個完整的、主線的、無異常的請求處理流程完結。


上述提到的各組件的詳細交互時序圖以下:

相關文章
相關標籤/搜索