整体架构与核心组件
1.错误设计与正确设计
请求不应该直接到达业务类,而是由Servlet容器作为中间层再分发处理,因为请求直接打到业务类上耦合太高了
2.核心架构
Tomcat整体架构核心就只有两层,一个是负责处理Socket的Connector,另一部分是处理业务的Servlet容器(Container)
3.整体架构
Tomcat核心虽然只有连接器与容器这两部分,但是把具体的部分展开内容还是很多的,咱们来看一下整体的大图,看一下整体架构,里面涉及到的组件有个大概印象就行,后面会详细说
4.核心组件层级
介绍
Tomcat核心组件是由层级关系的,谁是上层,谁是下层,以下主要通过图示与配置文件来解释层级关系
图示
配置文件 server.xml 结构示例
<?xml version='1.0' encoding='utf-8'?>
<Server>
<Listener/>
<GlobalNamingResources>
<Resource/>
</GlobalNamingResources>
<Service>
<Executor/>
<Connector/>
<Engine>
<Cluster/>
<Realm/>
<Host>
<Context/>
</Host>
</Engine>
</Service>
</Server>
5.核心组件简述
顶层组件
- Server:代表整个Tomcat,一个Server可以包含一个或者多个Service
- Service:一个Service组合了多个Connector和一个Container,负责管理这俩
网络层
- Connector:处理请求的,也就是网络层的实现,用于实现Tomcat中定义的Coyote连接器,HTTP协议也是在连接器这一层实现的
- ProtocolHandler:Coyote协议接口,通过Endpoint和Processor,实现针对具体协议的处理能力。Tomcat按照协议和IO提供了6个实现类:AjpNioProtocol,AjpAprProtocol,AjpNio2Protocol , Http11NioProtocol ,Http11Nio2Protocol ,Http11AprProtocol
- Endpoint:通信端点,处理底层socket网络连接,实现TCP/IP协议
- Processor:用于将Endpoint接收到的socket处理封装成request对象,实现HTTP协议
- Acceptor:循环监听和限制客户端请求,客户端请求超过最大连接数,就阻塞,否则将socket交给Poller处理,属于守护线程
- Poller:工作线程,tomcat处理客户端请求的工作模式类似于netty(Reactor模型,IO多路复用),将Acceptor扔过来的客户端socketChannel进行事件注册,注册后的事件称作PollerEvent,保存到队列中,poller内部维护一个selector,poller线程循环事件队列,并通过selector监听客户端通道IO读写事件,这个过程属于同步非阻塞,效率非常高,如果IO读写就绪,就将当前的channel交给SocketProcessor任务,该任务最终会扔给Tomcat扩展JDK的线程池来处理。同Acceptor,该线程也是一个守护线程
- SocketProcessor:处理有IO读写事件发生的客户端channel,然后封装一番交给对应的protocolHandler进行处理
- ThreadPoolExecutor:处理客户端请求的线程
容器层
- Container:容器,在Tomcat中有四个容器是层级关系,分别是Engine,Host,Context,Wrapper
- Engine:最上层的容器,其中包含一组Host组件
- Host:表示一个包含在 Engine 中的虚拟主机容器。内部包含一个或者多个Context容器
- Context:表示一个Servlet上下文,通常包含在Host容器中,Context组件代表 Web 应用程序(webapps目录下的一个项目就是一个 Context)
- Wrapper:最下层的容器,包装了一个Servlet
- Pipeline与Valve:Valve是责任链中的一个处理器,在Tomcat中有两个责任链,这个负责给Tomcat内部使用,而给外部使用的是Filter那个责任链
- Filter:属于J2EE的定义,表示一个请求的过滤链,也称为拦截器链,当请求在访问到最终的Servlet组件前,必须经过所有的拦截器链才能访问
- Mapper:帮助一个请求路由到对应的Servlet
- Manager:用于管理与Context组件绑定的Session池,不同的Manager组件可以自定义添加自己的处理Pipeline(由Valve组件组成)将Session进行持久化
可以忽略的组件
- Cluster:表示当前多个Tomcat服务器组成了一个集群集群之间可以互相传输共享信息例如可以使用Cluster组件实现Session会话共享,不过因为这种方式效率较低,一般不建议使用。通常的做法是使用Redis共享会话信息
- Realm:表示一个只读的安全领域的门面,通常使用Realm验证不同的用户,并且授予这些用户相应的访问规则。Realm组件通常和容器等级的组件联合使用
- Resource Link:代表了一个Web应用程序的资源链接即在配置文件中定义的Resource标签的信息。这些资源链访问Global Naming Resources中的资源
- Resource:Resource组件代表了一个命名资源对象
其他组件
- Listener:Listener组件也称为LifecycleListener,用于监听组件在整个生命周期过程中产生的事件,从而完成响应的动作,在Tomcat生命周期设计中会详细解释
- Loader:Loader组件代表了一个Java类加载器,用于在容器中加载必要的类文件,通常用于热部署时应用上下文类文件
- Adapter:由于协议不同,客户端发过来的请求信息也不尽相同,Tomcat定义了自己的Request 类来封装这些请求信息。ProtocolHandler接口负责解析请求并生成Tomcat Request类。但是这个Request对象不是标准的ServletRequest,不能被CoyoteAdapter使用,这是设配器模式的经典运用,连接器调用CoyoteAdapter的Service方法,传入的是Tomcat Request对象,CoyoteAdapter负责将这个对象转换成ServletRequest,再传递给容器
6.架构分析
图示
自顶向下分析Tomcat架构
- Tomcat需要一个对象来代表整个Servlet容器,这时引入了Server组件
- Tomcat需要一个组件来包含一个或者多个连接器,这时引入了Service组件
- Tomcat需要一个用于接收客户端并处理协议相关的功能,这时引入了Connector连接器
- Tomcat需要一个用于实现HTTP中指定服务主机的功能,这时引入了Host组件
- Tomcat需要一个用于表示Web应用的功能,这时引入了Context
- Tomcat需要一个用于表示Servlet的功能,这时引入了Wrapper
自下向上分析Tomcat架构
- 应用需要实现Servlet接口,在其中的Service方法编写业务逻辑。每个Servlet都有自己的生命周期,为了满足这些需求,引入了Wrapper组件
- 一个Web应用将会有很多Servlet实例,这时就会有很多Wrapper组件,同时,根据Servlet规范,需要实现Web应用的Session机制、Web类加载器机制、资源集管理,为了满足这些需求,需要一个管理对象代表一个Web应用,这时引入了Context组件
- HTTP头部拥有一个Host字段来选择后端服务的主机,这时需要一个用于管理上下文组件的需求,这时引入了Host虚拟主机
- 在拥有了多个Host虚拟主机组件后,就需要有一个管理这些组件的功能,这时引入了Engine组件
- 为了对外提供服务,引入Connector组件
- 有了Connector、Engine组件后,需要有一个能管理这些组件的功能,这时引入了Service
- Service组件的配置需求、管理需求,需引入最上层的Server组件