Twisted as Your WSGI Container

Moshe Zadka

https://github.com/moshez

Django's builtin webserver is awful

DO NOT USE THIS SERVER IN A PRODUCTION SETTING. It has not gone through security audits or performance tests.

("django-admin and manage.py")

don't use this server in anything resembling a production environment. It’s intended only for use while developing.

Django Tutorial

Development vs. Production

Brand new source of drift!

Twisted WSGI Container


$ # Assumes you installed the project
$ twist web --wsgi mysite.wsgi.application 
$ # From work directory
$ PYTHONPATH=mysite twist web --wsgi mysite.wsgi.application 

Full fledged "Dev Server"


watchmedo shell-command \
 --patterns="*.py;*.txt" --recursive \
 --command \
 'PYTHONPATH=mysite twist web --wsgi mysite.wsgi.application' \
 mysite

Put in manage.py

Command lines support

The --port argument is magical.
  • tcp:8001
  • txsni:/path/to/certs:tcp:8080
  • txacme:/path/to/certs:tcp:8888

100% Python

HTTP parser, socket code, thread pool manager...

HTTP/2

HTTP/2 supported out of the box via Hyper/H2 (100% unit test coverage).

Python is the Configuration Language (pt 1.)


    pool = threadpool.ThreadPool(name="WSGI",
                                 minThreads=3,
                                 maxThreads=4)
    reactor.callWhenRunning(pool.start)
    reactor.addSystemEventTrigger('after', 'shutdown', pool.stop)

Python is the Configuration Language (pt 2.)


    application = twemo.wsgi.application
    wsgi_resource = wsgi.WSGIResource(reactor, pool, application)

Python is the Configuration Language (pt. 3)


    site = server.Site(root)
    # Disable HTTP/2
    site.acceptableProtocols = lambda: [b'http/1.1'] 

Easy to package


$ pex -o twist.pex -m twisted twisted myproject
$ scp twist.pex machine2:/home/me/twist.pex
$ ssh /home/me/twist.pex web --wsgi myproject.wsgi.application

PID 1

Reaps adopted children

Static file support: resource


class DelegatingResource(resource.Resource):
    def __init__(self, wsgi_resource):
        resource.Resource.__init__(self)
        self.wsgi_resource = wsgi_resource
    def getChild(self, name, request):
        request.prepath = []
        request.postpath.insert(0, name)
        return self.wsgi_resource

Static file support: service

@interface.implementer(service.IServiceMaker, plugin.IPlugin)
class ServiceMaker(object):
  description = tapname = "simple"
  class options(usage.Options):
    optParameters = [["port", None, None, ""]]
  def makeService(self, options):
    pool = threadpool.ThreadPool()
    reactor.callWhenRunning(pool.start)
    reactor.addSystemEventTrigger('after', 'shutdown', pool.stop)
    wsgi = wsgi.WSGIResource(reactor, pool, twemo.wsgi.application)
    root = DelegatingResource(wsgi)
    root.putChild('static', static.File('.'))
    return strports.service(options['port'], server.Site(root))
serviceMaker = ServiceMaker()

Direct-to-Net

  • SSL support (including SNI, ACME)
  • Static file support

Schedule Repeating Tasks


    ts = internet.TimerService(30, print, "---MARK---")

Control channels

Dynamically set parameters (e.g., A/B experiment parameters) from

  • Dedicated REST API on another port
  • Thrift
  • AMP

Downsides

Multiprocess support

Q&A

A question is something you do not know the answer to.