TwistedWeb在多核/多处理器(TwistedWeb on multicore/multipr

2019-06-23 11:31发布

What techniques are people using to utilize multiple processors/cores when running a TwistedWeb server? Is there a recommended way of doing it?

My twisted.web based web service is running on Amazon EC2 instances, which often have multiple CPU cores (8, 16), and the type of work that the service is doing benefits from extra processing power, so i would very much like to use that.

I understand that it is possible to use haproxy, squid or a web server, configured as a reverse proxy, in front of multiple instances of Twisted. In fact, we are currently using such a setup, with nginx serving as a reverse proxy to several upstream twisted.web services running on the same host, but each on different port.

This works fine, but what i'm really interested in, is a solution where there is no "front-facing" server, but all twistd processes somehow bind to the same socket and accept requests. Is such thing even possible... or am i being crazy? The operating system is Linux (CentOS).

Thanks.

Anton.

Answer 1:

有许多的方式来支持多进程运行了扭曲的应用。 在开始回答一个重要的问题,不过,是你期望你的并发模型是,如何与共享状态的应用程序处理。

在一个单一的过程中扭曲的应用,并发是所有合作(与扭曲的异步I / O API的帮助)和共享状态可以在任何地方保持一个Python对象会去。 您的应用程序代码运行知道,直到它放弃控制,其他什么都可以运行。 此外,想要访问一些片共享状态也许可以这样做很容易,因为该状态可能保持在一个无聊的老Python对象,很容易访问你的应用程序的任何部分。

当你有多个进程,即使他们都运行基于双绞线的应用程序,那么你有两种形式的并发。 一个是相同的前一种情况 - 一个特定的进程中,并发是合作。 但是,你有一个新的,有多个进程正在运行。 你的平台的进程调度可能随时切换这些过程之间执行,你必须在这个非常小的控制(以及很少的可视性它发生时)。 它甚至可以安排你的两个过程,在不同的内核同时运行的(这可能是你希望即使什么)。 这意味着你失去了一些担保约一致性,因为一个过程中不知道什么时候第二个进程可能会一起走,并尝试在一些共享的状态运行。 这导致中考虑的另一重要领域,你将如何在实际过程之间共享状态。

不同于单一的流程模型,你不再有任何方便的,很容易到达的地方来存储您的状态,其中所有的代码可以达到它。 如果你把它放在一个过程,在这个过程中所有的代码可以很容易地访问它作为一个正常的Python对象,但在你的任何其他进程运行任何代码不再轻而易举地访问它。 您可能需要找到一个RPC系统,让你的进程相互通信。 或者,你可以构建您的过程鸿沟使每一个过程只接收需要存储在过程状态的请求。 这方面的一个例子可能是一个网站会话,其中关于用户的所有状态被存储在他们的会议,他们的会议是由饼干标识。 一个前端进程可以接收网络请求,检查该cookie,查找其后端进程负责该会话,然后到该后端进程转发请求。 该方案意味着,后端通常不需要进行沟通(只要你的Web应用程序是足够简单 - 即,只要用户不彼此交互,或共享数据进行操作)。

请注意,在这个例子中,一个分叉预模式是不恰当的。 前端过程必须完全拥有侦听端口,以便它能够检查所有传入的请求它们由后端工艺处理之前。

当然,也有很多类型的应用程序,与许多其他模型管理状态。 选择多处理正确的模式,首先需要了解什么样的并发的有意义的应用程序,以及如何管理你的应用程序的状态。

话虽这么说,有扭曲的非常新的版本(未发行,因为这点的),它很容易分享监听TCP端口在多个进程。 这里是演示了您可以使用一些新的API来实现这一目标的一种方式的代码片段:

from os import environ
from sys import argv, executable
from socket import AF_INET

from twisted.internet import reactor
from twisted.web.server import Site
from twisted.web.static import File

def main(fd=None):
    root = File("/var/www")
    factory = Site(root)

    if fd is None:
        # Create a new listening port and several other processes to help out.                                                                     
        port = reactor.listenTCP(8080, factory)
        for i in range(3):
            reactor.spawnProcess(
                    None, executable, [executable, __file__, str(port.fileno())],
                childFDs={0: 0, 1: 1, 2: 2, port.fileno(): port.fileno()},
                env=environ)
    else:
        # Another process created the port, just start listening on it.                                                                            
        port = reactor.adoptStreamPort(fd, AF_INET, factory)

    reactor.run()


if __name__ == '__main__':
    if len(argv) == 1:
        main()
    else:
        main(int(argv[1]))

与旧版本,有时你可以逃脱用fork共享端口。 然而,这是相当容易出错,无法在某些平台上,而不是用扭曲的支持方式:

from os import fork

from twisted.internet import reactor
from twisted.web.server import Site
from twisted.web.static import File

def main():
    root = File("/var/www")
    factory = Site(root)

    # Create a new listening port
    port = reactor.listenTCP(8080, factory)

    # Create a few more processes to also service that port
    for i in range(3):
        if fork() == 0:
            # Proceed immediately onward in the children.
            # The parent will continue the for loop.
            break

    reactor.run()


if __name__ == '__main__':
    main()

这工作,因为叉,在新创建的进程(子)继承了所有从原来的进程(父)的内存和文件描述符的正常行为。 由于工艺否则分离,这两个过程不彼此干扰,至少只要它们正在执行的Python代码去。 由于文件描述符继承,无论是父母或孩子能接受的端口上的连接。

由于转发HTTP请求是一件容易的事,我怀疑你会发现很多使用这两种技术的性能改进。 前者比代理更好一点,因为它简化您的部署,更容易对非HTTP应用程序的工作原理。 后者可能更多的是责任,不值得接受的。



Answer 2:

推荐的方法是IMO使用haproxy (或其它负载平衡器)像你已经是,如果配置正确的瓶颈不应该负载平衡器。 此外,你会希望有一些fallover方法, haproxy情况下提供你的流程的一个出现故障。

这是不可能的多个进程绑定到同一TCP套接字,但它是可能的UDP。



Answer 3:

如果您希望通过HTTPS投放你的网页内容为好,这是你需要对@让 - 保罗的片断的顶部做什么。

from twisted.internet.ssl import PrivateCertificate
from twisted.protocols.tls import TLSMemoryBIOFactory

'''
Original snippet goes here
..........
...............
'''

privateCert = PrivateCertificate.loadPEM(open('./server.cer').read() + open('./server.key').read())
tlsFactory = TLSMemoryBIOFactory(privateCert.options(), False, factory)
reactor.adoptStreamPort(fd, AF_INET, tlsFactory)

通过使用fd ,你将成为HTTP或HTTPS,但不能同时使用。 如果你想兼得, listenSSL父进程,包括SSL fd你产卵子进程时,从SSL端口作为第二个参数得到。

完全剪断是在这里:

from os import environ
from sys import argv, executable
from socket import AF_INET

from twisted.internet import reactor
from twisted.web.server import Site
from twisted.web.static import File

from twisted.internet import reactor, ssl
from twisted.internet.ssl import PrivateCertificate
from twisted.protocols.tls import TLSMemoryBIOFactory

def main(fd=None, fd_ssl=None):
    root = File("/var/www")
    factory = Site(root)

    spawned = []
    if fd is None:
        # Create a new listening port and several other processes to help out.                                                                     
        port = reactor.listenTCP(8080, factory)
        port_ssl = reactor.listenSSL(8443, factory, ssl.DefaultOpenSSLContextFactory('./server.key', './server.cer'))
        for i in range(3):
            child = reactor.spawnProcess(
                None, executable, [executable, __file__, str(port.fileno()), str(port_ssl.fileno())],
                childFDs={0: 0, 1: 1, 2: 2, port.fileno(): port.fileno(), port_ssl.fileno(): port_ssl.fileno()},
                env=environ)
            spawned.append(child)
    else:
        # Another process created the port, just start listening on it.                                                                            
        port = reactor.adoptStreamPort(fd, AF_INET, factory)
        cer = open('./server.cer')
        key = open('./server.key')
        pem_data = cer.read() + key.read()
        cer.close()
        pem.close()
        privateCert = PrivateCertificate.loadPEM(pem_data )
        tlsFactory = TLSMemoryBIOFactory(privateCert.options(), False, factory)
        reactor.adoptStreamPort(fd_ssl, AF_INET, tlsFactory)

    reactor.run()

    for p in spawned:
        p.signalProcess('INT')


if __name__ == '__main__':
    if len(argv) == 1:
        main()
    else:
        main(int(argv[1:]))


文章来源: TwistedWeb on multicore/multiprocessor