[Zope-Checkins] CVS: Zope/lib/python/ZServer/medusa/notes - async_blurbs.txt:1.1.2.1 debugging.txt:1.1.2.1 threads.txt:1.1.2.1 tkinter.txt:1.1.2.1

Chris McDonough chrism@zope.com
Tue, 17 Sep 2002 01:16:10 -0400


Update of /cvs-repository/Zope/lib/python/ZServer/medusa/notes
In directory cvs.zope.org:/tmp/cvs-serv12650/lib/python/ZServer/medusa/notes

Added Files:
      Tag: chrism-install-branch
	async_blurbs.txt debugging.txt threads.txt tkinter.txt 
Log Message:
Moved ZServer into lib/python.


=== Added File Zope/lib/python/ZServer/medusa/notes/async_blurbs.txt ===

[from the win32 sdk named pipe documentation]

==================================================
The simplest server process can use the CreateNamedPipe function to
create a single instance of a pipe, connect to a single client,
communicate with the client, disconnect the pipe, close the pipe
handle, and terminate. Typically, however, a server process must
communicate with multiple client processes. A server process can use a
single pipe instance by connecting to and disconnecting from each
client in sequence, but performance would be poor. To handle multiple
clients simultaneously, the server process must create multiple pipe
instances.

There are three basic strategies for servicing multiple pipe instances.

· Create multiple threads (and/or processes) with a separate thread
for each instance of the pipe. For an example of a multithreaded
server process, see Multithreaded Server.

· Overlap operations by specifying an OVERLAPPED structure in the
ReadFile, WriteFile, and ConnectNamedPipe functions. For an example of
a server process that uses overlapped operations, see Server Using
Overlapped Input and Output.

· Overlap operations by using the ReadFileEx and WriteFileEx
functions, which specify a completion routine to be executed when the
operation is complete. For an example of a server process that uses
completion routines, see Server Using Completion Routines.

 
The multithreaded server strategy is easy to write, because the thread
for each instance handles communications for only a single client. The
system allocates processor time to each thread as needed. But each
thread uses system resources, which is a potential disadvantage for a
server that handles a large number of clients. Other complications
occur if the actions of one client necessitate communications with
other clients (as for a network game program, where a move by one
player must be communicated to the other players).

With a single-threaded server, it is easier to coordinate operations
that affect multiple clients, and it is easier to protect shared
resources (for example, a database file) from simultaneous access by
multiple clients. The challenge of a single-threaded server is that it
requires coordination of overlapped operations in order to allocate
processor time for handling the simultaneous needs of the clients.

==================================================


=== Added File Zope/lib/python/ZServer/medusa/notes/debugging.txt ===
===========================================================================
			  The Monitor Server
===========================================================================
The monitor server gives the developer a way to get into a server while
it's running.  Here's a quick demonstration of how to get to your server
objects while in the monitor:

