最近研究了一下Play框架底层对于http请求的处理流程,总结如下。(注:Play版本的是1.2.4,其它版本的实现可能会有所不同)
如上图,一个http请求在play框架中,要被三个不同的模块依次处理,然后得到最终的response返回给客户端。
首先是底层的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中,返回给客户端。下面来通过代码来详细看一下这个过程。
Play框架的启动类是play.server.Server,在main方法中可以看到Play启动了一个netty server来接收请求:
ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(
Executors. newCachedThreadPool(), Executors.newCachedThreadPool())
);
try {
if (httpPort != -1) {
bootstrap.setPipelineFactory(new HttpServerPipelineFactory());
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等类型的响应数据返回给客户端,到此一个完整的、主线的、无异常的请求处理流程完结。
上述提到的各组件的详细交互时序图如下: