tomcatserver组件监听shutdown命令关闭服务器之源码分析

在tomcat 的server.xml文件中,顶层Server标签可以配置一个监听关闭服务器命令的端口,一旦接收到SHUTDOWN的请求后,则尝试关闭整个tomcat服务器。

渭滨网站制作公司哪家好,找创新互联!从网页设计、网站建设、微信开发、APP开发、响应式网站建设等网站项目制作,到程序开发,运营维护。创新互联2013年开创至今到现在10年的时间,我们拥有了丰富的建站经验和运维经验,来保证我们的工作的顺利进行。专注于网站建设就选创新互联。

具体配置如下:

<Server port="8005" shutdown="SHUTDOWN">

标识监听8005端口,如果接收到的请求为“SHUTDOWN”,那么会触发关闭服务器的操作。

为了实验这个端口好不好使,首先我把tomcat启动了起来,然后在浏览器发送了一个请求: http://localhost:8005/SHUTDOWN

结果tomcat后台日志为:

警告: StandardServer.await: Invalid command \'GET /SHUTDOWN HTTP/1.1\' received

并且服务器没有关闭。这次请求收到的内容为 GET /SHUTDOWN HTTP/1.1 而不是SHUTDOWN ,查看源码才发现,这里直接从监听的socket拿的数据,并没有做http请求解析,所以收到了一个http get请求的报文。要想正确发送SHUTDOWN,需要通过socket直接发送数据,使用jdk api的操作如下:

java.net.Socket socket = new java.net.Socket("localhost", 8005); socket.getOutputStream().write("SHUTDOWN".getBytes()); socket.getOutputStream().flush(); socket.getOutputStream().close(); socket.close();

这样tomcat服务器就能正确关闭了,日志如下:

七月 17, 2017 12:56:59 下午 org.apache.catalina.core.StandardServer await 信息: A valid shutdown command was received via the shutdown port. Stopping the Server instance. 七月 17, 2017 12:56:59 下午 org.apache.coyote.AbstractProtocol pause 信息: Pausing ProtocolHandler ["http-bio-8080"] 七月 17, 2017 12:56:59 下午 org.apache.coyote.AbstractProtocol pause 信息: Pausing ProtocolHandler ["ajp-bio-8009"] 七月 17, 2017 12:56:59 下午 org.apache.catalina.core.StandardService stopInternal 信息: Stopping service Catalina 七月 17, 2017 12:56:59 下午 org.apache.coyote.AbstractProtocol stop 信息: Stopping ProtocolHandler ["http-bio-8080"] 七月 17, 2017 12:56:59 下午 org.apache.coyote.AbstractProtocol stop 信息: Stopping ProtocolHandler ["ajp-bio-8009"] 七月 17, 2017 12:56:59 下午 org.apache.coyote.AbstractProtocol destroy 信息: Destroying ProtocolHandler ["http-bio-8080"] 七月 17, 2017 12:56:59 下午 org.apache.coyote.AbstractProtocol destroy 信息: Destroying ProtocolHandler ["ajp-bio-8009"]

下面看一下源码的实现:

首先Bootstrap类调用了Catalina的start()方法启动服务器,在start()方法最后,有如下代码:

/** * Catalina类 * Start a new server instance. */ public void start() { ... if (await) { await(); stop(); } }

变量await在启动时默认为true,await()方法如下:

/** * Catalina类 * Await and shutdown. */ public void await() { getServer().await(); }

Catalina类中的Server默认实现类为StandardServer,其await()方法如下:

//StandardServer类 //简化版源码 public void await() { //如果端口为-2或-1的处理方式 //指定了正常端口 //首先监听此端口 awaitSocket = new ServerSocket(port, 1, InetAddress.getByName(address)); // Loop waiting for a connection and a valid command //循环 while (!stopAwait) { ... //阻塞在这里 等待数据 socket = serverSocket.accept(); //接收到数据后往下执行 读取数据 // 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("StandardServer.await: read: ", e); ch = -1; } // Control character or EOF (-1) terminates loop if (ch < 32 || ch == 127) { break; } command.append((char) ch); expected--; } // Match against our command string boolean match = command.toString().equals(shutdown); //如果接收到的请求为"SHUTDOWN"或其他正确的自定义的字符串 if (match) { log.info(sm.getString("standardServer.shutdownViaPort")); //跳出while循环 await()方法结束,线程执行返回到Catalina类的start()方法,继续执行Catalina的stop()方法 break; } else log.warn("StandardServer.await: Invalid command \'" + command.toString() + "\' received");

下面看一下Catalina的stop()方法,此方法是真正触发tomcat服务器destroy生命周期的方法:

public void stop() { ... Server s = getServer(); LifecycleState state = s.getState(); if (LifecycleState.STOPPING_PREP.compareTo(state) <= 0 && LifecycleState.DESTROYED.compareTo(state) >= 0) { // Nothing to do. stop() was already called } else { s.stop(); s.destroy(); } }

s即StandardServer,首先判断s的状态,该状态是一个volatile变量,如果状态为正在销毁,则不进行任何操作;否则,调用s的stop()和destroy()方法。

stop()和destroy()方法为抽象类LifecycleBase的方法,tomcat中凡是拥有生命周期的组件均继承自此抽象类,并且这两个方法使用了 模板方法模式,方法内部实现了一部分更改组件状态的代码,之后调用了抽象方法stopInternal()和stopDestroy(),这俩方法的作用是留给子类去实现。

在StandardServer的stopInternal()方法中,会依次调用子组件Service的stop()方法:

@Override protected void stopInternal() throws LifecycleException { setState(LifecycleState.STOPPING); fireLifecycleEvent(CONFIGURE_STOP_EVENT, null); // Stop our defined Services for (int i = 0; i < services.length; i++) { services[i].stop(); } globalNamingResources.stop(); stopAwait(); }

StandardService的stopInternal()方法如下:

protected void stopInternal() throws LifecycleException { // Pause connectors first 首先停止connector再接收新的请求 synchronized (connectorsLock) { for (Connector connector: connectors) { connector.pause(); } } //设置状态 setState(LifecycleState.STOPPING); // Stop our defined Container second 关闭容器,顶层为StandardEngine if (container != null) { synchronized (container) { container.stop(); } } // Now stop the connectors 停止connector synchronized (connectorsLock) { for (Connector connector: connectors) { connector.stop(); } } synchronized (executors) { 关闭线程池 for (Executor executor: executors) { executor.stop(); } } }

之后,每层组件都是这样一步一步的向下调用,链式触发整个tomcat组件destory的生命周期,完成资源的释放和销毁,最终关闭服务器。

stop和destroy相似,只是工作不同,有兴趣的同志可以继续深入研究源码。

当前文章:tomcatserver组件监听shutdown命令关闭服务器之源码分析
转载源于:https://www.cdcxhl.com/article28/chojjp.html

成都网站建设公司_创新互联,为您提供软件开发网站策划定制开发网站排名面包屑导航外贸网站建设

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联