[rushing@gnome medusa]$ python monitor_client.py 127.0.0.1 9999
Enter Password: 
Python 1.5 (#47, Jul 27 1998, 00:59:35)  [GCC egcs-2.90.29 980515 (egcs-1.0.3 release)]
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
Welcome to <secure_monitor_channel connected 127.0.0.1:10775 at 81dbdd8>
>>> from __main__ import *
>>> dir()
['CHAT_PORT', 'FTP_PORT', 'HOSTNAME', 'HTTP_PORT', 'IP_ADDRESS', 'MONITOR_PORT', 'PUBLISHING_ROOT', '__builtins__', 'asyncore', 'chat_server', 'cs', 'debug_mode', 'default_handler', 'dh', 'filesys', 'fs', 'ftp', 'ftp_server', 'hs', 'http_server', 'lg', 'logger', 'monitor', 'ms', 'os', 'resolver', 'rs', 'sh', 'status_handler', 'status_objects', 'sys', 'uh', 'unix_user_handler']
>>> ms
<secure_monitor_server listening :9999 at 812a6f8>
>>> rs
<caching_resolver  at 8133890>
>>> hs
<http_server listening :8080 at 81237a0>
>>> dir(hs)
['_fileno', 'accept', 'accepting', 'addr', 'bind', 'bytes_in', 'bytes_out', 'close', 'connect', 'connect_ex', 'dup', 'exceptions', 'family_and_type', 'fileno', 'getpeername', 'getsockname', 'getsockopt', 'handlers', 'ip', 'listen', 'logger', 'makefile', 'port', 'recv', 'recvfrom', 'send', 'sendto', 'server_name', 'server_port', 'setblocking', 'setsockopt', 'shutdown', 'socket', 'total_clients', 'total_requests']
>>> hs.total_clients
<counter value=0 at 8122468>
>>> cs
<chat_server listening :8888 at 8125bf0>
>>> cs.close()
log: closing channel 9:<chat_server listening :8888 at 8125bf0>
>>> 

Use 'exit' or 'Ctrl-D' to close a monitor session.

===========================================================================
			 Emergency Debug Mode
===========================================================================

Bugs and memory leaks in long-running servers may take weeks to show
up.  A useful debugging technique is to leave a crippled or bloated
server running, but to move the servers' sockets to different ports so
that you can start up another fresh copy in the meanwhile.

This allows you to debug the servers without forcing you to leave your
site broken while you do it.

Here's how:

Uncomment the support for '/status/emergency_debug' in <status_handler.py>

When your server goes ballistic, simply hit this URL.  This will shut
down the servers, and restart them on new ports.  Add 10000 to the old
port number, so if you were running a web server on port 80, it will
be moved to 10080.

Start up another copy of your system, which will run on the original
ports.

Now you can debug the errant servers in isolation.  Beats using GDB!



=== Added File Zope/lib/python/ZServer/medusa/notes/threads.txt ===
# -*- Mode: Text; tab-width: 4 -*-

[note, a better solution is now available, see the various modules in
 the 'thread' directory (SMR 990105)]

		  A Workable Approach to Mixing Threads and Medusa.
---------------------------------------------------------------------------

When Medusa receives a request that needs to be handled by a separate
thread, have the thread remove the socket from Medusa's control, by
calling the 'del_channel()' method, and put the socket into
blocking-mode:

    request.channel.del_channel()
    request.channel.socket.setblocking (0)

Now your thread is responsible for managing the rest of the HTTP
'session'.  In particular, you need to send the HTTP response, followed
by any headers, followed by the response body.

Since the most common need for mixing threads and Medusa is to support
CGI, there's one final hurdle that should be pointed out: CGI scripts
sometimes make use of a 'Status:' hack (oops, I meant to say 'header')
in order to tell the server to return a reply other than '200 OK'.  To
support this it is necessary to scan the output _before_ it is sent.
Here is a sample 'write' method for a file-like object that performs
this scan:

HEADER_LINE = regex.compile ('\([A-Za-z0-9-]+\): \(.*\)')

	def write (self, data):
		if self.got_header:
			self._write (data)
		else:
			# CGI scripts may optionally provide extra headers.
			# 
			# If they do not, then the output is assumed to be
			# text/html, with an HTTP reply code of '200 OK'.
			# 
			# If they do, we need to scan those headers for one in
			# particular: the 'Status:' header, which will tell us
			# to use a different HTTP reply code [like '302 Moved']
			#
			self.buffer = self.buffer + data
			lines = string.split (self.buffer, '\n')
			# look for something un-header-like
			for i in range(len(lines)):
				if i == (len(lines)-1):
					if lines[i] == '':
						break
				elif HEADER_LINE.match (lines[i]) == -1:
					# this is not a header line.
					self.got_header = 1
					self.buffer = self.build_header (lines[:i])
					# rejoin the rest of the data
					self._write (string.join (lines[i:], '\n'))
					break


=== Added File Zope/lib/python/ZServer/medusa/notes/tkinter.txt ===

Here are some notes on combining the Tk Event loop with the async lib
and/or Medusa.  Many thanks to Aaron Rhodes (alrhodes@cpis.net) for
the info!

  > Sam,
  > 
  > Just wanted to send you a quick message about how I managed to
  > finally integrate Tkinter with asyncore.  This solution is pretty
  > straightforward.  From the main tkinter event loop i simply added
  > a repeating alarm that calls asyncore.poll() every so often. So
  > the code looks like this:
  > 
  > in main:
  > import asyncore
  > 
  >     self.socket_check()
  > 
  > ...
  > 
  > then, socket_check() is:
  > 
  >     def socket_check(self):
  >         asyncore.poll(timeout=0.0)
  >         self.after(100, self.socket_check)
  > 
  > 
  > This simply causes asyncore to poll all the sockets every 100ms
  > during the tkinter event loop.  The GUI doesn't block on IO since
  > all the IO calls are now handled with asyncore.