IO(七)
计划一周时间,理解Tomcat的设计思想,为什么这么设计,以及Tomcat设计带来的特点是什么。
今天是第一天,tomcat源码阅读。其实很奇怪的事情是什么,目前我平时的工作中,其实对Tomcat的使用很少,就是单纯自己去配置它。现在集成在SpringBoot中,好像只是单纯的作为一个工具在使用,但是我依然还是秉持着自己的复习路线。凡是日常开发中需要使用到的东西,至少要知道怎么用,以及它的设计原理。架构是一件很有艺术的事情,而这些Tomcat,Sping包,包括Java的核心包的设计,就是学习设计思想上的经典之作。
怎么看源码,这其实是一个很细致的问题。像Tomcat这种,我自己不会选择同SpringBoot一样的看法。我会选择它重要的组件,去进行阅读。
首先这张顶级架构图,可以看出我们主要需要看的几个部分。先看完组件,知道组件是为什么的,然后再看启动流程,知道他们之间是如何进行关联的。我一般是选择这种方式来进行源码的阅读。
先看下Connector。在这个类的Doc上,有个很简短的一句话:"Implementation of a Coyote connector."翻译过来,这是一个土狼连接器。然后看下这个类的继承图

看下这些都是什么作用。先从最顶层的Lifecycle是管理生命周期用的,Catalina组件可以实现这个接口用来管理自己的生命周期。

根据Doc里面给的流程,差不多就是以下几个状态之间的转换,主要是auto是如何实现的。在代码中,定义了很多具体的常量

