ベースのような単純なサーバーの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()
呼び出しにより、OS は定期的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