关闭扭曲的服务器

时间:2017-12-07 19:45:53

标签: python tcp server twisted

我有一个扭曲的ServerFactory,我正在用它来推动客户的工作。该协议包含一个包含所有作业的队列。如果客户端请求新作业,此队列耗尽后,服务器将断开客户端连接。最终这将不会保持客户端连接,服务器将准备关闭。

我的问题是:

在完成工作后关闭ServerFactory是否有普遍接受的最佳做法?

我知道对于客户来说,最佳做法是使用twisted.internet.task.react来连接和处理连接丢失,从而关闭父进程。但我不确定服务器的情况是否也是如此。

目前这是我处理关闭的方式:

from twisted.application import internet, service
from twisted.internet import reactor
from twisted.internet.protocol import ServerFactory
from twisted.protocols.basic import LineReceiver


class ServerProtocol(LineReceiver):
    """Twisted Protocol for sending and receiving lines of bytes."""
    clients = []
    logger = logging.getLogger('launcher.Launcher.RunServer')

    def connectionMade(self) -> None:
        """When a connection is made add the client to the clients list."""
        self.clients.append(self)

    def lineReceived(self, line: bytes) -> None:
        """Whenver a line is received send work to the sending client.

        Parameters
        ----------
        line
            The message received from a client.

        """
        msg = 'Received: ' + line.decode('utf-8') + ' from ' +\
            self.transport.hostname
        self.logger.info(msg)
        if not self.queue.empty():
            run = self.queue.get()
            run_bytes = bytes(run, 'utf-8')
            self.logger.info('Sending run bytes to %s',
                             self.transport.hostname)
            self.sendLine(run_bytes)
        else:
            self.clients.remove(self)
            self.transport.loseConnection()
            if not self.clients:
                self.logger.info('Shutting down RunServer')
                self.reactor.stop()


class RunServer(object):
    """Class for containing twisted server components.

    Parameters
    ----------
    workers
        List of workers that will serve as clients.
    queue
        Queue of runs to execute.

    Attributes
    ----------
    factory
        Twisted ServerFactory for producing protocols.

    """
    def __init__(self, queue: Queue) -> None:
        self.factory = ServerFactory()
        self.factory.protocol = ServerProtocol
        self.factory.protocol.queue = queue
        self.factory.protocol.reactor = reactor

    def start(self) -> None:
        """Start the server and thereby the execution of runs."""
        self.factory.protocol.reactor.listenTCP(80, self.factory)
        self.factory.protocol.reactor.run()

正如您所看到的那样,我将反应堆存储在self.factory.protocol.reactor中,并在所有作业都已耗尽且客户端已断开连接后使用reactor.stop

我很确定我之前已经阅读过这不是运行客户端所接受的模式,我认为服务器也是如此,但我还没有看到一个很好的例子。

1 个答案:

答案 0 :(得分:1)

我不得不相信这个人。

完全不需要RunServer类。对ServerFactory进行子类化并将RunServer.__init__中的逻辑放入子类__init__将允许更好的控制具有相同的行为。然后,您可以定义main方法,并使用文档(twisted.internet.task.react

中描述的react

以下是更新后的代码:

from twisted.internet.defer import Deferred
from twisted.internet import reactor
from twisted.internet.protocol import ServerFactory
from twisted.protocols.basic import LineReceiver


class QueueingProtocol(LineReceiver):
    def connectionMade(self) -> None:
        self.factory.connectionMade()

    def connectionLost(self, reason) -> None:
        self.factory.connectionLost(reason)

    def lineReceived(self, line: bytes) -> None:
        msg = 'Received: ' + line.decode('utf-8') + ' from ' +\
            self.transport.hostname
        self.logger.info(msg)
        if self.factory.empty():
            self.transport.lostConnection()
        else:
            run = self.factory.get()
            run_bytes = bytes(run, 'utf-8')
            self.logger.info('Sending run bytes to %s',
                             self.transport.hostname)
            self.sendLine(run_bytes)


class QueueingFactory(ServerFactory):
    protocol = QueueingProtocol

    def __init__(self, queue) -> None:
        self.queue = queue
        self.connections = 0
        self.queueHandled = Deferred()

    def connectionMade(self) -> None:
        self.connections += 1

    def empty(self):
        return self.queue.empty()

    def get(self):
        return self.queue.get()

    def connectionLost(self, reason) -> None:
        self.connections -= 1
        if self.connections == 0 and self.empty():
            self.queueHandled.callback("done")


def main(reactor, queue):
    factory = QueueingFactory(queue)
    reactor.listenTCP(80, factory)
    return factory.queueHandled

然后,您只需在需要的位置导入main,然后致电react(main, [some_queue])