0

目前我有一个基本的 HTTP 服务器设置使用BaseHTTPRequestHandler,我使用do_GET相同的方法。check如果请求在 5 秒内没有进来,我希望调用一个函数。

我正在考虑将多处理与 time 模块一起使用,但我担心它的可靠性。对于与此相关的最佳实践,是否有任何建议?

谢谢。

[编辑]

Marjin 的解决方案非常酷,但我最终得到以下回溯:-

Traceback (most recent call last):
  File "test.py", line 89, in <module>
    main()
  File "test.py", line 83, in main
    server.serve_forever()
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/SocketServer.py", line 224, in serve_forever
    r, w, e = select.select([self], [], [], poll_interval)
select.error: (4, 'Interrupted system call')

[编辑 2] 我在 Python 2.7 上尝试过,但仍然出现错误。

[编辑 3]

Traceback (most recent call last):
  File "test.py", line 90, in <module>
    main()
  File "test.py", line 84, in main
    server.serve_forever()
  File "/usr/local/lib/python2.7/SocketServer.py", line 225, in serve_forever
    r, w, e = select.select([self], [], [], poll_interval)
select.error: (4, 'Interrupted system call')
4

1 回答 1

1

对于一个简单的服务器,例如基于您的服务器,BaseHTTPRequestHandler您可以使用信号处理程序:

import time
import signal
import sys

last_request = sys.maxint  # arbitrary high value to *not* trigger until there has been 1 requests at least

def itimer_handler(signum, frame):
    print 'itimer heartbeat'
    if time.time() - last_request > 300:  # 5 minutes have passed at least with no request
        # do stuff now to log, kill, restart, etc.
        print 'Timeout, no requests for 5 minutes!'

signal.signal(signal.SIGALRM, itimer_handler) 
signal.setitimer(signal.ITIMER_REAL, 30, 30)  # check for a timeout every 30 seconds

# ...
def do_GET(..):
    global last_request
    last_request = time.time()  # reset the timer again

signal.setitimer()调用导致操作系统向我们的进程发送周期性SIGALRM信号。这不是太精确。setitimer)呼叫设置为 30 秒间隔。任何传入的请求都会重置一个全局时间戳,并且itimer_handler每 30 秒调用一次比较检查自上次设置时间戳以来是否已经过去了 5 分钟。

SIGALRM信号也会中断正在运行的请求,因此无论您在该处理程序中做什么,都需要快速完成。当函数返回正常的python代码流时,就像一个线程一样。

请注意,这至少需要 Python 2.7.4 才能工作;请参阅issue 7978,并且 2.7.4 尚未发布。您可以下载SocketServer.py将包含在 Python 2.7.4 中的文件,也可以应用以下反向移植来添加该errorno.EINTR版本中引入的处理:

'''Backport of 2.7.4 EINTR handling'''

import errno
import select
import SocketServer


def _eintr_retry(func, *args):
    """restart a system call interrupted by EINTR"""
    while True:
        try:
            return func(*args)
        except (OSError, select.error) as e:
            if e.args[0] != errno.EINTR:
                raise


def serve_forever(self, poll_interval=0.5):
    """Handle one request at a time until shutdown.

    Polls for shutdown every poll_interval seconds. Ignores
    self.timeout. If you need to do periodic tasks, do them in
    another thread.
    """
    self._BaseServer__is_shut_down.clear()
    try:
        while not self._BaseServer__shutdown_request:
            # XXX: Consider using another file descriptor or
            # connecting to the socket to wake this up instead of
            # polling. Polling reduces our responsiveness to a
            # shutdown request and wastes cpu at all other times.
            r, w, e = _eintr_retry(select.select, [self], [], [],
                                   poll_interval)
            if self in r:
                self._handle_request_noblock()
    finally:
        self._BaseServer__shutdown_request = False
        self._BaseServer__is_shut_down.set()


def handle_request(self):
    """Handle one request, possibly blocking.

    Respects self.timeout.
    """
    # Support people who used socket.settimeout() to escape
    # handle_request before self.timeout was available.
    timeout = self.socket.gettimeout()
    if timeout is None:
        timeout = self.timeout
    elif self.timeout is not None:
        timeout = min(timeout, self.timeout)
    fd_sets = _eintr_retry(select.select, [self], [], [], timeout)
    if not fd_sets[0]:
        self.handle_timeout()
        return
    self._handle_request_noblock()


# patch in updated methods
SocketServer.BaseServer.serve_forever = serve_forever
SocketServer.BaseServer.handle_request = handle_request
于 2013-02-11T14:50:04.263 回答