最近研究了一下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等類型的響應數據返回給客戶端,到此一個完整的、主線的、無異常的請求處理流程完結。
上述提到的各組件的詳細交互時序圖以下: