《深入拆解Tomcat&Jetty》总结四:Jetty架构解析

6 Jetty架构


Jetty更加小巧,更易于定制化

6.1 整体架构


简单来说,Jetty Server由多个Connector(连接器)、多个Handler(处理器),以及一个线程池组成。

 Connector组件和Handler组件分别来实现HTTP服务器和Servlet容器的功能;
这两个组件工作时所需要的线程资源都直接从一个全局线程池ThreadPool中获取。

Jetty Server可以有多个Connector在不同的端口上监听客户请求,而对于请求处理的Handler组件,也可以根据具体场景使用不同的Handler。
这样的设计提高了Jetty的灵活性,需要支持Servlet,则可以使用ServletHandler;需要支持Session,则再增加一个SessionHandler,可以不使用Servlet或者Session,只要不配置这个Handler就行了。

为了启动和协调上面的核心组件工作,Jetty提供了一个Server类来做这个事情,它负责创建并初始化Connector、Handler、ThreadPool组件,然后调用start方法启动它们

对比Tomcat:

区别1
Jetty中没有Service的概念
Tomcat中的Service包装了多个连接器和一个容器组件,一个Tomcat实例可以配置多个Service,不同的Service通过不同的连接器监听不同的端口;
而Jetty中Connector被所有Handler共享,而不是Tomcat这样某一个容器对应某几个连接器。

区别2
在Tomcat中每个连接器都有自己的线程池,而在Jetty中所有的Connector共享一个全局的线程池。

6.2 Connector组件


跟Tomcat一样,Connector的主要功能是对I/O模型和应用层协议的封装。
I/O模型方面,最新的Jetty 9版本只支持NIO,因此Jetty的Connector设计有明显的Java NIO通信模型的痕迹。
至于应用层协议方面,跟Tomcat的Processor一样,Jetty抽象出了Connection组件来封装应用层协议的差异。

NIO编程中的服务端在I/O通信上主要完成了三件事情:监听连接、I/O事件查询以及数据读写。
因此Jetty设计了Acceptor、SelectorManager和Connection来分别做这三件事情,

6.2.1 Acceptor


Acceptor用于接受请求,也有独立的Acceptor线程组用于处理连接请求:

for (int i = 0; i < _acceptors.length; i++) {
    Acceptor a = new Acceptor(i);//Accepto是一个Runnable
    getExecutor().execute(a);//全局线程池
}

Acceptor也是通过阻塞的方式来接受连接:

public void accept(int acceptorID) throws IOException {
    ServerSocketChannel serverChannel = _acceptChannel;
    if (serverChannel != null && serverChannel.isOpen()){
        // 这⾥是阻塞的
        SocketChannel channel = serverChannel.accept();
        // 执⾏到这⾥时说明有请求进来了
        accepted(channel);
    }
}

接受连接成功后会调用accepted()函数,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);
}


6.2.2 SelectorManager


Jetty的Selector由SelectorManager类管理,而被管理的Selector叫作ManagedSelector。
SelectorManager内部有一个ManagedSelector数组,真正干活的是ManagedSelector
accepted方法交给Selector处理:

public void accept(SelectableChannel channel, Object attachment){
    //选择⼀个ManagedSelector来处理Channel
    final ManagedSelector selector = chooseSelector();
    //提交⼀个任务Accept给ManagedSelector
    selector.submit(selector.new Accept(channel, attachment));
}

处理这个任务主要做了两步:

//第一步,调用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);
}

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

6.2.3 Connection

这个Runnable是EndPoint的一个内部类,它会调用Connection的回调方法来处理请求。
Jetty的Connection组件类比就是Tomcat的Processor,负责具体协议的解析,得到Request对象,并调用Handler容器进行处理。

具体实现类:HttpConnection

请求处理:

HttpConnection并不会主动向EndPoint读取数据,而是向在EndPoint中注册一堆回调方法,数据到了就调这些回调方法_readCallback,有点异步I/O的感觉,也就是说Jetty在应用层面使用回调函数模拟了异步I/O模型。
而在回调方法_readCallback里,会调用EndPoint的接口去读数据,读完后让HTTP解析器去解析字节流,HTTP解析器会将解析后的数据,包括请求行、请求头相关信息存到Request对象里。

