Make a non-blocking request with requests when running Flask with Gunicorn and Gevent
<p>My Flask application will receive a request, do some processing, and then make a request to a slow external endpoint that takes 5 seconds to respond.  It looks like running Gunicorn with Gevent will allow it to handle many of these slow requests at the same time.  How can I modify the example below so that the view is non-blocking?</p>

<pre><code>import requests

@app.route('/do', methods = ['POST'])
def do():
    result = requests.get('slow api')
    return result.content
</code></pre>

<pre class="lang-none prettyprint-override"><code>gunicorn server:app -k gevent -w 4
</code></pre>

<p>First a bit of background, A blocking socket is the default kind of socket, once you start reading your app or thread does not regain control until data is actually read, or you are disconnected. This is how <code>python-requests</code>, operates by default. There is a spin off called <code>grequests</code> which provides non blocking reads.</p>

<blockquote>
  <p>The major mechanical difference is that send, recv, connect and accept
  can return without having done anything. You have (of course) a number
  of choices. You can check return code and error codes and generally
  drive yourself crazy. If you donât believe me, try it sometime</p>
</blockquote>

<p>Source: <a href="https://docs.python.org/2/howto/sockets.html" rel="nofollow">https://docs.python.org/2/howto/sockets.html</a></p>

<p>It also goes on to say:</p>

<blockquote>
  <p>Thereâs no question that the fastest sockets code uses non-blocking
  sockets and select to multiplex them. You can put together something
  that will saturate a LAN connection without putting any strain on the
  CPU. The trouble is that an app written this way canât do much of
  anything else - it needs to be ready to shuffle bytes around at all
  times.</p>
  
  <p>Assuming that your app is actually supposed to do something more than
  that, threading is the optimal solution</p>
</blockquote>

<p>But do you want to add a whole lot of complexity to your view by having it spawn it's own threads. Particularly when gunicorn as <a href="http://docs.gunicorn.org/en/stable/design.html#async-workers" rel="nofollow">async workers</a>?</p>

<blockquote>
  <p>The asynchronous workers available are based on Greenlets (via
  Eventlet and Gevent). Greenlets are an implementation of cooperative
  multi-threading for Python. In general, an application should be able
  to make use of these worker classes with no changes.</p>
</blockquote>

<p>and</p>

<blockquote>
  <p>Some examples of behavior requiring asynchronous workers: Applications
  making long blocking calls (Ie, external web services)</p>
</blockquote>

<p>So to cut a long story short, don't change anything! Just let it be. If you are making any changes at all, let it be to introduce caching. Consider using <a href="https://cachecontrol.readthedocs.io/en/latest/" rel="nofollow">Cache-control</a> an extension recommended by python-requests developers.</p>

python flask python-requests gunicorn gevent