若是想让一个系统可以对外提供服务,咱们须要建立、组装并启动这些组件;在服务中止的时候,咱们还须要释放资源,销毁这些组件,所以这是一个动态的过程。也就是说,Tomcat 须要动态地管理这些组件的生命周期。html
在父组件的 init 方法里须要建立子组件并调用子组件的 init 方法。一样,在父组件的 start 方法里也须要调用子组件的 start 方法,所以调用者能够无差异的调用各组件的 init 方法和 start 方法,这就是组合模式的使用java
组件的 init 和 start 调用是由它的父组件的状态变化触发的,上层组件的初始化会触发子组件的初始化,上层组件的启动会触发子组件的启动,所以咱们把组件的生命周期定义成一个个状态,把状态的转变看做是一个事件。而事件是有监听器的,在监听器里能够实现一些逻辑,而且监听器也能够方便的添加和删除,这就是典型的观察者模式git
private final List<LifecycleListener> lifecycleListeners = new CopyOnWriteArrayList<>();
/** * Allow sub classes to fire {@link Lifecycle} events. * * @param type Event type * @param data Data associated with event. */
protected void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(this, type, data);
for (LifecycleListener listener : lifecycleListeners) {
listener.lifecycleEvent(event);
}
}
复制代码
接口可能有多个实现类,为了不每一个实现类都去实现一些重复的逻辑。能够定义一个基类来实现共同的逻辑。而基类中每每会定义一些抽象方法,所谓的抽象方法就是说基类不会去实现这些方法,而是调用这些方法来实现骨架逻辑。抽象方法是留给各个子类去实现的,而且子类必须实现,不然没法实例github
注意下图注释中的描述。数据库
/** * {@inheritDoc} */
@Override
public final synchronized void start() throws LifecycleException {
if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
LifecycleState.STARTED.equals(state)) {
if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
} else if (log.isInfoEnabled()) {
log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
}
return;
}
if (state.equals(LifecycleState.NEW)) {
init();
} else if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.INITIALIZED) &&
!state.equals(LifecycleState.STOPPED)) {
invalidTransition(Lifecycle.BEFORE_START_EVENT);
}
try {
setStateInternal(LifecycleState.STARTING_PREP, null, false);
startInternal(); //骨架代码中调用的预留的抽象方法,未来每一个子类在使用本方法的时候,就是调用的它本身实现了startInternal
if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.STARTING)) {
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
setStateInternal(LifecycleState.STARTED, null, false);
}
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.startFail", toString());
}
}
复制代码
Connector上的线程池负责处理这个它所接收到的全部请求。一个Connector有一个线程池。其余的后台任务也有专门的线程池,不会占用Connector的线程池。bootstrap
//建立并注册关闭钩子
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);//关闭钩子其实就是一个实现了run方法的线程对象。
}
复制代码
Tomcat会调用Web应用的代码来处理请求,可能Web应用代码阻塞在某个地方。数组
理论上:线程数=((线程阻塞时间 + 线程忙绿时间) / 线程忙碌时间) * cpu核数若是线程始终不阻塞,一直忙碌,会一直占用一个CPU核,所以能够直接设置 线程数=CPU核数。可是现实中线程可能会被阻塞,好比等待IO。所以根据上面的公式肯定线程数。那怎么肯定线程的忙碌时间和阻塞时间?要通过压测,在代码中埋点统计.缓存
问题:刚到新公司,看他们把一个tomact的connector的线程池设置成800,这个太夸张了吧,connector的线程池只是用来处理接收的http请求,线程池不会用来处理其余业务自己的事情,设置再大也只能提升请求的并发,并不能提升系统的响应,让这个线程池干其余的事情,并且线程数过高,线程上下文切换时间也高,反而会下降系统的响应速度吧?我理解是否是对的,老师?还有一个问题就是设置的connector线程数,是tomcat启动的时候就会初始化这么多固定的线程仍是这只是一个上限,还有就是若是线程处于空闲状态,会不会进行上下文切换呢?tomcat
解答:这里误解了Connector中的线程池,这个线程池就是用来处理业务的。另外你提到线程数设的过高,会有线程切换的开销,这是对的。线程数具体设多少,根据具体业务而定,若是你的业务是IO密集型的,好比大量线程等待在数据库读写上,线程数应该设的越高。若是是CPU密集型,彻底没有阻塞,设成CPU核数就行。800这个数有点高,我猜大家的应用属于IO密集型。并发
本文是我我的学习了李号双老师的专栏课程以后,结合专栏文章和老师对同窗答疑整理的学习笔记。仅供分享。更多精彩内容,你们能够扫描下方二位码,在极客时间订阅李号双老师的《深刻拆解Tomcat & Jetty》。获取一手学习资料。