如何正确关闭游戏服务器

一,如何正确的关闭游戏服务器java

1,最简单粗爆的方法linux

在Linux系统上,使用ps -aux|grep java 能够查到全部运行的java程序的pid,即进程号,而后使用kill - 9 进程号,杀死一个进程。数据库

这样作虽然简单快速,可是会有一个问题,若是咱们运行的服务器有缓存的数据,尚未来得及进行持久化存储,那么这样操做,内存中的数据就会丢失。kill - 9是一个必杀命令,无论进程处于什么状态,都是杀无赦,它不会给进程留下任何善后的机会。那么该如何正确的关闭游戏服务器吧?缓存

2,优雅的关闭进程bash

优雅的关闭进程,就是在收到关闭进程的命令后,进程进行一些数据处理,好比:服务器

1,再也不接收链接并发

2,再也不接收数据ide

3,把未持久化的数据进行持久化函数

4,清理一些临时文件等this

5,执行一些已经提交到线程池中但未执行的任务

  3,Java进程如何接收进程中止命令

1,JVM 关闭钩子

关闭钩子本质上是一个线程(也称为Hook线程),用来监听JVM的关闭。经过使用Runtime的addShutdownHook(Thread hook)能够向JVM注册一个关闭钩子。Hook线程在JVM 正常关闭才会执行,在强制关闭时不会执行。

这个钩子能够在一下几种场景中被调用:

1. 程序正常退出

2. 使用System.exit()

3. 终端使用Ctrl+C触发的中断

4. 系统关闭

5. OutOfMemory宕机

6. 使用Kill pid命令干掉进程(注:在使用kill -9 pid时,是不会被调用的)

对于一个JVM中注册的多个关闭钩子它们将会并发执行,因此JVM并不能保证它的执行顺行。当全部的Hook线程执行完毕后,若是此时runFinalizersOnExit为true,那么JVM将先运行终结器,而后中止。Hook线程会延迟JVM的关闭时间,这就要求在编写钩子过程当中必需要尽量的减小Hook线程的执行时间。另外因为多个钩子是并发执行的,那么极可能由于代码不当致使出现竞态条件或死锁等问题,为了不该问题,强烈建议在一个钩子中执行一系列操做。

 

另外在使用关闭钩子还要注意如下几点:

1. 不能在钩子调用System.exit(),不然卡住JVM的关闭过程,可是能够调用Runtime.halt()。

2. 不能再钩子中再进行钩子的添加和删掉操做,不然将会抛出IllegalStateException。

3. 在System.exit()以后添加的钩子无效。

4. 当JVM收到SIGTERM命令(好比操做系统在关闭时)后,若是钩子线程在必定时间没有完成,那么Hook线程可能在执行过程当中被终止。

5. Hool线程中一样会抛出异常,若是抛出异常又不处理,那么钩子的执行序列就会被中止。

 

下面是一个简单的示例:

 

public class T {

@SuppressWarnings("deprecation")

public static void main(String[] args) throws Exception {

//启用退出JVM时执行Finalizer

Runtime.runFinalizersOnExit(true);

MyHook hook1 = new MyHook("Hook1");

MyHook hook2 = new MyHook("Hook2");

MyHook hook3 = new MyHook("Hook3");

 

//注册关闭钩子

Runtime.getRuntime().addShutdownHook(hook1);

Runtime.getRuntime().addShutdownHook(hook2);

Runtime.getRuntime().addShutdownHook(hook3);

 

//移除关闭钩子

Runtime.getRuntime().removeShutdownHook(hook3);

 

//Main线程将在执行这句以后退出

System.out.println("Main Thread Ends.");

}

}

 

class MyHook extends Thread {

private String name;

public MyHook (String name) {

this.name = name;

setName(name);

}

public void run() {

System.out.println(name + " Ends.");

}

//重写Finalizer,将在关闭钩子后调用

protected void finalize() throws Throwable {

System.out.println(name + " Finalize.");

}

}

 

和(可能的)执行结果(由于JVM不保证关闭钩子的调用顺序,所以结果中的第2、三行可能出现相反的顺序):

 

Main Thread Ends.

Hook2 Ends.

Hook1 Ends.

Hook3 Finalize.

Hook2 Finalize.

Hook1 Finalize.

