Engine初始化
StandardEngine在init阶段,需要获取Realm,这个Realm是干嘛用的?
Realm(域)是用于对单个用户进行身份验证的底层安全领域的只读外观,并标识与这些用户相关联的安全角色。
StandardEngine初始化的代码如下:
@Override
protected void initInternal() throws LifecycleException {
getRealm();
super.initInternal();
}
public Realm getRealm() {
Realm configured = super.getRealm();
if (configured == null) {
configured = new NullRealm();
this.setRealm(configured);
}
return configured;
由前面的类图可知,StandardEngine继承至ContainerBase,而ContainerBase重写了initInternal()方法,用于初始化start、stop线程池,这个线程池有以下特点:
1、 core线程和max是相等的,默认为1
2、 允许core线程在超时未获取到任务时退出线程
3、 线程获取任务的超时时间是10s,也就是说所有的线程(包括core线程),超过10s未获取到任务,那么这个线程就会被销毁
这么做的初衷是什么呢?因为这个线程池只需要在容器启动和停止的时候发挥作用,没必要时时刻刻处理任务队列
ContainerBase的代码如下所示:
// 默认是1个线程
private int startStopThreads = 1;
protected ThreadPoolExecutor startStopExecutor;
@Override
protected void initInternal() throws LifecycleException {
BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<>();
startStopExecutor = new ThreadPoolExecutor(
getStartStopThreadsInternal(),
getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
startStopQueue,
new StartStopThreadFactory(getName() + "-startStop-"));
// 允许core线程超时未获取任务时退出
startStopExecutor.allowCoreThreadTimeOut(true);
super.initInternal();
}
private int getStartStopThreadsInternal() {
int result = getStartStopThreads();
if (result > 0) {
return result;
}
result = Runtime.getRuntime().availableProcessors() + result;
if (result < 1) {
result = 1;
}
return result;
这个startStopExecutor线程池有什么用呢?
1、 在start的时候,如果发现有子容器,则会把子容器的start操作放在线程池中进行处理
2、 在stop的时候,也会把stop操作放在线程池中处理
在前面的文章中我们介绍了Container组件,StandardEngine作为顶层容器,它的直接子容器是StardandHost,但是对StandardEngine的代码分析,我们并没有发现它会对子容器StardandHost进行初始化操作,StandardEngine不按照套路出牌,而是把初始化过程放在start阶段。个人认为Host、Context、Wrapper这些容器和具体的webapp应用相关联了,初始化过程会更加耗时,因此在start阶段用多线程完成初始化以及start生命周期,否则,像顶层的Server、Service等组件需要等待Host、Context、Wrapper完成初始化才能结束初始化流程,整个初始化过程是具有传递性的
Connector初始化
Connector也是继承至LifecycleMBeanBase,公共的初始化逻辑都是一样的。我们先来看下Connector的默认配置,大部分属性配置都可以在Connector类中找到,tomcat默认开启了HTTP/1.1、AJP/1.3,其实AJP的用处不大,可以去掉
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
Connector定义了很多属性,比如port、redirectPort、maxCookieCount、maxPostSize等等,比较有意思的是竟然找不到connectionTimeout的定义,全文搜索后发现使用了属性名映射,估计是为了兼容以前的版本
protected static final HashMap<String,String> replacements = new HashMap<>();
static {
replacements.put("acceptCount", "backlog");
replacements.put("connectionLinger", "soLinger");
replacements.put("connectionTimeout", "soTimeout");
replacements.put("rootFile", "rootfile");
}
public Object getProperty(String name) {
String repl = name;
if (replacements.get(name) != null) {
repl = replacements.get(name);
}
return IntrospectionUtils.getProperty(protocolHandler, repl);
}
public boolean setProperty(String name, String value) {
String repl = name;
if (replacements.get(name) != null) {
repl = replacements.get(name);
}
return IntrospectionUtils.setProperty(protocolHandler, repl, value);
initInternal过程如下所示:
1、 实例化Coyote适配器,这个适配器是用于Coyote的Request、Response与HttpServlet的Request、Response适配的,后续的博客会进行深入分析
2、 为ProtocolHander指定CoyoteAdapter用于处理请求
3、 初始化ProtocolHander,这一部分放在Connector后面进行分析
protected void initInternal() throws LifecycleException {
// 注册jmx
super.initInternal();
// 初始化Coyote适配器,这个适配器是用于Coyote的Request、Response与HttpServlet的Request、Response适配的
adapter = new CoyoteAdapter(this);
// protocolHandler需要指定Adapter用于处理请求
protocolHandler.setAdapter(adapter);
// Make sure parseBodyMethodsSet has a default
if (null == parseBodyMethodsSet) {
setParseBodyMethods(getParseBodyMethods());
}
// apr支持,忽略部分代码......
// 初始化ProtocolHandler,这个init不是Lifecycle定义的init,而是ProtocolHandler接口的init
try {
protocolHandler.init();
} catch (Exception e) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
}
ProtocolHandler初始化
接下来,我们分析下HTTP/1.1的ProtocolHandler的初始化过程。首先,我们来认识下ProtocolHandler,它是一个抽象的协议实现,它不同于JNI这样的Jk协议,它是单线程、基于流的协议。ProtocolHandler是一个Cycote连接器实现的主要接口,而Adapter适配器是由一个Coyote Servlet容器实现的主要接口,定义了处理请求的抽象接口。
public interface ProtocolHandler {
public void setAdapter(Adapter adapter);
public Adapter getAdapter();
public Executor getExecutor();
public void init() throws Exception;
public void start() throws Exception;
public void pause() throws Exception;
public void resume() throws Exception;
public void stop() throws Exception;
public void destroy() throws Exception;
// other code......
}
public interface Adapter {
public void service(Request req, Response res) throws Exception;
public boolean prepare(Request req, Response res) throws Exception;
public boolean asyncDispatch(Request req,Response res, SocketEvent status) throws Exception;
public void log(Request req, Response res, long time);
public void checkRecycled(Request req, Response res);
public String getDomain();
ProtocolHandler的子类如下所示,AbstractProtocol是基本的实现,而NIO默认使用的是Http11NioProtocol
调用ProtocolHandler的init进行初始化是调用的AbstractProtocol,首先完成jmx的注册,然后对NioEndpoint进行初始化
public abstract class AbstractProtocol<S> implements ProtocolHandler,
MBeanRegistration {
public void init() throws Exception {
// 完成jmx注册
if (oname == null) {
oname = createObjectName();
if (oname != null) {
Registry.getRegistry(null, null).registerComponent(this, oname, null);
}
}
if (this.domain != null) {
rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
Registry.getRegistry(null, null).registerComponent(
getHandler().getGlobal(), rgOname, null);
}
String endpointName = getName();
endpoint.setName(endpointName.substring(1, endpointName.length()-1));
endpoint.setDomain(domain);
// 初始化endpoint
endpoint.init();
}