Jetty架构设计之Connector、Handler组件

Acceptor通过阻塞方式接受连接。

public void accept(int acceptorID) throws IOException

{

ServerSocketChannel serverChannel = _acceptChannel;

if (serverChannel != null && serverChannel.isOpen())

{

// 这里是阻塞的

SocketChannel channel = serverChannel.accept();

// 执行到这里时说明有请求进来了

accepted(channel);

}

}

接受连接成功后会调用accepted,将SocketChannel设置为非阻塞模式,然后交给Selector去处理。

private void accepted(SocketChannel channel) throws IOException

{

channel.configureBlocking(false);

Socket socket = channel.socket();

configure(socket);

// _manager是SelectorManager实例,里面管理了所有的Selector实例

_manager.accept(channel);

}

SelectorManager


Jetty的Selector由SelectorManager类管理,而被管理的Selector叫作ManagedSelector。SelectorManager内部有一个ManagedSelector,真正的打工人。

每个ManagedSelector都有自己的Selector,多个Selector可以并行管理大量的channel,提高并发,连接请求到达时采用Round Robin的方式选择ManagedSelector。

public void accept(SelectableChannel channel, Object attachment)

{

//选择一个ManagedSelector来处理Channel

final ManagedSelector selector = chooseSelector();

//提交一个任务Accept给ManagedSelector

selector.submit(selector.new Accept(channel, attachment));

}

SelectorManager从本身的Selector数组中选择一个Selector来处理这个Channel,并创建一个任务Accept交给ManagedSelector,ManagedSelector在处理这个任务主要做了两步:

  1. 调用Selector#register把Channel注册到Selector上,拿到一个SelectionKey

_key = _channel.register(selector, SelectionKey.OP_ACCEPT, this);

  1. ,创建一个EndPoint和Connection,并跟这个SelectionKey(Channel)绑定

private void createEndPoint(SelectableChannel channel, SelectionKey selectionKey) throws IOException

{

//1. 创建EndPoint

EndPoint endPoint = _selectorManager.newEndPoint(channel, this, selectionKey);

//2. 创建Connection

Connection connection = _selectorManager.newConnection(channel, endPoint, selectionKey.attachment());

//3. 把EndPoint、Connection和SelectionKey绑在一起

endPoint.setConnection(connection);

selectionKey.attach(endPoint);

}

就好像到餐厅吃饭:

  • 先点菜(注册I/O事件)

  • 服务员(ManagedSelector)给你一个单子(SelectionKey)

  • 等菜做好了(I/O事件到了)

  • 服务员根据单子就知道是哪桌点了这个菜,于是喊一嗓子某某桌的菜做好了(调用了绑定在SelectionKey上的EndPoint的方法)

ManagedSelector并没有调用直接EndPoint的方法去处理数据,而是通过调用EndPoint的方法返回一个Runnable,然后把这个Runnable扔给线程池执行,这个Runnable才会去真正读数据和处理请求。

Connection


这个Runnable是EndPoint的一个内部类,它会调用Connection的回调方法来处理请求。Jetty的Connection组件类比就是Tomcat的Processor,负责具体协议的解析,得到Request对象,并调用Handler容器进行处理。下面我简单介绍一下它的具体实现类HttpConnection对请求和响应的处理过程。

请求处理:HttpConnection并不会主动向EndPoint读取数据,而是向在EndPoint中注册一堆回调方法:

getEndPoint().fillInterested(_readCallback);

告诉EndPoint,数据到了你就调我这些回调方法_readCallback吧,有点异步I/O的感觉,也就是说Jetty在应用层面模拟了异步I/O模型。

回调方法_readCallback里,会调用EndPoint的接口去读数据,读完后让HTTP解析器去解析字节流,HTTP解析器会将解析后的数据,包括请求行、请求头相关信息存到Request对象。

响应处理:Connection调用Handler进行业务处理,Handler会通过Response对象来操作响应流,向流里面写入数据,HttpConnection再通过EndPoint把数据写到Channel,这样一次响应就完成了。

  • Connector的工作流

Jetty架构设计之Connector、Handler组件

1.Acceptor监听连接请求,当有连接请求到达时就接受连接,一个连接对应一个Channel,Acceptor将Channel交给ManagedSelector来处理

2.ManagedSelector把Channel注册到Selector上,并创建一个EndPoint和Connection跟这个Channel绑定,接着就不断地检测I/O事件

3.I/O事件到了就调用EndPoint的方法拿到一个Runnable,并扔给线程池执行

