09.11.2016 Views

Foundations of Python Network Programming 978-1-4302-3004-5

Create successful ePaper yourself

Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.

CHAPTER 7 ■ SERVER ARCHITECTURE<br />

single client and then sends back the information it needs—and run several copies <strong>of</strong> it at once so that<br />

we can serve several clients at once, without making them wait on each other.<br />

The event-driven approaches in Listings 7–7 and 7–8 place upon our own program the burden <strong>of</strong><br />

figuring out which client is ready next, and how to interleave requests and responses depending on the<br />

order in which they arrive. But when using threads and processes, you get to transfer this burden to the<br />

operating system itself. Each thread controls one client socket; it can use blocking recv() and send()<br />

calls to wait until data can be received and transmitted; and the operating system then decides which<br />

workers to leave idle and which to wake up.<br />

Using multiple threads or processes is very common, especially in high-capacity web and database<br />

servers. The Apache web server even comes with both: its prefork module <strong>of</strong>fers a pool <strong>of</strong> processes,<br />

while the worker module runs multiple threads instead.<br />

Listing 7–9 shows a simple server that creates multiple workers. Note how pleasantly symmetrical<br />

the Standard Library authors have made the interface between threads and processes, thanks especially<br />

to Jesse Noller and his recent work on the multiprocessing module. The main program logic does not<br />

even know which solution is being used; the two classes have a similar enough interface that either<br />

Thread or Process can here be used interchangeably.<br />

Listing 7–9. Multi-threaded or Multi-process Server<br />

#!/usr/bin/env python<br />

# <strong>Foundations</strong> <strong>of</strong> <strong>Python</strong> <strong>Network</strong> <strong>Programming</strong> - Chapter 7 - server_multi.py<br />

# Using multiple threads or processes to serve several clients in parallel.<br />

import sys, time, launcelot<br />

from multiprocessing import Process<br />

from server_simple import server_loop<br />

from threading import Thread<br />

WORKER_CLASSES = {'thread': Thread, 'process': Process}<br />

WORKER_MAX = 10<br />

def start_worker(Worker, listen_sock):<br />

» worker = Worker(target=server_loop, args=(listen_sock,))<br />

» worker.daemon = True # exit when the main process does<br />

» worker.start()<br />

» return worker<br />

if __name__ == '__main__':<br />

» if len(sys.argv) != 3 or sys.argv[2] not in WORKER_CLASSES:<br />

» » print >>sys.stderr, 'usage: server_multi.py interface thread|process'<br />

» » sys.exit(2)<br />

» Worker = WORKER_CLASSES[sys.argv.pop()] # setup() wants len(argv)==2<br />

» # Every worker will accept() forever on the same listening socket.<br />

» listen_sock = launcelot.setup()<br />

» workers = []<br />

» for i in range(WORKER_MAX):<br />

» » workers.append(start_worker(Worker, listen_sock))<br />

» # Check every two seconds for dead workers, and replace them.<br />

» while True:<br />

» » time.sleep(2)<br />

118

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!