Jetty之接受请求。

Jetty作为一个独立的Servlet引擎,可以独立提供Web服务,但是他也可以与其他Web应用服务器继承,所以呀可以基于两种协议工作,一种是HTTP,另一种是AJP。如果将Jetty集成到JBoss或者Apache,那么就可以让Jetty基于AJP模式工作。下面分别介绍Jetty是如何基于这两种协议工作得,以及他们是如何建立连接和接受请求的。

基于HTTP工作

如果在前端没有其他Web服务器,那么Jetty应该基于HTTP工作。也就是当Jetty接收到一个请求时,必须按照HTTP解析请求和封装返回的数据。那么Jetty是如何接收一个连接又是如何处理这个连接的呢?

我们设置Jetty的Connector实现类为org.eclipse.jetty.server.bi.SocketConnector,让Jetty以BIO的方式工作。Jetty在启动时将会创建BIO的工作环境,他会创建HttpConnection类来解析和封装HTTP1.1的协议,ConnectorEndPoint类是以BIO的处理方式处理连接请求的,ServerSocket用于建立Socket连接以接收和传送数据,Executor用于处理连接的线程池,他负责处理每一个请求队列的任务。acceptorThread监听连接请求,一有Socket连接,他便将进入下面的处理流程。

当Socket被真正执行时,HttpConnection将被调用,这里定义了如何将请求传递到Servlet容器里,以如何将请求最终路由到目的Servlet。

下图是Jetty启动创建连接的时序图。

Jetty创建连接的环境需要以下三个步骤。

  1.     创建一个队列线程池,用于处理每个建立连接的任务,这个线程池可以由用户来指定,这和Tomcat是类似的。
  2.     创建ServerSocket,用于准备接收客户端的Socket请求,以及客户端用来包装这个Socket的一些辅助类。
  3.     创建一个或多个监听线程,用来监听访问端口是否有连接进来。

相比Tomcat创建连接的环境,Jetty的逻辑更加简单,牵涉的类更少,执行的代码量页更少。

当建立连接的环境已经准备好时,就可以接收HTTP请求了,当Acceptor接收到Socket连接后将转入下图所示的流程执行。

Acceptor线程将会为这个请求创建ConnectorEndPoint,HttpConnection用来表示这个连接是一个HTTP的连接,他会创建HttpParse类解析HTTP,并且会创建符合HTTP的Request和Response对象。接下来就是将这个线程交给队列线程池去执行了。

基于AJP工作

通常一个Wb服务站点的后端服务器不是将Java的应用服务器直接暴露给服务访问者,而是在应用服务器(如Jboss)的前面再加一个Web服务器(如Apache或者Nginx),对原因大家应该很容易理解,如做日志分析、负载均衡、权限控制、防止恶意请求以及静态资源预加载等。

下图是通常的Web服务端的架构图。

在这种架构下Servlet引擎就不需要解析和封装返回的HTTP,因为HTTP的解析工作已经在Apache或Nginx服务器上完成了,JBoss只要基于更加简单的Ajp工作就行了,这样能加快请求的响应速度。

对比HTTP的时序图可以发现,他们的逻辑几乎是相同的,不同的是替换了一个类,即Ajp13Parserer替换了HttpParser,他定义了如何处理AJP及需要哪些类来配合。

实际上Ajp处理请求相比于HTTP唯一的不同就是在读取到Socket数据包时如何来转换这个数据包,按照HTTP的包格式来解析就是HttpParser,按照AJP来解析就是Ajp13Parserer。封装返回的数据也是如此。

让Jetty工作在AJP下,需要配置connector的实现类为Ajp13SocketConnector,这个l欸继承了SocketConnector对象而不是HttpConnection。下图所示的是Jetty创建连接环境的时序图。

与Http方式唯一不同的地方就是将SocketConnector类替换成了Ajp13SocketConnector类,改成Ajp13SocketConnector的目的就是可以创建Ajp13Connection类,表示当前这个连接使用的是AJP,所以需要用Ajp13Parser类来解析AJP,处理连接的逻辑都是一样的。AJPParser类解析AJP的时序图如下图所示。

基于NIO方式工作

前面所描述的从Jetty建立客户端连接到处理客户端连接都是基于BIO的方式,他也支持另外一种NIO的处理方式,其中Jetty的默认connector就是NIO方式。

通常NIO的工作原型如下:

创建一个Selector相当于一个观察者打开一个Server端通道,把这个Server通道注册到观察者上并且指定监听事件,然后遍历这个观察者观察到的事件,取出感兴趣的事件再处理。这里有个最核心的地方就是,我们不需要为每个被观察者为每个被观察者创建一个线程来监控他随时发生的事情,而是把这些被观察者都注册一个地方统一管理,再由他把触发的事件统一发送给感兴趣的程序模块。这里的核心是能够统一的管理每个被观察者的事件,所以我们就可以把服务端的每个建立的连接传送和接受数据作为一个事件统一管理,这样就不必每个连接需要一个线程来维护了。

这里需要注意的是,很多人认为监听SelectionKey.OP_ACCEPT事件就已经是非阻塞方式了,其实Jetty仍然用一个线程来监听客户端的连接请求,当接收到请求后,把这个请求再注册到Selector上,然后才以非阻塞方式执行。这里还有一个容易引起误解的地方,即认为Jetty以NIO方式工作,只会有一个线程来处理所有的请求,甚至认为不同的用户会在服务端共享一个线程从而导致基于ThreadLocal的程序出现问题。其实从Jetty的源码中能够发现,真正共享一个线程的处理只是在监听不同连接的数据传送事件上,如有多个连接已经建立,传送方式是当没有数据传输时,线程是阻塞的,也就是一直在等待下一个数据的到来,而NIO的处理方式是只有一个线程在等待所有连接的数据的到来,而当某个连接数据到来时,Jetty会把他分配给这个连接对应的处理线程去处理,所以不同连接的处理线程仍然是独立的。

Jetty的NIO处理方式和Tomcat的几乎一样,唯一不同的地方是如何把监听到的事件分配给对应的连接处理。从测试效果来看,Jetty的NIO处理方式更加高效。下图是Jetty的NIO处理方式的时序图。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值