Tomcat中的服务器组件和 服务组件
开始学习Tocmat时,都是学习如何通过实例化一个连接器 和 容器 来获得一个Servlet容器,并将连接器 和 servlet容器相互关联,但是之前学习的都只有一个连接器可以使用,该连接器服务8080端口上的HTTP请求,无法添加另一个连接器来服务 诸如 HTTPS之类的其他请求,而且前面所有学的示例,都缺少一种启动或者 关闭servlet容器的机制,那么下面学习一下提供这两种机制的特性的组件,分别是服务器组件 和 服务组件。
服务器组件.
org.apahce.catalina.Server接口的实例 表示Catalina的整个Servlet引擎,囊括了所有的组件,服务器组件是非常有用的,因为它使用了一种方法很容易的就启动/关闭整个系统,不需要对连接器 和 servlet容器 分别操作。
下面来说明一下启动/关闭机制的具体工作原理,当启动服务器组件是,它会启动它囊括的所有组件,然后它就无期限的等待关闭命令,如果想要关闭系统,可以向指定的端口发送一条关闭命令,服务器组件接收到关闭命令之后,就会关闭其中所有的组件。
服务器组件使用了另一个组件(即服务组件)来包含其他组件,如一个容器组件 和 一个或者多个连接器组件,
下面先来看一下服务器组件的接口定义
package org.apache.catalina; import org.apache.catalina.deploy.NamingResources; /**
*
* <p>
* <b>Title:Server.java</b>
* </p>
* <p>
* Copyright:ChenDong 2018
* </p>
* <p>
* Company:仅学习时使用
* </p>
* <p>
* 类功能描述:Server元素表示整个Catalina
* servlet容器。它的属性代表了servlet容器的整体特性。服务器可以包含一个或多个服务以及顶级命名资源集。
*
*
*
* 通常,该接口的实现还将实现生命周期,这样当调用start()和stop()方法时,所有定义的服务也会启动或停止。
*
*
*
* 在这两者之间,实现必须打开端口属性指定的端口号上的服务器套接字。当接受连接时,读取第一行并与指定的关闭命令进行比较。如果命令匹配,则启动服务器关闭。
* </p>
*
* @author
* @date 2018年12月17日 下午8:04:51
* @version 1.0
*/ public interface Server { // ------------------------------------------------------------- Properties /**
*
*
* <p>
* Title: getInfo
* </p>
*
* @date 2018年12月17日 下午8:05:15
*
* <p>
* 功能描述:返回该类的实现信息
* </p>
*
* @return
*/
public String getInfo(); /**
* 返回全局命名资源。
*/
public NamingResources getGlobalNamingResources(); /**
*
* 设置全局命名资源
*
* @param namingResources
* 新的全局命名资源
*/
public void setGlobalNamingResources(NamingResources globalNamingResources); /**
*
*
* <p>
* Title: getPort
* </p>
*
* @date 2018年12月17日 下午8:06:42
*
* <p>
* 功能描述:返回我们用来监听关闭命令的端口号
* </p>
*
* @return
*/
public int getPort(); /**
*
*
* <p>
* Title: setPort
* </p>
*
* @date 2018年12月17日 下午8:07:27
*
* <p>
* 功能描述:设置我们用来监听关闭命令的端口号
* </p>
*
* @param port
* 新的监听端口号
*/
public void setPort(int port); /**
* 返回我们正在等待的关闭命令字符串。
*
*
*/
public String getShutdown(); /**
* 设置我们正在等待的关闭命令字符串
*
* @param shutdown
* 新的关闭命令字符串
*/
public void setShutdown(String shutdown); // --------------------------------------------------------- Public Methods /**
* 向定义的服务集添加新服务。
*
* @param service
* 要被添加的新服务
*/
public void addService(Service service); /**
* 等待直到收到正确的关闭命令,然后返回。
*/
public void await(); /**
*
*
* <p>
* Title: findService
* </p>
*
* @date 2018年12月17日 下午8:10:42
*
* <p>
* 功能描述:返回指定的服务(如果存在);否则返回<code>null</code>
* </p>
*
* @param name
* @return
*/
public Service findService(String name); /**
*
*
* <p>
* Title: findServices
* </p>
*
* @date 2018年12月17日 下午8:11:18
*
* <p>
* 功能描述:返回在此服务器中定义的服务集。
* </p>
*
* @return
*/
public Service[] findServices(); /**
*
*
* <p>
* Title: removeService
* </p>
*
* @date 2018年12月17日 下午8:12:02
*
* <p>
* 功能描述:从该服务器 定义的服务集中删除指定的服务
* </p>
*
* @param service
*/
public void removeService(Service service); /**
*
*
* <p>
* Title: initialize
* </p>
*
* @date 2018年12月17日 下午8:12:58
*
* <p>
* 功能描述:调用启动前的初始化。这用于允许连接器绑定到Unix操作环境中的受限端口。当然只要是在系统执行前 你爱写啥写啥
* </p>
*
* @throws LifecycleException
*/
public void initialize() throws LifecycleException;
}
shutdown属性保存了必须发送给Server实例用来关闭整个系统的关闭命令,port属性定义了服务器组件会从哪一个端口获取关闭命令,可以调用其addService方法为服务器组件添加服务组件,或者通过removeService方法来删除某个服务组件,findService方法返回添加到此服务器组件中的服务集合,initialize方法包含了在系统启动之前要执行的一些代码
在Catalina中 server同样有其标准实现类,StandardServer
StandardServer类
org.apahce.catalina.core.StandardServer类是Server接口的标准实现,为什么要说下这个类,主要是对其中的关闭机制 也就上文提到的一个关闭所有组件的机制感兴趣,而这也是这个类最重要的特性,该类中的许多方法都与新server.xml文件中的服务器配置的存储相关,但这些并不是下面要说的重点,
一个服务器组件可以有0个或者多个服务组件,StandardServer类提供了addService方法、removeService方法、和findService方法的实现。
StandardServer类有四个与生命周期相关的方法,分别是initialize方法、start方法、stop方法,就像其他组件一样,可以初始化并且启动服务器组件。也可以调用 await方法 和 stop方法,调用await方法会一直阻塞住,直到它从定义 的 8005;端口(也可以自己配置其他端口)上接收到关闭命令,当await方法返回的时候,会运行stop方法来关闭其下的所有服务组件。
下面会分别讨论上面的 四个与生命周期相关的方法。
initialize方法
方法主要是用于初始化添加到StandardServer的服务组件,
/**
* 调用启动前的初始化。
*/
public void initialize() throws LifecycleException {
if (initialized)
throw new LifecycleException(sm.getString("standardServer.initialize.initialized"));
initialized = true; // 初始化我们定义的服务集中的每一个服务
for (int i = 0; i < services.length; i++) {
services[i].initialize();
}
}
start方法
start方法用于启动服务器组件,在StandardServer类的start方法的实现中,它会启动器所有的服务组件,逐个启动所有的组件,如连接器组件 和 Servlet容器,
/**
* 该服务器组件的启动方法,它会启动器所有的服务组件,逐个启动所有的组件,
*/ public void start() throws LifecycleException { // 验证和更新当前组件状态 如果已经启动直接报错
if (started)
throw new LifecycleException(sm.getString("standardServer.start.started"));
// 向监听器发送 BEFORE_START_EVENT事件 与 START_EVENT事件
lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null); lifecycle.fireLifecycleEvent(START_EVENT, null);
started = true; // 逐个启动当前定义的服务集合中的每一个服务
synchronized (services) {
for (int i = 0; i < services.length; i++) {
if (services[i] instanceof Lifecycle)
((Lifecycle) services[i]).start();
}
} // 向监听器发送 AFTER_START_EVENT事件
lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null); }
StandardServer为了防止服务器组件重复启动,在start中设置为true 在stop方法中 重置为false。
stop方法
stop方法用于关闭服务器组件,在方法中会关闭服务器中的额所有组件
/**
* 优雅地终止该组件的公共方法的主动使用。此方法应该是调用该组件的给定实例的最后一个方法。
* 它还应该向任何注册的监听器发送STOP_EVENT类型的停止事
*/
public void stop() throws LifecycleException { // 如果还没有启动则 直接抛出错误
if (!started)
throw new LifecycleException(sm.getString("standardServer.stop.notStarted")); // 向监听器发送监听事件
lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null); lifecycle.fireLifecycleEvent(STOP_EVENT, null);
// 将started标志 置为false 这样才可以再次启动
started = false; // 逐个停止我们定义服务集中的每一个服务
for (int i = 0; i < services.length; i++) {
if (services[i] instanceof Lifecycle)
((Lifecycle) services[i]).stop();
} // 发送监听事件
lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null); }
调用stop方法hi关闭所有的服务组件并重置布尔变量started,这样才可以再次启动服务器组件。
await方法
await方法负责等待关闭整个Tomcat部署的命令。
/**
* 等待直到收到正确的关闭命令,然后返回。
*/
public void await() { // 设置要等待的服务器套接字
ServerSocket serverSocket = null;
try {
// 利用咱们自己定义的port(默认 8005 )端口号来 初始化服务器套接字
serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
} catch (IOException e) {
System.err.println("StandardServer.await: create[" + port + "]: " + e);
e.printStackTrace();
System.exit(1);
} // 循环等待连接和有效命令
while (true) { // 等待下一个连接
Socket socket = null;
InputStream stream = null;
try {
socket = serverSocket.accept();//
socket.setSoTimeout(10 * 1000); // 接收到Socket之后 read方法仅在十秒钟之内有效
// 超时 将会抛出
// java.net.SocketTimeoutException
stream = socket.getInputStream();
} catch (AccessControlException ace) {
System.err.println("StandardServer.accept security exception: " + ace.getMessage());
continue;
} catch (IOException e) {
System.err.println("StandardServer.await: accept: " + e);
e.printStackTrace();
System.exit(1);
} // 从套接字中读取一组字符
StringBuffer command = new StringBuffer();
int expected = 1024; // 切断以避免DoS攻击
while (expected < shutdown.length()) {
if (random == null)
random = new Random(System.currentTimeMillis());
expected += (random.nextInt() % 1024);
}
while (expected > 0) {
int ch = -1;
try {
ch = stream.read();
} catch (IOException e) {
System.err.println("StandardServer.await: read: " + e);
e.printStackTrace();
ch = -1;
}
if (ch < 32) // 控制字符或EOF终止循环
break;
command.append((char) ch);
expected--;
} // 既然我们用完了,就把socket关上。
try {
socket.close();
} catch (IOException e) {
;
} // 匹配命令字符串
boolean match = command.toString().equals(shutdown);
if (match) {
//匹配到就 跳出循环
break;
} else
System.err.println("StandardServer.await: Invalid command '" + command.toString() + "' received"); } //关闭服务器套接字并返回
try {
serverSocket.close();
} catch (IOException e) {
;
} }
await方法创建一个ServerSocket对象,监听8005端口,并在while循环中调用它的accpect方法挡在指定端口上接收到消息时,才会从accept方法中返回一个socket,然后将接收到的消息与关闭命令字符串做比较,相同的话就跳出循环,关闭ServerSocke,否则再次循环。接续等待消息。
Service
服务组件是org.apche.catalina.Service接口的实例,一个服务组件可以有一个servlet容器和 一个或者多个连接器实例,可以自由的把连接器实例添加到服务组件中,所有的连接器都会与这个servlet容器相关联,
package org.apache.catalina; /**
*
* <p>
* <b>Title:Service.java</b>
* </p>
* <p>
* Copyright:ChenDong 2018
* </p>
* <p>
* Company:仅学习时使用
* </p>
*
*
* <p>
* 服务组件是org.apache.catalina.Service接口的实例,一个服务组件可以有一个servlet容器和
* 多个连接器实例,可以自由的把连接器实例添加到服务组件中的,所有的 连接器都会与这个servlet容器相关联
* </p>
*
* @author
* @date 2018年12月17日 下午8:02:49
* @version 1.0
*/ public interface Service { // ------------------------------------------------------------- Properties /**
*
*
* <p>
* Title: getContainer
* </p>
*
* @date 2018年12月19日 下午7:47:23
*
* <p>
* 功能描述: 返回处理与此服务关联的所有<code>Connectors</code> 请求的servlet容器。
* </p>
*
* @return
*/
public Container getContainer(); /**
*
*
* <p>
* Title: setContainer
* </p>
*
* @date 2018年12月19日 下午7:48:10
*
* <p>
* 功能描述:设置处理此服务中所有连接器的servlet容器
* </p>
*
* @param container
* 被设置的servlet容器
*/
public void setContainer(Container container); /**
*
*
* <p>
* Title: getInfo
* </p>
*
* @date 2018年12月19日 下午7:49:07
*
* <p>
* 功能描述:返回该类的实现信息
* </p>
*
* @return
*/
public String getInfo(); /**
* 返回此服务的名称.
*/
public String getName(); /**
* 设置此服务的名称
*
* @param name
* 新的服务名
*/
public void setName(String name); /**
* Return the <code>Server</code> with which we are associated (if any).
*/
/**
*
*
* <p>
* Title: getServer
* </p>
*
* @date 2018年12月19日 下午7:51:23
*
* <p>
* 功能描述:返回与该服务关联的服务器组件
* </p>
*
* @return
*/
public Server getServer(); /**
*
*
* <p>
* Title: setServer
* </p>
*
* @date 2018年12月19日 下午7:52:01
*
* <p>
* 功能描述: 设置拥有该服务的服务器组件
* </p>
*
* @param server
* 与该服务关联的服务器组件
*/
public void setServer(Server server); // --------------------------------------------------------- Public Methods /**
*
*
* <p>
* Title: addConnector
* </p>
*
* @date 2018年12月19日 下午7:53:50
*
* <p>
* 功能描述:向当前定义的 连接器集合 中添加新的 连接器,并将其与该服务中唯一的一个Servlet容器相关联
* </p>
*
* @param connector
* 添加的连接器
*/
public void addConnector(Connector connector); /**
*
*
* <p>
* Title: findConnectors
* </p>
*
* @date 2018年12月19日 下午7:57:20
*
* <p>
* 功能描述:查找并返回该服务中定义的连连接器集合
* </p>
*
* @return
*/
public Connector[] findConnectors(); /**
* Remove the specified Connector from the set associated from this Service.
* The removed Connector will also be disassociated from our Container.
*
* @param connector
* The Connector to be removed
*/
/**
*
*
* <p>
* Title: removeConnector
* </p>
*
* @date 2018年12月19日 下午8:00:09
*
* <p>
* 功能描述:从此服务中的连接器集合中移除指定的连接器,并将其与服务中唯一的一个servlet容器分离
* </p>
*
* @param connector
*/
public void removeConnector(Connector connector); /**
*
*
* <p>
* Title: initialize
* </p>
*
* @date 2018年12月19日 下午8:01:45
*
* <p>
* 功能描述:初始化方法
* </p>
*
* @throws LifecycleException
*/
public void initialize() throws LifecycleException; }
在Catalina中,StandardService类是其标准实现
StandardSetvice
org.apahce.catalina.core.StandardService类是 服务组件Service接口的标准实现,StandardService类的initialize方法用于初始化添加到其中所有的连接器,此外,StandardService类还实现了Service以及org.apache.catalina.Lifecycle声明周期接口,然后,它的start方法也可以启动连接器和所有的servlet容器。
connector 和 container
StandardService类有两种组件,分别是连接器和 servlet容器,其中servlet容器只能有一个,而连接器可以有多个,多个连接器使Tomcat可以为多种不同的请求协议提供服务,例如,一个连接器处理HTTP请求,而另外一个连接器处理HTTPS请求。
StandardService类使用变量Container 来指向一个Container接口的实例,使用数组connectors来保存所有连接器的引用。
/**
* 保存服务中所有连接器引用的数组
*/
private Connector connectors[] = new Connector[0]; /**
* 保存服务中唯一的一个Servlet容器
*/
private Container container = null;
需要调用setContainer方法将servlet容器与服务组件相关联,
/**
*
* 设置处理此服务中所有连接器的servlet容器
*
* @param container
* 新的servlet容器
*/
public void setContainer(Container container) {
// 取出当前服务中唯一的servlet容器
Container oldContainer = this.container;
// 如果取出的servlet容器不为空 且为Engine级别的servlet容器
if ((oldContainer != null) && (oldContainer instanceof Engine))
// 将调用其setSevice方法 断掉与服务的关联
((Engine) oldContainer).setService(null);
// 设置新的servlet容器
this.container = container;
// 如果新的servlet容器 不为空 且 是Engine级别的容器
if ((this.container != null) && (this.container instanceof Engine))
// 将该服务实例传入容器 与之关联
((Engine) this.container).setService(this);
// 如果该服务已经启动 且 服务中的容器实例不为空 且 这个容器实例是一个生命周期的实现类
if (started && (this.container != null) && (this.container instanceof Lifecycle)) {
try {
// 调用其start方法
((Lifecycle) this.container).start();
} catch (LifecycleException e) {
;
}
}
// 逐个调用连接器集合中的每一个连接器的setContainer方法 与服务中 的容器相挂关联
synchronized (connectors) {
for (int i = 0; i < connectors.length; i++)
connectors[i].setContainer(this.container);
}
// 如果服务已经启动且旧的容器不为空 且是一个生命周期实现 则 调用旧容器的stop方法
if (started && (oldContainer != null) && (oldContainer instanceof Lifecycle)) {
try {
((Lifecycle) oldContainer).stop();
} catch (LifecycleException e) {
;
}
} // 通知属性变更监听器
support.firePropertyChange("container", oldContainer, this.container); }
与服务组件相关联的servlet容器的实例将被传给每个连接器对象的setContainer方法中,这样在服务组件中就可以形成每个连接器和servlet容器之间的关联关系。
可以调用addConnector方法将连接器添加到服组件中,调用removeConnectoe方法将某个连接器移除,
/**
*
*
*
* 添加一个新的连接器到连接器集合中 并且将之与服务中的servlet容器相关联
*
* @param connector
* 要被添加连接器
*/ public void addConnector(Connector connector) { synchronized (connectors) {
// 添加
connector.setContainer(this.container);
connector.setService(this);
Connector results[] = new Connector[connectors.length + 1];
System.arraycopy(connectors, 0, results, 0, connectors.length);
results[connectors.length] = connector;
connectors = results; // 若该服务已经初始化过了
if (initialized) {
try {
// 则初始化这个新加的连接器
connector.initialize();
} catch (LifecycleException e) {
e.printStackTrace(System.err);
}
} // 若服务已经被启动则将新添加的连接器启动
if (started && (connector instanceof Lifecycle)) {
try {
((Lifecycle) connector).start();
} catch (LifecycleException e) {
;
}
} // Report this property change to interested listeners
support.firePropertyChange("connector", null, connector);
} }
removeConnector
/**
* 从该服务组件的连接器集合中移除指定的连接器,
*
* @param connector
* 被指定移除的连接器
*/
public void removeConnector(Connector connector) { synchronized (connectors) {
// 被移除的连接器在集合中的索引位置
int j = -1;
for (int i = 0; i < connectors.length; i++) {
// 找到被移除的连接器在数组中的索引之后 赋值给j 并跳出循环
if (connector == connectors[i]) {
j = i;
break;
}
}
// 如果j小于0 说明 被指定要移除的连接器 并不存在于连接器数组中
if (j < 0)
return;
// 如果服务已经被启动过了,且被删除的连接器 又是一个声明周期接口的实现
if (started && (connectors[j] instanceof Lifecycle)) {
try {
// 调用被删除连接器的stop方法
((Lifecycle) connectors[j]).stop();
} catch (LifecycleException e) {
;
}
}
// 并向被被删除的连接器setContainer方法中传入null,清除与该服务中唯一的servlet容器的关联关系
connectors[j].setContainer(null);
// 去掉与该服务的关联关系
connector.setService(null);
int k = 0;
// 重新组合新的数组 去掉 被删除的连接器
Connector results[] = new Connector[connectors.length - 1];
for (int i = 0; i < connectors.length; i++) {
if (i != j)
results[k++] = connectors[i];
}
connectors = results; // 并触发属性改变监听器
support.firePropertyChange("connector", connector, null);
} }
与生命周期有关的方法
与生命周期有关的方法包括从Lifecycle接口中实现的start和stop方法,在加上initialize方法,其中initialize方法会调用该服务组件中所有连接器的initialize方法,
/**
* 该服务的初始化方法 将会调用该服务组件中连接器集合中的每一个连接器的初始化方法
*/
public void initialize() throws LifecycleException {
if (initialized)
throw new LifecycleException(sm.getString("standardService.initialize.initialized"));
initialized = true; //逐个调用连接器集合中的每个连接器的初始化方法
synchronized (connectors) {
for (int i = 0; i < connectors.length; i++) {
connectors[i].initialize();
}
}
}
start方法负责启动被添加到该服务组件中的连接器和 servlet容器,
/**
* 该服务组件的声明周期接口 开始方法的实现,其内部会将调用该 服务中的所有连接器和 servlet容器的start方法 并通知相关监听事件
*/
public void start() throws LifecycleException { // 这个组件是否已经被启动过了
if (started) {
throw new LifecycleException(sm.getString("standardService.start.started"));
} // 向声明周期监听器发送 BEFORE_START_EVENT事件
lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null); System.out.println(sm.getString("standardService.start.name", this.name));
// 向生命周期监听器 发送START_EVENT事件
lifecycle.fireLifecycleEvent(START_EVENT, null);
// 将启动标志置为 true 表示已经启动
started = true; // 首先启动服务组件中定义的唯一的一个servlet容器
if (container != null) {
synchronized (container) {
if (container instanceof Lifecycle) {
((Lifecycle) container).start();
}
}
} // 第二步 启动我们定义的连接器集合中的每一个连接器
synchronized (connectors) {
for (int i = 0; i < connectors.length; i++) {
if (connectors[i] instanceof Lifecycle)
((Lifecycle) connectors[i]).start();
}
} // 向生命周期监听器发送AFTER_START_EVENT事件
lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null); }
stop方法 用于关闭与该服务组件相关联的servlet容器和所有的连接器,
/**
* 生命周期接口的停止方法的实现,会将该服务中唯一一个 servlet容器 和 被添加到该服务组件中的连接器 关闭
*/
public void stop() throws LifecycleException { // 验证状态
if (!started) {
throw new LifecycleException(sm.getString("standardService.stop.notStarted"));
} // 向生命周期监听器发送 BEFORE_STOP_EVENT事件
lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null); // 向生命周期监听器STOP_EVENT 事件
lifecycle.fireLifecycleEvent(STOP_EVENT, null); System.out.println(sm.getString("standardService.stop.name", this.name));
// 将启动标志置为 false 这样才可以继续启动
started = false; // 第一步 停止服务组件中的所有连接器
synchronized (connectors) {
for (int i = 0; i < connectors.length; i++) {
if (connectors[i] instanceof Lifecycle)
((Lifecycle) connectors[i]).stop();
}
} // 第二步 停止服务组件中唯一一个servlet容器
if (container != null) {
synchronized (container) {
if (container instanceof Lifecycle) {
((Lifecycle) container).stop();
}
}
} // 向声明周期监听器 发送 AFTER_STOP_EVENT事件
lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null); }
那上面就大概的将 Tomcat中的服务和 服务器组件都大致的展示了一下,下面我们来写一个简单的应用程序来实际操作一下。
下面的 应用程序重在说明如何使用服务器组件 和 服务组件,特别是在StandardServer中如何利用启动 和 关闭机制。
package myex14.pyrmont.startup; import org.apache.catalina.Connector;
import org.apache.catalina.Context;
import org.apache.catalina.Engine;
import org.apache.catalina.Host;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.Loader;
import org.apache.catalina.Server;
import org.apache.catalina.Service;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.http.HttpConnector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardEngine;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.core.StandardServer;
import org.apache.catalina.core.StandardService;
import org.apache.catalina.core.StandardWrapper;
import org.apache.catalina.loader.WebappLoader; import ex14.pyrmont.core.SimpleContextConfig; /**
* <p>
* <b>Title:Bootstrap.java</b>
* </p>
* <p>
* Copyright:ChenDong 2018
* </p>
* <p>
* Company:仅学习时使用
* </p>
* <p>
* 类功能描述:应用程序启动类,应用程序重在说明如何使用 服务器组件 和 服务组件,重点是 StandardServer中的开启和关闭机制
* </p>
*
* @author 陈东
* @date 2018年12月19日 下午9:33:03
* @version 1.0
*/
public class Bootstrap { /**
*
* <p>
* Title: main
* </p>
*
* @date 2018年12月19日 下午9:33:03
*
* <p>
* 功能描述:
* </p>
*
* @param args
*
*/
public static void main(String[] args) {
System.setProperty("catalina.base", System.getProperty("user.dir"));
// 初始化一个连接器
Connector connector = new HttpConnector(); // 初始化 对应servlet的Wrapper容器
Wrapper wrapper1 = new StandardWrapper();
wrapper1.setName("Primitive");
wrapper1.setServletClass("PrimitiveServlet"); Wrapper wrapper2 = new StandardWrapper();
wrapper2.setName("Modern");
wrapper2.setServletClass("ModernServlet"); // 初始化一个Context容器
Context context = new StandardContext();
// 设置根路径
context.setPath("/app1");
// 设置根文件夹
context.setDocBase("app1");
// 添加子容器
context.addChild(wrapper1);
context.addChild(wrapper2); // 给StandardContext 创建一个 配置监听器
LifecycleListener listener = new SimpleContextConfig(); ((Lifecycle) context).addLifecycleListener(listener); // 初始化一个Host级别的容器
Host host = new StandardHost();
host.addChild(context);
host.setName("localhost");
host.setAppBase("webapps"); // 初始化一个加载器
Loader loader = new WebappLoader();
context.setLoader(loader); // 添加servletMapping映射
context.addServletMapping("/Primitive", "Primitive");
context.addServletMapping("/Modern", "Modern"); // 初始化一个Engine级别的容器
Engine engine = new StandardEngine();
// 添加孩子host
engine.addChild(host);
// 根据 host的name属性设置默认的Host
engine.setDefaultHost("localhost"); // -----------------------重点来了
// 初始化一个Service
Service servie = new StandardService();
servie.setName("stand-alone Service");
// 初始化一个Server
Server server = new StandardServer();
server.addService(servie);
servie.addConnector(connector);
servie.setContainer(engine); // 启动服务器组件
if (server instanceof Lifecycle) {
try {
server.initialize();
((Lifecycle) server).start();
//启动监听关闭命令的端口开始监听关闭指令,进入循环等待
//此时连接器已经处于运行状态
//在await方法没有接到关闭命令之前 是不回复返回的 一旦接到关闭命令则 会执行下面的stop反方
server.await();
} catch (Exception e) {
e.printStackTrace();
}
} //关闭服务器组件
if(server instanceof Lifecycle){
try {
((Lifecycle)server).stop(); } catch (Exception e) {
// TODO: handle exception
} } } }
Stopper类
Stopper类提供了一种 优雅的方式来关闭Catalina服务器,它保证了所有生命周期组件的stop方法都能够调用
package myex14.pyrmont.startup; import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket; /**
* <p>
* <b>Title:Stopper.java</b>
* </p>
* <p>
* Copyright:ChenDong 2018
* </p>
* <p>
* Company:仅学习时使用
* </p>
* <p>
* 类功能描述:目的是向Server负责监听关闭命令的端口发送 关闭命令
* </p>
*
* @author 陈东
* @date 2018年12月19日 下午10:02:37
* @version 1.0
*/
public class Stopper { /**
*
* <p>
* Title: main
* </p>
*
* @date 2018年12月19日 下午10:02:37
*
* <p>
* 功能描述:
* </p>
*
* @param args
*
*/
public static void main(String[] args) {
int port = 8005;
String shutdown = "SHUTDOWN";
try {
Socket socket = new Socket("127.0.0.1", port); OutputStream stream = socket.getOutputStream(); for(int i=0;i<shutdown.length();i++)
stream.write(shutdown.charAt(i)); stream.flush();
stream.close();
socket.close();
System.out.println("这个服务已经成功关闭"); } catch (IOException e) {
System.out.println("这个服务还没有启动");
} } }
当然了 这个Stopper类 只是一个简单的例子 真正部署的时候 不会像现在这样的简单 ,只要是想关闭服务器 其实就是自己 创建了一个 连接到 指定ip指定端口的 套接字实例,并且使用该套接字,直接发送 关闭命令
Tomcat中的服务器组件和 服务组件的更多相关文章
- How tomcat works 读书笔记十四 服务器组件和服务组件
之前的项目还是有些问题的,例如 1 只能有一个连接器,只能处理http请求,无法添加另外一个连接器用来处理https. 2 对容器的关闭只能是粗暴的关闭Bootstrap. 服务器组件 org.apa ...
- Tomcat 中部署 web 应用 ---- Dubbo 服务消费者 Web 应用 war 包的部署
使用Maven构建Dubbo服务的可执行jar包 Dubbo服务的运行方式: 1.使用Servlet容器运行(Tomcat.Jetty等)----不可取 缺点:增加复杂性(端口.管理) 浪费资源(内存 ...
- Tomcat中部署web应用 ---- Dubbo服务消费者Web应用war包的部署
样例视频:http://www.roncoo.com/course/view/f614343765bc4aac8597c6d8b38f06fd IP: 192.168.2.61 部署容器:apache ...
- 【JVM】linux上tomcat中部署的web服务,时好时坏,莫名其妙宕机,报错:There is insufficient memory for the Java Runtime Environment to continue.
=========================================================================================== 环境: linu ...
- Tomcat系列之服务器的安装与配置以及各组件详解
Tomcat系列之服务器的安装与配置以及各组件详解 大纲 一.前言 二.安装与配置Tomcat 三.Tomcat 目录的结构 四.Tomcat 配置文件 注,本文的测试的操作系统为CentOS 6.4 ...
- Tomcat中日志组件
Tomcat日志组件 AccessLog接口 public interface AccessLog { public void log(Request request, Response respon ...
- SpringCloudAlibaba 微服务组件 Nacos 之配置中心源码深度解析
大家好,这篇文章跟大家聊下 SpringCloudAlibaba 中的微服务组件 Nacos.Nacos 既能做注册中心,又能做配置中心,这篇文章主要来聊下做配置中心时 client 端的一些设计,主 ...
- Tomcat结构、启动过程、关键组件简单分析
Tomcat 结构: Tomcat最顶层容器叫Server,代表整个服务器,Server中包含至少一个Service,用于具体提供服务,Serv ...
- Unity3d&C#分布式游戏服务器ET框架介绍-组件式设计
前几天写了<开源分享 Unity3d客户端与C#分布式服务端游戏框架>,受到很多人关注,QQ群几天就加了80多个人.开源这个框架的主要目的也是分享自己设计ET的一些想法,所以我准备写一系列 ...
随机推荐
- WinPEter制作U盘启动盘
一.图说WinPE启动U盘的制作 1.首先将U盘插入电脑的USB接口(重要提示:制作过程U盘会被格式化,注意备份资料): 2.解压下载的WinPEU.rar文件: 3.在WinPEU.rar解压目录打 ...
- tp5 模型关联,多表联查实用方法
1.模型中建立关联关系 public function goods(){ return $this->belongsTo('app\common\model\goods\Goods', 'goo ...
- start-20180323
几年前申请了博客,http://www.cnblogs.com/cdfive/,一篇文章没写-_-|| 账号都忘了orz.. 又到了离职的时候,开始重新找工作: 昨天一家平台好的单位面试没过,可能是跳 ...
- mac安装phpmysql
1.百度搜“phpmadmin”,还是一样,第二个因为是PC版本,不能用,点击第一个连接,去phpmyadmin的官网. 2.下载完毕后,进入到下载文件保存目录,双击压缩包,压缩包则会自动解压. 3. ...
- OpenCV中出现“Microsoft C++ 异常: cv::Exception,位于内存位置 0x0000005C8ECFFA80 处。”的异常
对于OpenCV的安装 要感谢网友空晴拜小白提供的教程 链接如下: https://blog.csdn.net/sinat_36264666/article/details/73135823?ref= ...
- 小D课堂 - 新版本微服务springcloud+Docker教程_4-03 高级篇幅之Ribbon负载均衡源码分析实战
笔记 3.高级篇幅之Ribbon负载均衡源码分析实战 简介: 讲解ribbon服务间调用负载均衡源码分析 1.完善下单接口 2.分析@LoadBalanced ...
- 一百零七:CMS系统之权限和角色模型定义
模型与权限关系映射表 class CMSPersmission: """ 权限管理映射 """ # 255的二进制方式来表示 1111 11 ...
- Spring Boot启动的报错 Stopping service [Tomcat]
我遇到的问题是项目中使用java_websocket的WebSocketClient,由于性能要求,需要再Controller直接继承WebSocketClient, 在项目启动过程中调试进入spri ...
- docker命令小结
文档:docker命令小结.note链接:http://note.youdao.com/noteshare?id=54015b76db9ae764182cb921e348b7fc&sub=DD ...
- linux的vm.overcommit_memory的内存分配参数详解
公司的redis有时background save db不成功,通过log发现下面的告警,很可能由它引起的: [13223] 17 Mar 13:18:02.207 # WARNING overcom ...