使用监控技术实现tomcat的按需要进行重启

    我们经常需要碰到这种情况,当用户提出一个问题,并且我们已经修改之后,已经由自动更新系统更新到客户方,但这时需要重新启动tomcat才能使新修改的代码工作。这时候,就要想办法来重新启动这个tomcat了。
    有的同学提到一个使用tomcat控制台重新部署项目的想法,但是通过这种方法,有时候并不能有效地进行部署,因为可能存在非守护进程不能完全退出,以及有些资源未能正确回收的问题。相比来说,直接重启tomcat来得更方便一些。

    在前面已经说到,实现项目的自动更新,是利用embeded tomcat来进行重新启动来完成的,这中间实现tomcat在指定时间更新并重启的功能是使用类似监听技术来实现的。即在客户机器上,同时部署了两个组件,一个为运行我们项目的tomcat,另一个则为监控项目代码的java监听器。此监听器用于在指定时间监听项目代码是否有更新,如果有更新,则使用svn进行更新,并重新启动tomcat。相应的代码如下所示:

	private void updateAndRestart() throws Exception {
		logger.log(Level.INFO, "准备执行更新线程......");
		if(hasUpdate(svn)) {
			logger.log(Level.INFO, "发现更新,准备重新启动tomcat......");
			shutdownTomcat();
			logger.log(Level.INFO, "成功关闭tomcat,正在重起");
			startTomcat();
			logger.log(Level.INFO, "重起tomcat成功......");
		} else
			logger.log(Level.INFO, "无有效更新......");
	}

    这样的代码,即实现了tomcat的更新和重启操作,惟一不足的是这段代码并不能接收来自于网页上的命令实现按需要重启,它只能在规定的时间(如每天24点)进行操作。
    有的同学,想到如果将这个监听器做成一个类似项目的服务并启动, 即可以实现效果了。但实际的问题时,对于项目来说,通过路由器打开太多的端口是有害而无利了。我们已经打开了80端口用于项目访问,最好不要再打开其它端口了。

    本文描述了一个在项目代码中增加用于实现重启的功能代码,并通过socket请求访问监听器并通过监听器来重启项目tomcat的实现。

    实现原理如下:

  1.     监听器监听指定端口,进行监听操作
  2.     由项目向外提供重启功能界面,通过功能界面进行操作
  3.     项目的重启功能向监听器指定端口发送重启指令
  4.     监听器接收到相应socket请求,即执行重启操作

监听器监听

    在监听器启动时,需要通过监听一个指定的端口来描述当前的应用,以便于判断当前监听器是否在工作或者通过此端口在寻找到当前正在工作的监听器等(具体应用可google之,其中tomcat的shutdown操作即基于此原理)。在现有的实现中,此监听未有任何运用,那么,我们可以在此操作上提供扩展,以进行监听重启指令。如下所示:

	private void startListener() throws Exception {
		ServerSocket serverSocket = new ServerSocket(listenPort, 1, InetAddress.getByName("localhost"));
		while(true) {
			Socket socket = serverSocket.accept();
			try {
				handleRestartTomcat(socket);
			} catch(Exception ignore) {
			}
		}
	}

监听器重启
    具体的实现重启的监听操作代码:

	private void handleRestartTomcat(Socket socket) throws Exception{
		InputStream inputStream = socket.getInputStream();
		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
		IOUtils.copy(inputStream, byteArrayOutputStream);
		socket.close();
		IOUtils.closeQuietly(byteArrayOutputStream);
		String command = new String(byteArrayOutputStream.toByteArray(), "UTF-8");
		if(RESTART_COMMAND.equals(command)) {
			shutdownTomcat();
			startTomcat();
		}
	}

    以上代码,即实现相应的重启命令读取以及操作,即通过读取相应socket请求传递过来的命令,如命令即如同指定的命令(如restart),即开始调用关闭tomcat操作,以及启动tomcat的操作,这样即实现了一个重启tomcat的操作。

    项目提供重启功能界面
    由于监听器不能独立向外提供功能界面以及端口访问,所以相应的操作只能衍生到项目这边来,项目本身通过端口(如80,8080)向外提供服务,所以可以通过此服务向外提供重启功能界面,当指定用户(如sysUser)执行此功能时,即实现向监听器发送重启命令。实现的代码如下所示:

	public String restart() throws Exception {
		//寻找监听端口
		Class<?> tomcatStartupConstantClass;
		......
		//监听端口
		Field listenPortField = tomcatStartupConstantClass.getDeclaredField("LISTEN_PORT");
		int listenPort = listenPortField.getInt(null);
		//主机地址
		Field hostField = tomcatStartupConstantClass.getDeclaredField("HOST");
		String host = hostField.get(null).toString();
		//开始发送相关请求信息
		......
		Socket socket = new Socket(host, listenPort);
		OutputStream outputStream = socket.getOutputStream();
		outputStream.write("restart".getBytes("UTF-8"));
		outputStream.close();
		addActionMessage("操作成功");
		return SUCCESS;
	}

    以上代码,即实现了发送重启命令。接下来就该由监听器来启动当前项目了,这个有点像借别人的刀来把自己给杀了的味道……
    当然,以上代码就是一个纯粹的struts2的处理方法,所以可以直接将此方法放到struts2的action中。通过权限判断,以将此功能只能由指定的用户调用,以避免无谓的操作。

转载请标明出处:i flym
本文地址:https://www.iflym.com/index.php/code/use-monitor-to-implement-tomcat-restart.html

相关文章:

作者: flym

I am flym,the master of the site:)

发表评论

电子邮件地址不会被公开。 必填项已用*标注