Search code examples
pythonpython-3.xwsgibottlewsgiref

How is Bottle's built-in WSGI server different from the standard Python wsgiref server module?


What is Bottle doing in its wsgiref server implementation that the built in Python WSGIref simple server is not? When I look at Bottle, for example, it adheres to the WSGI standard and the documentation states:

1.5.1 Server Options The built-in default server is based on wsgiref WSGIServer. This non-threading HTTP server is perfectly fine for development and early production, but may become a performance bottleneck when server load increases.
There are three ways to eliminate this bottleneck:

  • • Use a different server that is either multi-threaded or asynchronous.
  • • Start multiple server processes and spread the load with a load-balancer.
  • • Do both [emphasis mine]

Yet, everything I have read says to not use the Python wsgrief server for anything production.

What does Bottle do with wsgrief that the built in Python wsgiref does not? I'm not really questioning the wisdom of using asynch servers or "bigger" more "scalable" WSGI servers. But, I'd like to know what Bottle is doing with the wsgiref server that makes it okay for "early Production," the regular library does not.

My application would serve less than 20 people hitting a PostgreSQL or MySQL database, CRUD operations. I guess you could ask a similar question with Flask.

For reference,

http://bottlepy.org/docs/dev/bottle-docs.pdf [pdf] https://docs.python.org/2/library/wsgiref.html#module-wsgiref.simple_server https://github.com/bottlepy/bottle/blob/master/bottle.py

This is Bottle's implementation, at least for opening the port:

class WSGIRefServer(ServerAdapter):
    def run(self, app):  # pragma: no cover
        from wsgiref.simple_server import make_server
        from wsgiref.simple_server import WSGIRequestHandler, WSGIServer
        import socket

        class FixedHandler(WSGIRequestHandler):
            def address_string(self):  # Prevent reverse DNS lookups please.
                return self.client_address[0]

            def log_request(*args, **kw):
                if not self.quiet:
                    return WSGIRequestHandler.log_request(*args, **kw)

        handler_cls = self.options.get('handler_class', FixedHandler)
        server_cls = self.options.get('server_class', WSGIServer)

        if ':' in self.host:  # Fix wsgiref for IPv6 addresses.
            if getattr(server_cls, 'address_family') == socket.AF_INET:

                class server_cls(server_cls):
                    address_family = socket.AF_INET6

        self.srv = make_server(self.host, self.port, app, server_cls,
                               handler_cls)
        self.port = self.srv.server_port  # update port actual port (0 means random)
        try:
            self.srv.serve_forever()
        except KeyboardInterrupt:
            self.srv.server_close()  # Prevent ResourceWarning: unclosed socket
            raise

Solution

  • EDIT:

    What is Bottle doing in its wsgiref server implementation that the built in Python WSGIref simple server is not?

    What does Bottle do with wsgrief that the built in Python wsgiref does not?

    Nothing (of substance).


    Not sure I understand your question, but I'll take a stab at helping.

    The reason for my confusion is: the code snippet you posted precisely answers [what I think is] your question. Bottle's WSGIRefServer class does nothing substantial except wrap wsgiref.simple_server. (I'm calling the logging and the IPv6 tweaks unsubstantial because they're not related to "production-readiness," which I gather is at the heart of your question.)

    Is it possible that you misinterpreted the docs? I'm thinking perhaps yes, because you say:

    I'd like to know what Bottle is doing with the wsgiref server that makes it okay for "early Production," the regular library does not.

    but the Bottle docs are making the point that Bottle's WSGIRefServer should not be used to handle high throughput loads.

    In other words, WSGIRefServer is the same as wsgiref, whereas I think you interpreted the docs as saying that the former is somehow improved over the latter. (It's not.)

    Hope this helps!