Linux优雅退出问题

问题:Springboot框架开发的项目中会内嵌tomcat容器,在杀死进程的时候tomcat为被正常杀死,导致端口未被释放,第二次启动的时候报端口冲突。

先讲一个基本概念:如何在shell中终止一个后台进程?

 kill的作用是向某个指定的进程或进程组发送指定信号,从而结束该进程/进程组。-s选项可以指定要发送的具体信号,如果没有指定,则默认发送SIGTERM(15)信号至指定进程/进程组,若进程没有捕获该信号的逻辑,则SIGTERM的作用是终止进程。

kill pid与kill -9 pid的区别
kill pid的作用是向进程号为pid的进程发送SIGTERM(这是kill默认发送的信号,信号值为15),该信号是一个结束进程的信号且可以被应用程序捕获。若应用程序没有捕获并响应该信号的逻辑代码,则该信号的默认动作是kill掉进程。这是终止指定进程的推荐做法。
kill -9 pid则是向进程号为pid的进程发送SIGKILL(该信号的编号为9),从本文上面的说明可知,SIGKILL既不能被应用程序捕获,也不能被阻塞或忽略,其动作是立即结束指定进程。通俗地说,应用程序根本无法“感知”SIGKILL信号,它在完全无准备的情况下,就被收到SIGKILL信号的操作系统给干掉了,显然,在这种“暴力”情况下,应用程序完全没有释放当前占用资源的机会。事实上,SIGKILL信号是直接发给init进程的,它收到该信号后,负责终止pid指定的进程。关于linux init进程的说明,可以参考这里这里。在某些情况下(如进程已经hang死,无法响应正常信号),就可以使用kill -9来结束进程。
若通过kill结束的进程是一个创建过子进程的父进程,则其子进程就会成为孤儿进程(Orphan Process),这种情况下,子进程的退出状态就不能再被应用进程捕获(因为作为父进程的应用程序已经不存在了),不过应该不会对整个linux系统产生什么不利影响。

stop() {
    echo "Stopping $serviceName"
    echo "Testing dir..."
    checkDirWritable
    if [[ $? -ne 0 ]]; then
      echo_failure;
      return 1
    fi
    [ ! -f $PID_FILE ] && {
      echo "Stop: not exist pid file, return directly"
        echo_failure;
        return 0
    }
    PID=`cat $PID_FILE`
    RETVAL=$?
    [ -z "$PID" ] && {
      echo "Stop fail: empty pid file"
        echo_failure; #empty pid value"
        return 1;
    }
    echo "Searching process with pid: $PID"
    ps -p "$PID" >/dev/null 2>&1
    if [ $? -eq 0 ]; then
      echo "PID($PID) exist, stopping process..."
        #用kill命令杀死进程=====================
        kill $PID >/dev/null 2>&1
        RETVAL=$?
        [ $RETVAL -eq 0 ] || {
          echo "Stop fail: could not kill process"
            echo_failure; # could not kill process
            return 2
        }
      echo "Stop exiting process success"
    else
      echo "Cannot find process with pid: $PID"
    fi
    
    rm -f $PID_FILE; # Remove control files
    rm -f $LOK_FILE
    echo_success
    return 0
}

上面也已经提到了:因为springboot项目中会内嵌tomcat容器  在kill进程的时候在被进程捕获后tomcat关闭需要一些时间,如果stop之后不休眠一定时间 有可能会导致tomcat未能正常关闭,导致第二次启动的时候报端口冲突,第二次有Pid但是进程没有成功启动。

所以我们在这里调整stop之后休眠10s在启动start

还有一种情况是:setsockopt中参数SO_REUSEADDR

一般来说,一个端口释放后会等待两分钟之后才能再被使用,SO_REUSEADDR是让端口释放后立即就可以被再次使用。 SO_REUSEADDR用于对TCP套接字处于TIME_WAIT状态下的socket,才可以重复绑定使用。server程序总是应该在调用bind()之前设置SO_REUSEADDR套接字选项。TCP,先调用close()的一方会进入TIME_WAIT状态。

我们知道,在TCP断开链接的时候我们需要四次握手来断开,而且当两端都关闭了read/write通道以后我们还是要等待一个TIME_WAIT时间。

这就是SO_REUSEADDR的作用所在.其实这个选项就是告诉OS如果一个端口处于TIME_WAIT状态, 那么我们就不用等待直接进入使用模式, 不需要继续等待这个时间结束.

那这样我们肯定要问,那为什么我们需要有这个TIME_WAIT时间啊?

看看TCP/IP协议组我们就知道,这样做是为了让在网络中残余的TCP包消失, 也就是说, 如果我们没有等到这个时间就让OS把这个端口释放给其他的进程使用,别的进程很有可能就会收到上一个会话的残余TCP包,这样就会出现一系列的不可预知的错误.

posted @ 2017-08-14 16:04  翎野君  阅读(1037)  评论(0编辑  收藏  举报