当前位置: 首页 > 科技观察

深入剖析Tomcat原理

时间:2023-03-16 21:17:46 科技观察

早期的web容器早期的web应用主要用于浏览新闻等静态页面,HTTP服务器(如Apache、Nginx)返回静态HTML给浏览器,由浏览器负责用于解析HTML并将结果呈现给用户。随着互联网的发展,Servlet规范往往需要更多的动态交互。于是Sun推出了Servlet技术:servlet规范!最新的是Servlet4.0,支持HTTP2.0!符合Servlet规范的Web进程:Servlet由Servlet容器创建和管理,客户端的请求会被封装成ServletRequest和ServletResponse。它本质上是对通信协议的封装。Tomcat简介Tomcat是一个Servlet容器,它实现了对Servlet和JavaServerPage(JSP)的支持。同时,它还具有HTTP服务器的功能。因此,“Tomcat=HTTP服务器+Servlet容器”,一般我们称这个组件为:“轻量级Web容器”!Tomcat架构Tomcat-Server“Server”:一个Server就是一个Tomcat实例,下载Tomcat压缩包,执行/bin/startup.sh启动一个Tomcat实例。“Service”:对外服务的整体,包括多个Connector和一个Engine。同时一个Server可以配置多个Service。实际上,Service只是将组件组合在一起,本身并没有实现任何功能。Tomcat-ConnectorConnector启动ServerSocket,负责监听Socket请求,将数据转化为TomcatRequest,交给Engine处理。一个服务可以有多个连接器,这意味着它可以监听多个端口。Tomcat-container即Servlet容器,是Tomcat容器的顶层组件。它管理多个虚拟主机。一个Service只能有一个Engine,但是一个Engine可以配置多个Host。Tomcat的Servlet容器具有明显的分层架构。“Host”:虚拟主机,默认为localhost,即127.0.0.1。也可以配置不同的IP地址,访问不同的IP地址就可以访问不同的虚拟主机。一个Host可以部署多个Context。“Context”:应用,一般我们会把自己实现的Servlet应用打包成一个war,放在Tomcat的webapps目录下。Tomcat会将其解压缩并将其部署到一个Context组件中,该组件代表一个应用程序上下文。一个Context可以管理多个Wrapper。毕竟,一个Web应用程序必须有多个Servlet。“Wrapper”:这个组件不包含在Tomcat的配置文件中,因为它是在web.xml中配置的,它是一个Servlet。准确的说,Tomcat是将我们自己实现的Servlet用Wrapper包装起来的。一个请求最终会转到Wrapper去执行。Tomcat生命周期管理“Tomcat设计了很多组件,保证高内聚低耦合,保证可扩展性。”但是,大量的组件也会带来其他问题,比如组件管理、启动、关闭和销毁时涉及多个组件的操作。Tomcat设计了LifeCycle接口,定义了生命周期钩子函数:init()、start()、stop()和destroy(),所有组件都实现了这个接口并定义了自己的处理逻辑。而且,当上层组件触发自己的生命周期钩子函数时,也会触发其管理的下层组件的钩子函数。其实国外的设计框架都喜欢设计这个LifeCycle接口,新版的ApacheDubbo也加入了这个功能。Tomcat在实现组件生命周期管理时充分利用了“组合模式、观察者模式和模板模式”。“组合模式”:Tomcat通过组合模式使用上层组件来管理它的下层组件,每个组件都是这样管理的。这样暴露给用户的是只需要访问一个组件就可以达到完整系统调用的一致性效果。对于Tomcat的顶层组件Server,其init()方法:“观察者模式”:Tomcat考虑自身的可扩展性,为了避免版本升级,不得不修改生命周期钩子函数。它引入了监听器LifecycleListener和LifecycleState。它设计了一组贯穿组件生命周期全过程的状态集合,例如:组件刚创建时,处于NEW状态,调用init()方法后处于INITIALIZED状态...生命周期方法调用前后,组件的状态会发生变化State,状态的变化会被封装成一个事件LifecycleEvent,这些事件会被监听器处理。“模板模式”:主要体现代码实现,其实就是状态的改变,事件的创建和监听器的回调。其实这些操作并不需要在每个组件中都实现,这样会造成代码重复。Tomcat在实现该功能时,将这些通用逻辑抽象出来,定义为一个LifecycleBase抽象类,其中定义了骨架方法,如init()方法。Tomcat连接器Tomcat连接器用于监听Socket连接,将TCP底层字节流数据转换为Request和Response;连接器主要有3个组件:“EndPoint、Processor、Adapter”。“端点”负责向处理器提供字节流。“Processor”负责向Adapter提供TomcatRequest对象。“适配器”负责将ServletRequest对象转换为容器。其中,Tomcat将EndPoint和Processor结合起来形成了ProtocolHandler,实际上是一种组合设计模式的使用。Tomcat连接器-NioEndpoint《Tomcat使用NioEndPointjava-basednio包实现I/O多路复用模型》,主要包括LimitLatch、Acceptor、Poller、SocketProcessor和Executor5个组件。它的工作过程如下:“LimitLatch”:负责控制最大连接数。在NIO模式下,默认为10000。当达到这个阈值时,拒绝连接请求。它是基于AQS实现的,原理和Lock是一样的。“Acceptor”:一个独立的线程,不断调用ServerSocketChannel的accept()方法接收新的连接。一旦有新的连接请求到达,它返回一个SocketChannel对象,然后将其封装在一个PollerEvent对象中,并将PollerEvent对象推入Poller的Queue(“典型的生产者-消费者模式”)。“Poller”:在一个线程中独立运行,最底层是一个Selector。每个Poller线程可能同时被多个Acceptor线程调用,注册PollerEvent。Poller通过内部的Selector对象不断向内核查询Channel的状态,一旦可读,就生成一个任务类SocketProcessor交给Executor处理。“SocketProcessor”:实现了Runable接口,主要调用Http11Processor处理请求。Tomcat会将Socket包装成SocketWrapper,Http11Processor会调用SocketWrapper读写数据。“Executor”:一个自定义的线程池,负责运行SocketProcessor,会调用Http11Processor读取和解析请求数据。Http11Processor是对应用层协议的封装。它会调用容器获取响应,然后通过Channel写入响应。Tomcat连接器-Nio2EndpointTomcat也支持异步I/O,基于Java实现AIO-Nio2Endpoint的组件与NioEndpoint类似,只是Nio2Endpoint中没有Poller组件,即没有Selector。这是因为在异步I/O模式下,Selector的工作交给了内核。“LimitLatch”:和NioEndPoint一样,连接控制器负责控制最大连接数。“Nio2Acceptor”:ExtendedAcceptor,是处理连接的回调类。通过异步I/O的方式接收到新的连接后,会得到一个AsynchronousSocketChannel,封装成一个Nio2SocketWrapper,创建一个SocketProcessor任务类,供Thread池处理。“Nio2SocketWrapper”:实际读取Channel中的数据,为Http11Processor提供读写接口。但是由于异步I/O的特性,当Http11Processor读取Nio2SocketWrapper时,很有可能内核还没有准备好数据。为了解决这个问题,Http11Processor使用了两个read调用:通过注册回调类readCompletionHandler。最后,Tomcat的实现细节很多,没办法一一复现。在大多数情况下,你需要自己针对源代码运行它,像许多实际应用程序:ContainerBackgroundProcessor,它实现了热更新机制:热加载和热部署。对象池技术,典型的以空间换时间的思想,通过SynchronizedStack减少了SocketWrapper和SocketProcessor的创建和销毁。Tomcat支持Servlet3.0引入的异步Servlet。其思想是将业务线程与TomcatI/O线程分离,将复杂耗时的业务计算移至业务线程池中,释放Tomcat的威力。I/O线程,以便及时响应其他请求。会话管理...集群管理...