4.线程池中调度某个线程执行Runnable

5.Runnable执行时,调用回调函数,这个回调函数是Connection注册到EndPoint中的

6.回调函数内部实现,其实就是调用EndPoint的接口方法来读数据

7.Connection解析读到的数据,生成请求对象并交给Handler组件去处理

Handler

======================================================================

一个接口,有一堆实现类,Jetty的Connector组件调用这些接口来处理Servlet请求,我们先来看看这个接口定义成什么样子。

public interface Handler extends LifeCycle, Destroyable

{

// 处理请求的方法

public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)

throws IOException, ServletException;

// 每个Handler都关联一个Server组件,被Server管理

public void setServer(Server server);

public Server getServer();

// 销毁方法相关的资源

public void destroy();

}

任何一个Handler都需要关联一个Server组件,也就是说Handler需要被Server组件来管理。一般来说Handler会加载一些资源到内存,因此通过设置destroy方法来销毁。

Handler继承关系


Handler只是一个接口,完成具体功能的还是它的子类。那么Handler有哪些子类呢?它们的继承关系又是怎样的?这些子类是如何实现Servlet容器功能的呢?

Jetty中定义了一些默认Handler类,并且这些Handler类之间的继承关系比较复杂,我们先通过一个全景图来了解一下

Jetty架构设计之Connector、Handler组件

Handler接口之下有抽象类AbstractHandler,之下有AbstractHandlerContainer,为什么需要这个类呢?

过渡,为实现链式调用,一个Handler内部必然要有其他Handler的引用,所以这个类的名字里才有Container。

  • HandlerWrapper和HandlerCollection都是Handler,但这些Handler里还包括其他Handler的引用

  • HandlerWrapper只包含一个其他Handler的引用,而HandlerCollection中有一个Handler数组的引用。

Jetty架构设计之Connector、Handler组件

HandlerWrapper有两个子类:

  • Server

Handler模块的入口,必然要将请求传递给其他Handler来处理,为了触发其他Handler的调用,所以它是一个HandlerWrapper。

  • ScopedHandler

实现了“具有上下文信息”的责任链调用。Servlet规范规定Servlet在执行过程中有上下文。这些Handler在执行过程中就是通过ScopedHandler来实现的获得上下文。

ScopedHandler有一堆的子类用来实现Servlet规范,比如ServletHandler、ContextHandler、SessionHandler、ServletContextHandler和WebAppContext

HandlerCollection

维护了一个Handler数组。因为Jetty可能需要同时支持多个Web应用,如果每个Web应用有一个Handler入口,那么多个Web应用的Handler就成了一个数组,比如Server中就有一个HandlerCollection,Server会根据用户请求的URL从数组中选取相应的Handler来处理,就是选择特定的Web应用来处理请求。

Handler的类型


协调Handler

负责将请求路由到一组Handler中去,比如HandlerCollection,它内部持有一个Handler数组,当请求到来时,它负责将请求转发到数组中的某一个Handler。

过滤器Handler

自己会处理请求,处理完了后再把请求转发到下一个Handler,比如HandlerWrapper,它内部持有下一个Handler的引用。

所有继承了HandlerWrapper的Handler都具有了过滤器Handler的特征,比如ContextHandler、SessionHandler和WebAppContext等。

内容Handler

这些Handler会真正调用Servlet来处理请求,生成响应内容,比如ServletHandler。

如果浏览器请求的是一个静态资源,也有相应的ResourceHandler来处理这个请求,返回静态页面。

实现Servlet规范


上文提到,ServletHandler、ContextHandler以及WebAppContext等,它们实现了Servlet规范,那具体是怎么实现的呢?为了帮助你理解,在这之前,我们还是来看看如何使用Jetty来启动一个Web应用。

//新建一个WebAppContext,WebAppContext是一个Handler

WebAppContext webapp = new WebAppContext();

webapp.setContextPath("/mywebapp");

webapp.setWar(“mywebapp.war”);

//将Handler添加到Server中去

server.setHandler(webapp);

//启动Server

server.start();

server.join();

上面的过程主要分为两步:

第一步创建一个WebAppContext,接着设置一些参数到这个Handler中,就是告诉WebAppContext你的WAR包放在哪,Web应用的访问路径是什么。

第二步就是把新创建的WebAppContext添加到Server中,然后启动Server。

上一篇:JMeter设置IP欺骗


下一篇:jmeter单接口参数化