理解思路
- 第一:抓住standardserver整体类依赖结构来理解

- 第二:结合server.xml来理解
见下文具体阐述。
- 第三:结合server config官方配置文档
http://tomcat.apache.org/tomcat-9.0-doc/config/server.html
server结构设计
我们需要从高一点的维度去理解server的结构设计,而不是多少方法多少代码;这里的理解一定是要结合server.xml对应理解。
server.xml
- 首先要看下server.xml,这样你便知道了需要了解的四个部分
<server port="8005" shutdown="shutdown">
<!-- 1.属性说明
port:指定一个端口,这个端口负责监听关闭tomcat的请求
shutdown:向以上端口发送的关闭服务器的命令字符串
-->
<!-- 2.listener 相关 -->
<listener classname="org.apache.catalina.core.aprlifecyclelistener" />
<listener classname="org.apache.catalina.mbeans.serverlifecyclelistener" />
<listener classname="org.apache.catalina.mbeans.globalresourceslifecyclelistener" />
<listener classname="org.apache.catalina.storeconfig.storeconfiglifecyclelistener"/>
<!-- 3.globalnamingresources 相关 -->
<globalnamingresources>
<environment name="simplevalue" type="java.lang.integer" value="30"/>
<resource name="userdatabase" auth="container"
type="org.apache.catalina.userdatabase"
description="user database that can be updated and saved"
factory="org.apache.catalina.users.memoryuserdatabasefactory"
pathname="conf/tomcat-users.xml" />
</globalnamingresources>
<!-- 4.service 相关 -->
<service name="catalina">
</service>
</server>server中的接口设计
- 公共属性, 包括上面的port,shutdown, address等
/** * @return the port number we listen to for shutdown commands. * * @see #getportoffset() * @see #getportwithoffset() */ public int getport(); /** * set the port number we listen to for shutdown commands. * * @param port the new port number * * @see #setportoffset(int) */ public void setport(int port); /** * get the number that offsets the port used for shutdown commands. * for example, if port is 8005, and portoffset is 1000, * the server listens at 9005. * * @return the port offset */ public int getportoffset(); /** * set the number that offsets the server port used for shutdown commands. * for example, if port is 8005, and you set portoffset to 1000, * connector listens at 9005. * * @param portoffset sets the port offset */ public void setportoffset(int portoffset); /** * get the actual port on which server is listening for the shutdown commands. * if you do not set port offset, port is returned. if you set * port offset, port offset + port is returned. * * @return the port with offset */ public int getportwithoffset(); /** * @return the address on which we listen to for shutdown commands. */ public string getaddress(); /** * set the address on which we listen to for shutdown commands. * * @param address the new address */ public void setaddress(string address); /** * @return the shutdown command string we are waiting for. */ public string getshutdown(); /** * set the shutdown command we are waiting for. * * @param shutdown the new shutdown command */ public void setshutdown(string shutdown); /** * get the utility thread count. * @return the thread count */ public int getutilitythreads(); /** * set the utility thread count. * @param utilitythreads the new thread count */ public void setutilitythreads(int utilitythreads);
| 属性 | 描述 |
|---|---|
| classname | 使用的java类名称。此类必须实现org.apache.catalina.server接口。如果未指定类名,则将使用标准实现。 |
| address | 该服务器等待关闭命令的tcp / ip地址。如果未指定地址,localhost则使用。 |
| port | 该服务器等待关闭命令的tcp / ip端口号。设置为-1禁用关闭端口。注意:当使用apache commons daemon启动tomcat (在windows上作为服务运行,或者在un * xes上使用jsvc运行)时,禁用关闭端口非常有效。但是,当使用标准shell脚本运行tomcat时,不能使用它,因为它将阻止shutdown.bat |
| portoffset | 应用于port和嵌套到任何嵌套连接器的端口的偏移量。它必须是一个非负整数。如果未指定,0则使用默认值。 |
| shutdown | 为了关闭tomcat,必须通过与指定端口号的tcp / ip连接接收的命令字符串。 |
| utilitythreads | 此service中用于各种实用程序任务(包括重复执行的线程)的线程数。特殊值0将导致使用该值 runtime.getruntime().availableprocessors()。runtime.getruntime().availableprocessors() + value除非小于1,否则将使用负值, 在这种情况下将使用1个线程。预设值是1。 |
- namingresources
/**
* @return the global naming resources.
*/
public namingresourcesimpl getglobalnamingresources();
/**
* set the global naming resources.
*
* @param globalnamingresources the new global naming resources
*/
public void setglobalnamingresources
(namingresourcesimpl globalnamingresources);
/**
* @return the global naming resources context.
*/
public javax.naming.context getglobalnamingcontext();- service相关, 包括添加service, 查找service,删除service等
/** * add a new service to the set of defined services. * * @param service the service to be added */ public void addservice(service service); /** * wait until a proper shutdown command is received, then return. */ public void await(); /** * find the specified service * * @param name name of the service to be returned * @return the specified service, or <code>null</code> if none exists. */ public service findservice(string name); /** * @return the set of services defined within this server. */ public service[] findservices(); /** * remove the specified service from the set associated from this * server. * * @param service the service to be removed */ public void removeservice(service service);
standardserver的实现
线程池
// 此service中用于各种实用程序任务(包括重复执行的线程)的线程数
@override
public int getutilitythreads() {
return utilitythreads;
}
/**
* 获取内部进程数计算逻辑:
* > 0时,即utilitythreads的值。
* <=0时,runtime.getruntime().availableprocessors() + result...
*/
private static int getutilitythreadsinternal(int utilitythreads) {
int result = utilitythreads;
if (result <= 0) {
result = runtime.getruntime().availableprocessors() + result;
if (result < 2) {
result = 2;
}
}
return result;
}
@override
public void setutilitythreads(int utilitythreads) {
// use local copies to ensure thread safety
int oldutilitythreads = this.utilitythreads;
if (getutilitythreadsinternal(utilitythreads) < getutilitythreadsinternal(oldutilitythreads)) {
return;
}
this.utilitythreads = utilitythreads;
if (oldutilitythreads != utilitythreads && utilityexecutor != null) {
reconfigureutilityexecutor(getutilitythreadsinternal(utilitythreads));
}
}
// 线程池
private synchronized void reconfigureutilityexecutor(int threads) {
// the scheduledthreadpoolexecutor doesn't use maximumpoolsize, only corepoolsize is available
if (utilityexecutor != null) {
utilityexecutor.setcorepoolsize(threads);
} else {
scheduledthreadpoolexecutor scheduledthreadpoolexecutor =
new scheduledthreadpoolexecutor(threads,
new taskthreadfactory("catalina-utility-", utilitythreadsasdaemon, thread.min_priority));
scheduledthreadpoolexecutor.setkeepalivetime(10, timeunit.seconds);
scheduledthreadpoolexecutor.setremoveoncancelpolicy(true);
scheduledthreadpoolexecutor.setexecuteexistingdelayedtasksaftershutdownpolicy(false);
utilityexecutor = scheduledthreadpoolexecutor;
utilityexecutorwrapper = new org.apache.tomcat.util.threads.scheduledthreadpoolexecutor(utilityexecutor);
}
}
/**
* get if the utility threads are daemon threads.
* @return the threads daemon flag
*/
public boolean getutilitythreadsasdaemon() {
return utilitythreadsasdaemon;
}
/**
* set the utility threads daemon flag. the default value is true.
* @param utilitythreadsasdaemon the new thread daemon flag
*/
public void setutilitythreadsasdaemon(boolean utilitythreadsasdaemon) {
this.utilitythreadsasdaemon = utilitythreadsasdaemon;
}service相关方法实现
里面的方法都很简单。
/**
* add a new service to the set of defined services.
*
* @param service the service to be added
*/
@override
public void addservice(service service) {
service.setserver(this);
synchronized (serviceslock) {
service results[] = new service[services.length + 1];
system.arraycopy(services, 0, results, 0, services.length);
results[services.length] = service;
services = results;
if (getstate().isavailable()) {
try {
service.start();
} catch (lifecycleexception e) {
// ignore
}
}
// report this property change to interested listeners
support.firepropertychange("service", null, service);
}
}
public void stopawait() {
stopawait=true;
thread t = awaitthread;
if (t != null) {
serversocket s = awaitsocket;
if (s != null) {
awaitsocket = null;
try {
s.close();
} catch (ioexception e) {
// ignored
}
}
t.interrupt();
try {
t.join(1000);
} catch (interruptedexception e) {
// ignored
}
}
}
/**
* wait until a proper shutdown command is received, then return.
* this keeps the main thread alive - the thread pool listening for http
* connections is daemon threads.
*/
@override
public void await() {
// negative values - don't wait on port - tomcat is embedded or we just don't like ports
if (getportwithoffset() == -2) {
// undocumented yet - for embedding apps that are around, alive.
return;
}
if (getportwithoffset() == -1) {
try {
awaitthread = thread.currentthread();
while(!stopawait) {
try {
thread.sleep( 10000 );
} catch( interruptedexception ex ) {
// continue and check the flag
}
}
} finally {
awaitthread = null;
}
return;
}
// set up a server socket to wait on
try {
awaitsocket = new serversocket(getportwithoffset(), 1,
inetaddress.getbyname(address));
} catch (ioexception e) {
log.error(sm.getstring("standardserver.awaitsocket.fail", address,
string.valueof(getportwithoffset()), string.valueof(getport()),
string.valueof(getportoffset())), e);
return;
}
try {
awaitthread = thread.currentthread();
// loop waiting for a connection and a valid command
while (!stopawait) {
serversocket serversocket = awaitsocket;
if (serversocket == null) {
break;
}
// wait for the next connection
socket socket = null;
stringbuilder command = new stringbuilder();
try {
inputstream stream;
long acceptstarttime = system.currenttimemillis();
try {
socket = serversocket.accept();
socket.setsotimeout(10 * 1000); // ten seconds
stream = socket.getinputstream();
} catch (sockettimeoutexception ste) {
// this should never happen but bug 56684 suggests that
// it does.
log.warn(sm.getstring("standardserver.accept.timeout",
long.valueof(system.currenttimemillis() - acceptstarttime)), ste);
continue;
} catch (accesscontrolexception ace) {
log.warn(sm.getstring("standardserver.accept.security"), ace);
continue;
} catch (ioexception e) {
if (stopawait) {
// wait was aborted with socket.close()
break;
}
log.error(sm.getstring("standardserver.accept.error"), e);
break;
}
// read a set of characters from the socket
int expected = 1024; // cut off to avoid dos attack
while (expected < shutdown.length()) {
if (random == null)
random = new random();
expected += (random.nextint() % 1024);
}
while (expected > 0) {
int ch = -1;
try {
ch = stream.read();
} catch (ioexception e) {
log.warn(sm.getstring("standardserver.accept.readerror"), e);
ch = -1;
}
// control character or eof (-1) terminates loop
if (ch < 32 || ch == 127) {
break;
}
command.append((char) ch);
expected--;
}
} finally {
// close the socket now that we are done with it
try {
if (socket != null) {
socket.close();
}
} catch (ioexception e) {
// ignore
}
}
// match against our command string
boolean match = command.tostring().equals(shutdown);
if (match) {
log.info(sm.getstring("standardserver.shutdownviaport"));
break;
} else
log.warn(sm.getstring("standardserver.invalidshutdowncommand", command.tostring()));
}
} finally {
serversocket serversocket = awaitsocket;
awaitthread = null;
awaitsocket = null;
// close the server socket and return
if (serversocket != null) {
try {
serversocket.close();
} catch (ioexception e) {
// ignore
}
}
}
}
/**
* @return the specified service (if it exists); otherwise return
* <code>null</code>.
*
* @param name name of the service to be returned
*/
@override
public service findservice(string name) {
if (name == null) {
return null;
}
synchronized (serviceslock) {
for (service service : services) {
if (name.equals(service.getname())) {
return service;
}
}
}
return null;
}
/**
* @return the set of services defined within this server.
*/
@override
public service[] findservices() {
return services;
}
/**
* @return the jmx service names.
*/
public objectname[] getservicenames() {
objectname onames[]=new objectname[ services.length ];
for( int i=0; i<services.length; i++ ) {
onames[i]=((standardservice)services[i]).getobjectname();
}
return onames;
}
/**
* remove the specified service from the set associated from this
* server.
*
* @param service the service to be removed
*/
@override
public void removeservice(service service) {
synchronized (serviceslock) {
int j = -1;
for (int i = 0; i < services.length; i++) {
if (service == services[i]) {
j = i;
break;
}
}
if (j < 0)
return;
try {
services[j].stop();
} catch (lifecycleexception e) {
// ignore
}
int k = 0;
service results[] = new service[services.length - 1];
for (int i = 0; i < services.length; i++) {
if (i != j)
results[k++] = services[i];
}
services = results;
// report this property change to interested listeners
support.firepropertychange("service", service, null);
}
}lifecycle相关模板方法
这里只展示startinternal方法
/**
* start nested components ({@link service}s) and implement the requirements
* of {@link org.apache.catalina.util.lifecyclebase#startinternal()}.
*
* @exception lifecycleexception if this component detects a fatal error
* that prevents this component from being used
*/
@override
protected void startinternal() throws lifecycleexception {
firelifecycleevent(configure_start_event, null);
setstate(lifecyclestate.starting);
globalnamingresources.start();
// start our defined services
synchronized (serviceslock) {
for (int i = 0; i < services.length; i++) {
services[i].start();
}
}
if (periodiceventdelay > 0) {
monitorfuture = getutilityexecutor().schedulewithfixeddelay(
new runnable() {
@override
public void run() {
startperiodiclifecycleevent();
}
}, 0, 60, timeunit.seconds);
}
}
protected void startperiodiclifecycleevent() {
if (periodiclifecycleeventfuture == null || (periodiclifecycleeventfuture != null && periodiclifecycleeventfuture.isdone())) {
if (periodiclifecycleeventfuture != null && periodiclifecycleeventfuture.isdone()) {
// there was an error executing the scheduled task, get it and log it
try {
periodiclifecycleeventfuture.get();
} catch (interruptedexception | executionexception e) {
log.error(sm.getstring("standardserver.periodiceventerror"), e);
}
}
periodiclifecycleeventfuture = getutilityexecutor().scheduleatfixedrate(
new runnable() {
@override
public void run() {
firelifecycleevent(lifecycle.periodic_event, null);
}
}, periodiceventdelay, periodiceventdelay, timeunit.seconds);
}
}方法的第一行代码先触发 configure_start_event 事件,以便执行 standardserver 的 lifecyclelistener 监听器,然后调用 setstate 方法设置成 lifecyclebase 的 state 属性为 lifecyclestate.starting。 接着就 globalnamingresources.start(),跟 initinternal 方法其实是类似的。
再接着就调用 service 的 start 方法来启动 service 组件。可以看出,standardserve 的 startinternal 跟 initinternal 方法类似,都是调用内部的 service 组件的相关方法。
调用完 service.init 方法后,就使用 getutilityexecutor() 返回的线程池延迟执行startperiodiclifecycleevent 方法,而在 startperiodiclifecycleevent 方法里,也是使用 getutilityexecutor() 方法,定期执行 firelifecycleevent 方法,处理 lifecycle.periodic_event 事件,如果有需要定期处理的,可以再 server 的 lifecyclelistener 里处理 lifecycle.periodic_event 事件。
到此这篇关于tomcat server的设计和实现:standardserver详解的文章就介绍到这了,更多相关tomcat server standardserver内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论