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);
}
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在处理这个任务主要做了两步:
- 调用Selector#register把Channel注册到Selector上,拿到一个SelectionKey
_key = _channel.register(selector, SelectionKey.OP_ACCEPT, this);
- ,创建一个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才会去真正读数据和处理请求。
这个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的工作流
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组件去处理
======================================================================
一个接口,有一堆实现类,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有哪些子类呢?它们的继承关系又是怎样的?这些子类是如何实现Servlet容器功能的呢?
Jetty中定义了一些默认Handler类,并且这些Handler类之间的继承关系比较复杂,我们先通过一个全景图来了解一下
Handler接口之下有抽象类AbstractHandler,之下有AbstractHandlerContainer,为什么需要这个类呢?
过渡,为实现链式调用,一个Handler内部必然要有其他Handler的引用,所以这个类的名字里才有Container。
-
HandlerWrapper和HandlerCollection都是Handler,但这些Handler里还包括其他Handler的引用
-
HandlerWrapper只包含一个其他Handler的引用,而HandlerCollection中有一个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中去,比如HandlerCollection,它内部持有一个Handler数组,当请求到来时,它负责将请求转发到数组中的某一个Handler。
过滤器Handler
自己会处理请求,处理完了后再把请求转发到下一个Handler,比如HandlerWrapper,它内部持有下一个Handler的引用。
所有继承了HandlerWrapper的Handler都具有了过滤器Handler的特征,比如ContextHandler、SessionHandler和WebAppContext等。
内容Handler
这些Handler会真正调用Servlet来处理请求,生成响应内容,比如ServletHandler。
如果浏览器请求的是一个静态资源,也有相应的ResourceHandler来处理这个请求,返回静态页面。
上文提到,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。