Using Libcloud in multi-threaded and async environments
Most of the time you want to perform more operations in parallel or just want your code to finish faster (for example starting a lot of servers or periodically polling for node status).
Problems like this are usually solved using threads or async libraries such as Twisted, Tornado or gevent.
This page contains some information and tips about how to use Libcloud in such environments.
gevent has an ability to monkey patch and replace functions in the Python , urllib2
, httplib
and time
module with its own functions which don’t block.
You need to do two things when you want to use Libcloud with gevent:
- Enable monkey patching
- Create a separate driver instance for each Greenlet. This is necessary because a driver instance reuses the same Connection class.
For an example see Efficiently download multiple files using gevent.
If you don’t use it properly it can block the whole reactor (similar as any other blocking library or a long CPU-intensive task) which means the execution of other pending tasks in the event queue will be blocked.
A simple solution to prevent blocking the reactor is to run Libcloud calls inside a thread. In Twisted this can be achieved using threads.deferToThread
which runs a provided method inside the Twisted thread pool.
The example below demonstrates how to create a new node inside a thread without blocking the whole reactor.
from __future__ import absolute_import
from pprint import pprint
# pylint: disable=import-error
from twisted.internet import defer, threads, reactor
from libcloud.compute.types import Provider
from libcloud.compute.providers import get_driver
@defer.inlineCallbacks
def create_node(name):
node = yield threads.deferToThread(_thread_create_node,
name=name)
pprint(node)
reactor.stop()
def _thread_create_node(name):
Driver = get_driver(Provider.RACKSPACE)
conn = Driver('username', 'api key')
node = conn.create_node(name=name, image=image, size=size)
return node
def stop(*args, **kwargs):
reactor.stop()
d = create_node(name='my-lc-node')
d.addCallback(stop) # pylint: disable=no-member
d.addErrback(stop) # pylint: disable=no-member
reactor.run()