一个http请求在play框架中的前世今生

最近研究了一下Play框架底层对于http请求的处理流程,总结如下。(注:Play版本的是1.2.4,其它版本的实现可能会有所不同)

 一个http请求在play框架中的前世今生_第1张图片


如上图,一个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等类型的响应数据返回给客户端,到此一个完整的、主线的、无异常的请求处理流程完结。


上述提到的各组件的详细交互时序图如下:

一个http请求在play框架中的前世今生_第2张图片

你可能感兴趣的:(一个http请求在play框架中的前世今生)