能够看到,main函数执行完成,首先输出的是Main Thread Ends,接下来执行关闭钩子,输出Hook2 Ends和Hook1 Ends。这两行也能够证明:JVM确实不是以注册的顺序来调用关闭钩子的。而因为hook3在调用了addShutdownHook后,接着对其调用了removeShutdownHook将其移除,因而hook3在JVM退出时没有执行,所以没有输出Hook3 Ends。

另外,因为MyHook类实现了finalize方法,而main函数中第一行又经过Runtime.runFinalizersOnExit(true)打开了退出JVM时执行Finalizer的开关,因而3个hook对象的finalize方法被调用,输出了3行Finalize。

注意,屡次调用addShutdownHook来注册同一个关闭钩子将会抛出IllegalArgumentException:

Exception in thread "main" java.lang.IllegalArgumentException: Hook previously registered

at java.lang.ApplicationShutdownHooks.add(ApplicationShutdownHooks.java:72)

at java.lang.Runtime.addShutdownHook(Runtime.java:211)

at T.main(T.java:12)

 

另外,从JavaDoc中得知:

 

   Once the shutdown sequence has begun it can be stopped only by invoking the halt method, which forcibly terminates the virtual machine.

   Once the shutdown sequence has begun it is impossible to register a new shutdown hook or de-register a previously-registered hook. Attempting either of these operations will cause an IllegalStateException to be thrown.

 

“一旦JVM关闭流程开始,就只能经过调用halt方法来中止该流程,也不可能再注册或移除关闭钩子了,这些操做将致使抛出IllegalStateException”。

 

若是在关闭钩子中关闭应用程序的公共的组件,如日志服务,或者数据库链接等,像下面这样:

 

Runtime.getRuntime().addShutdownHook(new Thread() {

public void run() {

try {

LogService.this.stop();

} catch (InterruptedException ignored){

//ignored

}

}

});

 

因为关闭钩子将并发执行,所以在关闭日志时可能致使其余须要日志服务的关闭钩子产生问题。为了不这种状况,可使关闭钩子不依赖那些可能被应用程序或其余关闭钩子关闭的服务。实现这种功能的一种方式是对全部服务使用同一个关闭钩子(而不是每一个服务使用一个不一样的关闭钩子),而且在该关闭钩子中执行一系列的关闭操做。这确保了关闭操做在单个线程中串行执行,从而避免了在关闭操做以前出现竞态条件或死锁等问题。

二,在游戏服务器中添加关闭钩子

 

public class ShutDownService {

private static CommonLog gameLogger = CommonLog.getInstance();

private static List<IShutDown> shutDownList = new ArrayList<>();

//注册须要在关闭钩子中执行的任务

public static void registShutDown(IShutDown shutDownServer) {

shutDownList.add(shutDownServer);

}

public static void startShutDownHook() {

 

Runtime.getRuntime().addShutdownHook(new Thread() {

@Override

public void run() {

gameLogger.warn(0, "开始关闭服务器,正在清理资源.....");

for (IShutDown shutDown : shutDownList) {

if (shutDown != null) {

shutDown.shutDown();

while (!shutDown.isTerminated()) {

 

}

gameLogger.warn(0, "----关闭" + shutDown.getClass().getName() + "成功----");

}

}

gameLogger.warn(0, "###---服务器关闭成功---###");

}

});

}

}

 

三,Linux脚本根据端口杀死一个进程

 

#!/bin/bash

echo "从新启动服务"

jar_name=GameLogicServer.jar

PROCESS=`ps -ef|grep ${jar_name} |grep -v grep|grep -v PPID|awk '{ print $2}'`

for i in $PROCESS

do

 echo "######Kill the ${jar_name} process [ $i ] ########"

 

 kill -15 $i

done

 

 

while true

 do

OLD_PROCESS=`ps -ef|grep ${jar_name} |grep -v grep|grep -v PPID|awk '{ print $2}'`

if [ "${OLD_PROCESS}" = "" ]

then

echo "${PROCESS}进程已杀死成功,开始启动新的进程"

break

else

echo "正在等待${PROCESS}进程关闭....."

sleep 1s

fi

 

 

 done

 

nohup java -server -agentpath:/usr/jprofiler9/jprofiler9/bin/linux-x64/libjprofilerti.so=port=8849,nowait -jar ${jar_name} > console.out 2>&1 &

echo "服务器启动完成"

更多游戏技术资料请参照:游戏技术网:http://www.youxijishu.com/

相关文章
相关标签/搜索