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 />

Event-Driven Servers Are Blocking and Synchronous<br />

The terminology surrounding event-driven servers like the one shown in Listing 7–7 has become quite<br />

tangled. Some people call them “non-blocking,” despite the fact that the poll() call blocks, and others<br />

call them “asynchronous” despite the fact that the program executes its statements in their usual linear<br />

order. How can we keep these claims straight?<br />

First, I note that everyone seems to agree that it is correct to call such a server “event-driven,” which<br />

is why I am using that term here.<br />

Second, I think that when people loosely call these systems “non-blocking,” they mean that it does<br />

not block waiting for any particular client. The calls to send and receive data on any one socket are not<br />

allowed to pause the entire server process. But in this context, the term “non-blocking” has to be used<br />

very carefully, because back in the old days, people wrote programs that indeed did not block on any<br />

calls, but instead entered a “busy loop” that repeatedly polled a machine’s I/O ports watching for data to<br />

arrive. That was fine if your program was the only one running on the machine; but such programs are a<br />

disaster when run under modern operating systems. The fact that event-driven servers can choose to<br />

block with select() or poll() is the very reason they can function as efficient services on the machine,<br />

instead <strong>of</strong> being resource hogs that push CPU usage immediately up to 100%.<br />

Finally, the term “asynchronous” is a troubled one. At least on Unix systems, it was traditionally<br />

reserved for programs that interacted with their environment by receiving signals, which are violent<br />

interruptions that yank your program away from whatever statement it is executing and run special<br />

signal-handling code instead. Check out the signal module in the Standard Library for a look at how<br />

<strong>Python</strong> can hook into this mechanism. Programs that could survive having any part <strong>of</strong> their code<br />

randomly interrupted were rather tricky to write, and so asynchronous programming was quite correctly<br />

approached with great caution. And at bottom, computers themselves are inherently asynchronous.<br />

While your operating system does not receive “signals,” which are a concept invented for user-level<br />

programs, they do receive IRQs and other hardware interrupts. The operating system has to have<br />

handlers ready that will correctly respond to each event without disturbing the code that will resume<br />

when the handler is complete.<br />

So it seems to me that enough programming is really asynchronous, even today, that the term<br />

should most properly be reserved for the “hard asynchrony” displayed by IRQs and signal handlers. But,<br />

on the other hand, one must admit that while the program statements in Listing 7–7 are synchronous<br />

with respect to one another—they happen one right after the other, without surprises, as in any <strong>Python</strong><br />

program—the I/O itself does not arrive in order. You might get a string from one client, then have to<br />

finish sending an answer to a second client, then suddenly find that a third client has hung up its<br />

connection. So we can grudgingly admit that there is a “s<strong>of</strong>t asynchrony” here that involves the fact that<br />

network operations happen whenever they want, instead <strong>of</strong> happening lockstep in some particular<br />

order.<br />

So in peculiar and restricted senses, I believe, an event-driven server can indeed be called nonblocking<br />

and asynchronous. But those terms can also have much stronger meanings that certainly do<br />

not apply to Listing 7–7, so I recommend that we limit ourselves to the term “event-driven” when we talk<br />

about it.<br />

Twisted <strong>Python</strong><br />

I mentioned earlier that you are probably doing something wrong if you are sitting down to wrestle with<br />

select() or poll() for any reason other than to write a new event-driven framework. You should<br />

normally treat them as low-level implementation details that you are happy to know about—having seen<br />

and studied Listing 7–7 makes you a wiser person, after all—but that you also normally leave to others.<br />

In the same way, understanding the UTF-8 string encoding is useful, but sitting down to write your own<br />

encoder in <strong>Python</strong> is probably a sign that you are re-inventing a wheel.<br />

114

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

Saved successfully!

Ooh no, something went wrong!