剩余的代码:
// --------------------------------------------------------- Public Methods
/**
* 将LifecycleEvent侦听器添加到此组件。
* 侦听器的作用:在发生关联状态更改之后,将触发该侦听器。
* 侦听器里面有方法 public void lifecycleEvent(LifecycleEvent event);
* void反过来说就是侦听器里面大概率会有一个成员变量来暂存状态
* @param listener The listener to add
*/
public void addLifecycleListener(LifecycleListener listener);
/**
* 获取与此生命周期相关联的生命周期侦听器
* @return An array containing the life cycle listeners associated with this
* life cycle. If this component has no listeners registered, a
* zero-length array is returned.
*/
public LifecycleListener[] findLifecycleListeners();
/**
* Remove a LifecycleEvent listener from this component.
*
* @param listener The listener to remove
*/
public void removeLifecycleListener(LifecycleListener listener);
/**
* 准备要启动的组件。此方法应执行对象创建后所需的任何初始化。
* The following {@link LifecycleEvent}s will be fired in the following order:
* <ol>
* <li>INIT_EVENT: On the successful completion of component initialization.</li>
* </ol>
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
public void init() throws LifecycleException;
/**
* Prepare for the beginning of active use of the public methods other than
* property getters/setters and life cycle methods of this component. This
* method should be called before any of the public methods other than
* property getters/setters and life cycle methods of this component are
* utilized. The following {@link LifecycleEvent}s will be fired in the
* following order:
* <ol>
* <li>BEFORE_START_EVENT: At the beginning of the method. It is as this
* point the state transitions to
* {@link LifecycleState#STARTING_PREP}.</li>
* <li>START_EVENT: During the method once it is safe to call start() for
* any child components. It is at this point that the
* state transitions to {@link LifecycleState#STARTING}
* and that the public methods other than property
* getters/setters and life cycle methods may be
* used.</li>
* <li>AFTER_START_EVENT: At the end of the method, immediately before it
* returns. It is at this point that the state
* transitions to {@link LifecycleState#STARTED}.
* </li>
* </ol>
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
public void start() throws LifecycleException;
/**
* Gracefully terminate the active use of the public methods other than
* property getters/setters and life cycle methods of this component. Once
* the STOP_EVENT is fired, the public methods other than property
* getters/setters and life cycle methods should not be used. The following
* {@link LifecycleEvent}s will be fired in the following order:
* <ol>
* <li>BEFORE_STOP_EVENT: At the beginning of the method. It is at this
* point that the state transitions to
* {@link LifecycleState#STOPPING_PREP}.</li>
* <li>STOP_EVENT: During the method once it is safe to call stop() for
* any child components. It is at this point that the
* state transitions to {@link LifecycleState#STOPPING}
* and that the public methods other than property
* getters/setters and life cycle methods may no longer be
* used.</li>
* <li>AFTER_STOP_EVENT: At the end of the method, immediately before it
* returns. It is at this point that the state
* transitions to {@link LifecycleState#STOPPED}.
* </li>
* </ol>
*
* Note that if transitioning from {@link LifecycleState#FAILED} then the
* three events above will be fired but the component will transition
* directly from {@link LifecycleState#FAILED} to
* {@link LifecycleState#STOPPING}, bypassing
* {@link LifecycleState#STOPPING_PREP}
*
* @exception LifecycleException if this component detects a fatal error
* that needs to be reported
*/
public void stop() throws LifecycleException;
/**
* Prepare to discard the object. The following {@link LifecycleEvent}s will
* be fired in the following order:
* <ol>
* <li>DESTROY_EVENT: On the successful completion of component
* destruction.</li>
* </ol>
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
public void destroy() throws LifecycleException;
/**
* Obtain the current state of the source component.
*
* @return The current state of the source component.
*/
public LifecycleState getState();
/**
* Obtain a textual representation of the current component state. Useful
* for JMX. The format of this string may vary between point releases and
* should not be relied upon to determine component state. To determine
* component state, use {@link #getState()}.
*
* @return The name of the current component state.
*/
public String getStateName();
/**
* Marker interface used to indicate that the instance should only be used
* once. Calling {@link #stop()} on an instance that supports this interface
* will automatically call {@link #destroy()} after {@link #stop()}
* completes.
*/
public interface SingleUse {
}
Lifecycle这个接口主要是定义了监听和生命周期。实现这个接口,就有了组件的生命周期概念。
然后看一下MBeanRegistration接口的作用,MBean是一个什么pojo?Mean一般是MeanServer,实现了MBeanServerConnection接口,MBeanServerConnection接口中定义了一些创建MeanServer的方法。
也就是说MeanServer这个接口主要是用来生成对象的,这个对象的作用是:MBean Server是一个集中的管理MBean对象实例的地方。通过MBeanServer,你可以查询、管理、操作Agent应用发布的特定的MBean。
而MBean是一种规范的JavaBean,通过集成和实现一套标准的Bean接口,这种叫MBean,Mbean注册到MBeanServer中。之后将被MBeanServer中注册过的Adapter(比如渲染为HTML的HtmlAdapter)渲染为直观的页面将MBean的属性和方法展示给用户。
这边我看网上的一些介绍,要牵涉到JMX架构。那就顺带把这个先给看了。
JMX,全称为Java Management Extensions
JMX(Java Management Extensions)是一个为应用程序植入管理功能的框架。JMX是一套标准的代理和服务,实际上,用户可以在任何Java应用程序中使用这些代理和服务实现管理。这是官方文档上的定义,我看过很多次也无法很好的理解。我个人的理解是JMX让程序有被管理的功能,例如你开发一个WEB网站,它是在24小时不间断运行,那么你肯定会对网站进行监控,如每天的UV、PV是多少;又或者在业务高峰的期间,你想对接口进行限流,就必须去修改接口并发的配置值。
应用场景:中间件软件WebLogic的管理页面就是基于JMX开发的,而JBoss则整个系统都基于JMX构架。
对于一些参数的修改,网上有一段描述还是比较形象的:
1、程序初哥一般是写死在程序中,到要改变的时候就去修改代码,然后重新编译发布。
2、程序熟手则配置在文件中(JAVA一般都是properties文件),到要改变的时候只要修改配置文件,但还是必须重启系统,以便读取配置文件里最新的值。
3、程序好手则会写一段代码,把配置值缓存起来,系统在获取的时候,先看看配置文件有没有改动,如有改动则重新从配置里读取,否则从缓存里读取。
4、程序高手则懂得物为我所用,用JMX把需要配置的属性集中在一个类中,然后写一个MBean,再进行相关配置。另外JMX还提供了一个工具页,以方便我们对参数值进行修改。
那么Connector至少先具备了生命周期,被JMX拓展的特点,然后看看它自身是有什么方法。
首先它是默认默认为使用HTTP / 1.1 NIO实现。同时它也支持HTTP/1.1,AJP/1.3。
看它的构造函数
public Connector(String protocol) {
boolean aprConnector = AprLifecycleListener.isAprAvailable() &&
AprLifecycleListener.getUseAprConnector();
if ("HTTP/1.1".equals(protocol) || protocol == null) {
if (aprConnector) {
protocolHandlerClassName = "org.apache.coyote.http11.Http11AprProtocol";
} else {
protocolHandlerClassName = "org.apache.coyote.http11.Http11NioProtocol";
}
} else if ("AJP/1.3".equals(protocol)) {
if (aprConnector) {
protocolHandlerClassName = "org.apache.coyote.ajp.AjpAprProtocol";
} else {
protocolHandlerClassName = "org.apache.coyote.ajp.AjpNioProtocol";
}
} else {
protocolHandlerClassName = protocol;
}
// Instantiate protocol handler
ProtocolHandler p = null;
try {
Class<?> clazz = Class.forName(protocolHandlerClassName);
p = (ProtocolHandler) clazz.getConstructor().newInstance();
} catch (Exception e) {
log.error(sm.getString(
"coyoteConnector.protocolHandlerInstantiationFailed"), e);
} finally {
this.protocolHandler = p;
}
// Default for Connector depends on this system property
setThrowOnFailure(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"));
}
首先是确定协议,然后反射根据名字来创建出处理类。protocolHandler的作用是抽象协议实现,包括线程等,这边重要的是,这玩意里面有很多的细节配置,例如连接中Post,Get请求的最大数据量,异步时间等。
Connector组件其中包含Protocol组件、Mapper组件和CoyoteAdapter组件;这三个是最关键的,可以这样理解,Connector其实只是一个外壳,真正的内在驱动力还是要靠这三个组件。
例如Protocol组件,封装了Netty
public class Http11NioProtocol extends AbstractHttp11JsseProtocol<NioChannel> {
private static final Log log = LogFactory.getLog(Http11NioProtocol.class);
public Http11NioProtocol() {
super(new NioEndpoint());
}
@Override
protected Log getLog() { return log; }
// -------------------- Pool setup --------------------
/**
* NO-OP.
*
* @param count Unused
*/
public void setPollerThreadCount(int count) {
}
public int getPollerThreadCount() {
return 1;
}
public void setSelectorTimeout(long timeout) {
((NioEndpoint)getEndpoint()).setSelectorTimeout(timeout);
}
public long getSelectorTimeout() {
return ((NioEndpoint)getEndpoint()).getSelectorTimeout();
}
public void setPollerThreadPriority(int threadPriority) {
((NioEndpoint)getEndpoint()).setPollerThreadPriority(threadPriority);
}
public int getPollerThreadPriority() {
return ((NioEndpoint)getEndpoint()).getPollerThreadPriority();
}
// ----------------------------------------------------- JMX related methods
@Override
protected String getNamePrefix() {
if (isSSLEnabled()) {
return "https-" + getSslImplementationShortName()+ "-nio";
} else {
return "http-nio";
}
}
}
CoyoteAdapter组件是一个适配器,它负责将Connector组件和Engine容器适配连接起来。把接收到的客户端请求报文解析生成的请求对象Request和响应对象Response传递到Engine容器,交由容器处理;
Mapper组件可以称为路由器,它提供了对客户端请求URL的映射功能,即可以通过它将请求转发到对应的Host组件、Context组件、Wrapper组件以进行处理并响应客户端,也就是我们说的将某客户端请求发送到某虚拟机上的某个Web应用的某个Servlet。

浙公网安备 33010602011771号