响应处理:

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

6.2.4 小结


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中的,即Endpoint通知Connection

6.回调函数内部实现,其实就是调用EndPoint的接口方法来读数据,即Connection收到通知后去EndPoint读数据

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

 

Jetty Server就是由多个Connector、多个Handler,以及一个线程池组成,在设计上简洁明了。

Jetty的Connector只支持NIO模型,跟Tomcat的NioEndpoint组件一样,它也是通过Java的NIO API实现的。

Java NIO编程有三个关键组件:Channel、Buffer和Selector,而核心是Selector。
为了方便使用,Jetty在原生Selector组件的基础上做了一些封装,实现了ManagedSelector组件。

在线程模型设计上Tomcat的NioEndpoint跟Jetty的Connector是相似的,都是用一个Acceptor数组监听连
接,用一个Selector数组侦测I/O事件,用一个线程池执行请求。
它们的不同点在于,Jetty使用了一个全局的线程池,所有的线程资源都是从线程池来分配。

Jetty Connector设计中的一大特点是,使用了回调函数来模拟异步I/O,比如Connection向EndPoint注册了
一堆回调函数。
它的本质将函数当作一个参数来传递,告诉对方,你准备好了就调这个回调函数。

对比NIO通信模型和例子:https://www.iteye.com/blog/uule-2429099

 

subReactor即Selector,轮询出事件后,交给worker去处理

6.3 Handler组件:Jetty的灵魂


Connector会将Request请求交给Handler去处理

Jetty通过Handler实现了高度可定制化,那么Handler是如何处理请求的呢?如何实现的呢?

6.3.1 Handler定义

Handler就是一个接口,它有一堆实现类,Jetty的Connector组件调用这些接口实现类来处理Servlet请求:

public interface Handler extends LifeCycle, Destroyable{
    //处理请求的⽅法,类似Tomcat容器组件的service方法
    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接口之下有抽象类AbstractHandler,有接口一般就有抽象实现类。

在AbstractHandler之下有AbstractHandlerContainer,为什么需要这个类呢?
这其实是个过渡,为了实现链式调用,一个Handler内部必然要有其他Handler的引用,所以这个类的名字里才有Container,意思就是这样的Handler里包含了其他Handler的引用。

它的两个子类:HandlerWrapper和HandlerCollection简单来说就是,HandlerWrapper和HandlerCollection都是Handler,但是这些Handler里还包括其他Handler的引用。
不同的是,HandlerWrapper只包含一个其他Handler的引用,而HandlerCollection中有一个Handler数组的引用:


6.3.2 类型

 本质上这些Handler分成三种类型:
第一种是协调Handler,这种Handler负责将请求路由到一组Handler中去,比如上图中的HandlerCollection,它内部持有一个Handler数组,当请求到来时,它负责将请求转发到数组中的某一个Handler。
第二种是过滤器Handler,这种Handler自己会处理请求,处理完了后再把请求转发到下一个Handler,比如图上的HandlerWrapper,它内部持有下一个Handler的引用。需要注意的是,所有继承了HandlerWrapper的Handler都具有了过滤器Handler的特征,比如ContextHandler、SessionHandler和WebAppContext等。
第三种是内容Handler,这些Handler会真正调用Servlet来处理请求,生成响应的内容,比如ServletHandler。如果浏览器请求的是一个静态资源,也有相应的ResourceHandler来处理这个请求,返回静态页面。

6.3.3 总架构:

WebAppContext对应一个Web应用,本身就是一个ContextHandler

WebAppContext会将这些Handler构建成一个执行链,通过这个链会最终调用到我们的业务Servlet。

 Jetty的Handler组件和Tomcat中的容器组件是大致是对等的概念

Jetty中的WebAppContext相当于Tomcat的Context组件,都是对应一个Web应用

Jetty中的ServletHandler对应Tomcat中的Wrapper组件,它负责初始化和调用Servlet,并实现了Filter的功能。
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值