5

私が使用している非常に単純なサーバーを適切にシャットダウンする方法について、私は混乱しています。

私はこれで十分だと思っていました:

#!/usr/bin/python

import signal
import myhandler
import SocketServer

def terminate(signal, frame):
    print "terminating on %s at %s"
    server.shutdown()

if __name__ == "__main__":

    signal.signal(signal.SIGTERM, terminate)

    server = SocketServer.TCPServer(("localhost", 9999), myhandler.MyHandler)

    server.serve_forever()

サーバーは正常に動作しますが、SIGTERM をスローすると、出力するだけでterminating on 15 at ...実際にはシャットダウンしません (つまり、すべてのソケットを閉じて終了します)。

今pydocはそれを説明します

 shutdown(self)
     Stops the serve_forever loop.

     Blocks until the loop has finished. This must be called while
     serve_forever() is running in another thread, or it will
     deadlock.

しかし、これは私が迷子になっているところです。スレッド化されたプログラミングに頭を悩ませることさえほとんどないからです。今のところ、killallいつでも開始できる単純な TCP エコー サーバーが必要です (残りの LISTENING ソケットのために失敗します)。

それで、これを達成する正しい方法は何ですか?

4

2 に答える 2

3

免責事項: 私は 0、nil、null、none、python の経験がありません。 免責事項2:私は、あなたのサーバーが「行くべき道」であるとは決して思いません...サーバーに関連するものはすべて、最も基本的なことや学校の宿題以外のものでさえありません。人々が基本を学ぶのに役立つ良いサンプルかもしれませんが、同時に、私が数え切れないほど多くのレベルで誤解を招き、間違っています.

あなたの問題に戻ります。私はあなたのコードを取り、意図したとおりに動作するように修正しました:

#!/usr/bin/python

import signal
import SocketServer
import threading
import thread

class DummyServer(SocketServer.BaseRequestHandler):
    def handle(self):
        data = self.request.recv(1024)
        self.request.send(data)
        return

def shutdownHandler(msg,evt):
    print "shutdown handler called. shutting down on thread id:%x"%(id(threading.currentThread()))
    server.shutdown()
    print "shutdown complete"
    evt.set()
    return

def terminate(signal,frame):
    print "terminate handle on thread id:%x"%(id(threading.currentThread()))
    t = threading.Thread(target = shutdownHandler,args = ('SIGTERM received',doneEvent))
    t.start()

if __name__ == "__main__":
    doneEvent = threading.Event()
    print "main thread id:%x"%(id(threading.currentThread()))
    signal.signal(signal.SIGTERM, terminate)
    server = SocketServer.TCPServer(("localhost",9999), DummyServer)
    server.serve_forever()
    doneEvent.wait()

SocketServer のコード、特に server_forever() および shutdown() メソッドを確認する必要があります。また、スレッドについて、およびそれらの間であらゆる種類の通信/シグナリングを行う方法を学ぶようにしてください。これらのトピックに関する優れたソースがたくさんあります。

スレッドについて覚えておくべき基本的なことは、一般的に言えば、スレッドは一度に 1 つのことしかできないということです。シグナル ハンドラーはそれらの例外の 1 つです :) スレッドが server_forever() でスタックしている場合、 shutdown() への呼び出しも実行できるように同じスレッド。Python (シグナルのドキュメントを確認してください) は、シグナル ハンドラーをメイン スレッドで実行します。これは、コードから server_forever() ループを実行するのと同じスレッドです。シグナル ハンドラー内から shutdown() を呼び出すと、デッドロックが発生します。

これを回避するには、shutdown() を実行するためだけに新しいスレッドを作成します。新しいスレッドの shutdown() 呼び出しは、メイン スレッドの server_forever() に、ループを中断して終了する時間であることを知らせます。メイン スレッドは、shutdown() を実行しているスレッドが完了する前に終了することさえあります。

doneEvent は、シャットダウン スレッドが完了するまでメイン スレッド (doneEvent.wait()) が待機することを確認するために存在します。終了する前に「shutdown complete」を出力します。

于 2013-08-14T22:49:49.953 に答える
1

server_close()簡単な解決策として、 afterを呼び出すことができますserve_forever():

import socketserver

class StoppableServer(socketserver.TCPServer):
    def run(self):
        try:
            self.serve_forever()
        except KeyboardInterrupt:
            pass
        finally:
            # Clean-up server (close socket, etc.)
            self.server_close()

Ctrl+Cまたは SIGTERMで停止可能なサーバー:

server = StoppableServer(("127.0.0.1", 8080), socketserver.BaseRequestHandler)
server.run()

スレッドで実行中のサーバー:

server = StoppableServer(("127.0.0.1", 8080), socketserver.BaseRequestHandler)

thread = threading.Thread(None, server.run)
thread.start()

# ... do things ...

server.shutdown()
thread.join()
于 2016-02-23T11:48:26.957 に答える