Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I yield to another request when using gunicorn with asyncio?

I'm trying to use the gaiohttp worker in Gunicorn with a Django app I'm developing with the following command:

gunicorn -k gaiohttp -b localhost:8080 myproject.wsgi

My ultimate goal is to be able to process requests concurrently - i.e. have 1 gunicorn worker process multiple requests concurrently. There are I/O bound operations which makes these requests slow.

I know an event loop is already running when I'm processing a request:

class MyView(View):

    def get(self, request):
        loop = asyncio.get_event_loop()
        loop.is_running() # True
        ...

Questions:

  1. How can I perform an operation such as yield from asyncio.sleep(10) within my view code?

    class MyView(View):
    
        def get(self, request):
            # Raises AssertionError: yield from wasn't used with future
            yield from asyncio.sleep(10)
    
  2. I can add tasks to the event loop, however they don't block while processing the request

    @asyncio.coroutine
    def simulate_work():
        yield from asyncio.sleep(10)
    
    class MyView(View):
    
        def get(self, request):
            # This runs simulate_work(), however, it doesn't block 
            # the response is returned before simulate_work() is finished
            loop = asyncio.get_event_loop()
            task = loop.create_task(simulate_work())
    
  3. I try to use futures, but the event loop is already running

    @asyncio.coroutine
    def simulate_work(future):
        yield from asyncio.sleep(10)
        future.set_result('Done!')
    
    class MyView(View):
    
        def get(self, request):
            future = asyncio.Future()
            asyncio.async(simulate_work(future))
            loop = asyncio.get_event_loop()
            # Raises RuntimeError: Event loop is running.
            loop.run_until_complete(future)
            print(future.result())
    

Clearly there's something I'm not understanding about asyncio or gaiohttp.

How can I have asyncio.sleep(10) block for the current requests, but not block gunicorn from processing other requests?

like image 873
Derek Kwok Avatar asked Jan 29 '15 18:01

Derek Kwok


People also ask

How does Gunicorn handle multiple requests?

If each request takes 10 milliseconds, a single worker dishes out 100 RPS. If some requests take 10 milliseconds, others take, say, up to 5 seconds, then you'll need more than one concurrent worker, so the one request that takes 5 seconds does not "hog" all of your serving capability.

Does Gunicorn do load balancing?

Gunicorn relies on the operating system to provide all of the load balancing when handling requests.

Are Gunicorn workers processes or threads?

Gunicorn is based on the pre-fork worker model. This means that there is a central master process that manages a set of worker processes. The master never knows anything about individual clients. All requests and responses are handled completely by worker processes.

What is Gunicorn Eventlet?

Gunicorn provides a much more configurable and production-tested server. eventlet allows writing asynchronous, coroutine-based code that looks like standard synchronous Python. It uses greenlet to enable task switching without writing async/await or using asyncio . gevent is another library that does the same thing.


1 Answers

Sorry, you cannot call coroutines from your wsgi application -- WSGI is synchronous protocol, as well as frameworks built on top of it (Django, Flask, Pyramid).

I've implemented gaiohttp worker but it's second class citizen in asyncio world. If you really need asynchronous HTTP server please try aiohttp.web.

like image 92
Andrew Svetlov Avatar answered Nov 10 '22 12:11

Andrew Svetlov