Examples of clustered/distributed environments
The following configurations can be used to get ideas and examples of how to distribute the load of you webapps between multiple uWSGI servers. All the examples need a version from the trunk or >= 0.9.4
A Mako remote generator
Template generation can be a heavy task in our setup. We have 5 servers:
192.168.9.10 -> public cherokee webservers configured for load-balancing to three uWSGI servers:
192.168.9.11 -> uWSGI server1
192.168.9.12 -> uWSGI server2
192.168.9.13 -> uWSGI server3
this three uWSGI server are diskless systems, their only purpose is to calculate the fibonacci sequence based on the QUERY_STRING values. After getting the tuple of values every node need to generate an output from a mako template saved on server 192.168.9.14.
We need to pass the mako generation to the 192.168.9.14 server.
First of all install the uWSGI server to 192.168.9.14 and define a python module (call it my_server.py) that will setup it to generate mako contents:
import uwsgi
from mako.template import Template
def makogenerator(obj, id):
makofilename = obj[0]
vars = obj[1]
return Template(filename = '/opt/makotemplates/' + makofilename).render(attributes = vars)
uwsgi.message_manager_marshal = makogenerator
the uwsgi.message_manager_marshal attributes set the callable to execute when the uWSGI server receive a marshalled message
run the uWSGI server on this new node:
./uwsgi -s 192.168.9.14:3033 -w my_server
Now on the uWSGI nodes connected to cherokee define a commodity function to generate mako content:
def mako(filename, vars):
return uwsgi.send_uwsgi_message("192.168.9.14", 3033, 33, 17, (filename, vars), 30)
The send_uwsgi_message function takes 6 arguments: the host of the uWSGI server that will receive the message, its port, the first uwsgi modifier (33 for marshalled messages), the second uwsgi modifier (a custom value), an object to pass and a timeout value (30 seconds in this case)
Now when we want to generate our mako output simply return:
mako('mytemplate.txt', {'fib_sequence': my_calculated_sequence, 'whattimeisit':time.time()})
remember that you can only marshal string,numbers,tuple,dictionary,list and float.
CPU Ultra-Intensive task
You have a webpage that count the value of N**4096 where N is a number from 0 to 15000.
This is a cpu intensive task that complete in about 30 seconds on a relatively fast machine.
30 seconds in web-environments are long as the Serpent Road in Dragonball, so your boss ask you to reduce this time.
You now have 5 new nodes where using uWSGI you can reduce the time to about 7 seconds.
Setup every node for doing the heavy calc:
import time
import uwsgi
def calc(n):
return n**4096
def calcfunc(vars, id):
starttime = time.time()
for x in range(vars[0],vars[1]):
calc(x)
return time.time()-starttime
uwsgi.message_manager_marshal = calcfunc
then in your old master node (connected to a webserver) modify the WSGI callable to split the load between your 5 new servers:
def remotecalc(env, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
clusters = ( ('192.168.173.5', 3431, [0,3000] ),
('192.168.173.6', 3432, [3001, 6000] ),
('192.168.173.7', 3433, [6001, 9000] ),
('192.168.173.8', 3434, [9001, 12000] ),
('192.168.173.9', 3435, [12001, 15000] )
all_values = uwsgi.send_multi_uwsgi_message(clusters, 33, 17, 40);
return all_values
The uwsgi.send_multi_uwsgi_message function send a message to a tuple of uWSGI cluster nodes then waits for data from each one (in this case every response is the time used by the node to do the calculus).
Whenever possible try to split your heavy operations to multiple cluster nodes. It is fun.
A centralized Spooler
You want all of your cluster nodes to launch long running task on a single node.
Configure a node as the spooler then on your web nodes enqueue data to the remote spooler using the raw send_uwsgi_message function:
uwsgi.send_uwsgi_message("spooler_ip", spooler_port, 17 , 0, {'key1':'value1', 'key2':'value2'}, 30)
17 is the modifier1 for spooler requests.
