重新启动 Apache 时其他进程接管端口 80 - 为什么以及如何解决?

重新启动 Apache 时其他进程接管端口 80 - 为什么以及如何解决?

我有一台 CentOS 5.5 服务器,在端口 80 上运行 Apache 以及一些其他应用程序。一切正常,直到我出于某种原因需要重新启动 httpd 进程。这样做会返回:

sudo /etc/init.d/httpd restart
Stopping httpd:                                            [  OK  ]
Starting httpd: (98)Address already in use: make_sock: could not bind to address [::]:80
(98)Address already in use: make_sock: could not bind to address 0.0.0.0:80
no listening sockets available, shutting down
Unable to open logs

一开始我以为 httpd 可能已经冻结并且仍在运行,但事实并非如此。所以我运行 netstat 来找出正在使用端口 80 的程序:

sudo netstat -tlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name   
tcp        0      0 *:7203                      *:*                         LISTEN      24012/java          
tcp        0      0 localhost.localdomain:smux  *:*                         LISTEN      3547/snmpd          
tcp        0      0 *:mysql                     *:*                         LISTEN      21966/mysqld        
tcp        0      0 *:ssh                       *:*                         LISTEN      3562/sshd           
tcp        0      0 *:http                      *:*                         LISTEN      3780/python26

事实证明,在 httpd 重新启动的瞬间,我的 python 进程接管了对 http 的监听。因此,我终止了 python 并尝试再次启动 httpd - 但遇到了同样的错误。再次运行 Netstat:

sudo netstat -tlp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name   
tcp        0      0 *:7203                      *:*                         LISTEN      24012/java          
tcp        0      0 localhost.localdomain:smux  *:*                         LISTEN      3547/snmpd          
tcp        0      0 *:mysql                     *:*                         LISTEN      21966/mysqld        
tcp        0      0 *:ssh                       *:*                         LISTEN      3562/sshd           
tcp        0      0 *:http                      *:*                         LISTEN      24012/java

现在我的 java 进程已接管监听 http。我也终止了它,然后可以成功重新启动 httpd。

但这是一个糟糕的解决方法。为什么这些 python 和 java 进程会在 httpd 重新启动后立即开始监听端口 80?如何解决?

另外两条评论。1) Java 和 Python 进程都是由 apache 通过 php 脚本启动的。但是当 apache 重新启动时,它们不应该受到影响。2) 我在另外两台运行 Ubuntu 的机器上进行了相同的设置,没有任何问题。

有任何想法吗?

编辑:

Java 进程监听端口 7203,而 python 进程据说不监听任何端口。由于某种原因,当 apache 重新启动时,它们开始监听端口 80。以前没有发生过这种情况。在 Ubuntu 上运行良好。由于某种原因,在我当前的 CentOS 5.5 机器上出现了这个问题。

答案1

问题可能在于 Apache 启动子进程的方式。它们可能通过分叉生成,并让克隆的 Apache 进程成为其他进程。每个克隆都会继承父进程的所有打开的文件句柄,包括 TCP 端口 80 的打开句柄。

Netstat 仅显示与打开文件句柄关联的一个应用程序,而三个进程保持该句柄保持打开。

您的问题的可能解决方案:

  1. 如果在 Apache 重启期间保持子进程运行很重要,最简单的解决方案是将 3 个进程作为单独的系统服务启动。

  2. 如果它们依赖于正在运行的 Apache,则“apache stop”命令也应终止它们。这可以通过编辑 /etc/init.d/apache 脚本来实现。

  3. 如果您被迫从 Apache 启动它们,则需要将它们作为真正的“守护进程”启动!

我为一个 PyQt 应用程序编写了一个守护进程创建器,灵感来自http://code.activestate.com/recipes/278731-creating-a-daemon-the-python-way/。您可以从 Apache 启动该脚本,为每个子进程调用 exec_as_daemon 并关闭它。

# Copyright: (c) 2011 phobie <[email protected]>
# License: CC-by-sa 3.0
import os
def exec_as_daemon(s_path_cmd, l_args=[]):
    i_pid = os.fork()
    if i_pid != 0:
        # Always remember to gobble your zombie children
        os.wait()
        # Back to parent
        return
    # Detach from parent
    os.setsid()
    # Do not block any mounts
    os.chdir('/')
    # Reset file creation rights
    os.umask(0)
    i_pid = os.fork()
    if i_pid != 0:
        # Close the direct child process
        os._exit(0)
    # Get the maximum count of open file handles
    try:
        import resource
        i_fd_max = resource.getrlimit(resource.RLIMIT_NOFILE)[1]
        if i_fd_max == resource.RLIM_INFINITY:
            i_fd_max = 1024
    except ImportError:
        i_fd_max = 1024
    # Try to close all possible file handles
    for i_cur_fd in range(0, i_fd_max):
        try:
            os.close(i_cur_fd)
        except OSError:
            pass
    # Assosiate STDIN with /dev/null
    os.open(os.devnull if hasattr(os, "devnull") else '/dev/null', os.O_RDWR)
    # STDOUT to /dev/null
    os.dup2(0, 1)
    # STDERR to /dev/null
    os.dup2(0, 2)
    l_args.insert(0, s_path_cmd)
    # Let the clone become a other process
    os.execv(s_path_cmd, l_args)

if __name__ == '__main__':
    exec_as_daemon('/usr/bin/java', ['-jar', '/usr/local/bin/apache_helper_daemon.jar'])
    exec_as_daemon('/usr/local/bin/apache_helper_daemon.py')

答案2

理论上,只有一个进程应该监听给定的 IP/端口。如果您需要多个应用程序监听同一个端口,则需要一种反向代理类型的设置,该设置将根据收到的内容确定哪个进程获取消息。

了解您的 Python 和 Java 应用程序正在做什么会很有帮助。如果它们也是侦听端口 80 的服务器,那么它们会在 Apache 运行时打开端口 80 进行侦听时卡住,并且一旦您终止 Apache,进程队列中的下一个进程就会通过并打开端口。您需要更改 Python 和 Java 代码以侦听不同的端口。

答案3

您未能彻底关闭 Apache 主进程,并且其子进程仍挂在套接字上。不要运行重新启动,而是运行服务 httpd stop。如果此操作无法彻底退出,您需要进一步调查您通过 Apache 启用的 Java 和 Python 应用程序。

如果服务 httpd stop 无法正常工作,请卸载 Java 和 Python 应用程序,直到可以正常重新启动。然后,调查这些进程无法正常终止的原因。

答案4

发生的事情很奇怪。解决方法是reload改用restart

sudo /etc/init.d/httpd reload

尝试查找有关新的 java 和 python 进程的信息。您可以尝试查找它们何时启动以及命令行参数:

ps -edf|egrep 'python|java|PID'

还请检查 /etc/init.d/httpd 中是否存在与 java 或 python 相关的内容。

使用审计工具查明程序何时启动、由谁启动等:

相关内容