Hi.
Monitor.py is old and inconsistent with the current state of the
Appserver; I have rewritten it. In order to use it, you need to use the
patch attached to this email. Monitor.py will only work on Unix.
My patch for ThreadedAppServer.py is useful if you want the monitor
functionality to work at all. There are a few bugs that shipped with
the last few releases, and it prevents you from running the monitor from
within the AppServer.
Monitor.py will now allow you to start, stop and restart the appserver
from the command line. Drop a symlink between Monitor.py and
/etc/init.d, and it will control your AppServer on startup/shutdown.
Monitor.py now support SIGHUP as well (to restart).
For the curious, I have also written a new plugin for WebWare. It's an
alternative to PSP that is more similar to Struts/EJBs. I hope to
release that soon (especially if there is outside interest).
Cheers!
-giles hall
diff -r -C3 Webware-0.8.1/WebKit/Monitor.py Webware-patched/WebKit/Monitor.py
*** Webware-0.8.1/WebKit/Monitor.py Sun May 6 21:53:28 2001
--- Webware-patched/WebKit/Monitor.py Wed Oct 1 12:22:22 2003
***************
*** 1,316 ****
! #!/usr/bin/env python
!
! """
! Fault tolerance system for WebKit
!
! author: Jay Love
!
! This module is intended to provide additional assurance that the AppServer continues
running
! at all times. This module will be reponsible for starting the AppServer, and
monitoring its
! health. It does that by periodically sending a status check message to the
AppServer to ensure
! that it is responding. If it finds that the AppServer does not respond within a
specified time,
! it will start a new copy of the AppServer, after killing the previous process.
!
*******************************************************************************************
! USE:
!
!
! "Monitor.py start"
! or
! "Monitor.py stop"
!
!
! The default AppServer specified below will be used, or you can list the AppServer
you would like after "start".
!
! You can have the whole process run as a daemon by specifying "daemon" after "start"
on the command line.
!
! To stop the processes, run "Monitor.py stop".
!
!
********************************************************************************************
! Future:
! Add ability to limit number of requests served. When some count is reached, send
! a message to the server to save it's sessions, then exit. Then start a new AppServer
! that will pick up those sessions.
!
! It should be possible on both Unix and Windows to monitor the AppServer process in 2
ways:
! 1) The method used here, ie can it service requests?
! 2) is the process still running?
!
! Combining these with a timer lends itself to load balancing of some kind.
!
!
! """
!
!
!
! defaultServer = "AsyncThreadedAppServer"
!
!
import socket
import time
import os
- import asyncore
- import sys
import signal
import string
from marshal import dumps
! global serverName
! serverName = defaultServer
! global srvpid
! srvpid=0
! checkInterval = 10 #add to config if this implementation is adopted, seconds
between checks
! maxStartTime = 120 #seconds to wait for a AppServer to start before killing it and
trying again
! global addr
! global running
! running = 0
!
! statstr = dumps({'format':'STATUS',})
! statstr = dumps(len(statstr)) + statstr
! quitstr = dumps({'format':'QUIT',})
! quitstr = dumps(len(quitstr)) + quitstr
!
! debug = 1
!
!
! def createServer(setupPath=0):
! """Unix only, after forking"""
! print "Starting Server"
!
! import WebKit
! code = 'from WebKit.%s import main' % serverName
! exec code
! main(['monitor',])
!
!
! def startupCheck():
! """
! Make sure the AppServer starts up correctly.
! """
! global debug
! count = 0
! print "Waiting for start"
! time.sleep(checkInterval/2) #give the server a chance to start
! while 1:
! if checkServer(0):
! break
! count = count + checkInterval
! if count > maxStartTime:
! print "Couldn't start AppServer"
! print "Killing AppServer"
! os.kill(srvpid,signal.SIGKILL)
! sys.exit(1)
! print "Waiting for start"
! time.sleep(checkInterval)
!
! def startServer(killcurrent = 1):
! """
! Start the AppServer.
! """
! global srvpid
! global debug
! if os.name == 'posix':
! if killcurrent:
! try: os.kill(srvpid,signal.SIGTERM)
! except: pass
! try: os.waitpid(srvpid,0)
! except: pass
! srvpid = os.fork()
! if srvpid == 0:
! createServer(not killcurrent)
! sys.exit()
!
!
!
! def checkServer(restart = 1):
! """
! Send a check request to the AppServer. If restart is 1, then
! attempt to restart the server if we can't connect to it.
!
! This function could also be used to see how busy an AppServer is by measuring
the delay in
! getting a response when using the standard port.
! """
! global addr
! global running
! try:
! sts = time.time()
! s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
! s.connect(addr)
! s.send(statstr)
! s.shutdown(1)
! resp = s.recv(9) #up to 1 billion requests!
! monwait = time.time() - sts
! if debug:
! print "Processed %s Requests" % resp
! print "Delay %s" % monwait
! return 1
! except:
! print "No Response from AppServer"
! if running and restart:
! startServer()
! startupCheck()
! else:
! return 0
!
!
!
! def main(args):
! global debug
! global serverName
! global running
!
! running = 1
!
! file = open("monitorpid.txt","w")
! if os.name == 'posix':
! file.write(str(os.getpid()))
! file.flush()
! file.close()
! startServer(0)
! try:
! startupCheck()
!
! except Exception, e:
! if debug:
! print "Startup Check Exception %s" % e
! print "Exiting Monitor"
! try: os.kill(srvpid,signal.SIGTERM)
! except: pass
! sys.exit()
!
! while running:
! try:
! if debug:
! print "Checking Server"
! checkServer()
! time.sleep(checkInterval)
! except Exception, e:
! if debug:
! print "Exception %s" % e
! if not running:
! return
! print "Exiting Monitor"
! try: os.kill(srvpid,signal.SIGTERM)
! except: sys.exit(0)
! try: os.waitpid(srvpid,0) #prevent zombies
! except: sys.exit(0)
!
!
!
! def shutDown(arg1,arg2):
! global running
! print "Monitor Shutdown Called"
! running = 0
! global addr
! try:
! s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
! s.connect(addr)
! s.send(quitstr)
! s.shutdown(1)
! resp = s.recv(10)
! s.close()
! print "AppServer response to shutdown request:", resp
! except Exception, e:
! print e
! print "No Response to Shutdown Request, performing hard kill"
! os.kill(srvpid, signal.SIGINT)
! os.waitpid(srvpid,0)
! return 0
!
! import signal
! signal.signal(signal.SIGINT, shutDown)
! signal.signal(signal.SIGTERM, shutDown)
!
! ######################################################################
!
! def stop():
! pid = int(open("monitorpid.txt","r").read())
! os.kill(pid,signal.SIGINT) #this goes to the other running instance
of this module
! #######################################################################
def usage():
! print """
! This module serves as a watcher for the AppServer process.
! The required command line argument is one of:
! start: Starts the monitor and default appserver
! stop: Stops the currently running Monitor process and the AppServer it is running.
This is the only way to stop the process other than hunting down the individual
process ID's and killing them.
!
! Optional arguments:
! "AppServerClass": The AppServer class to use (AsyncThreadedAppServer or
ThreadedAppServer)
! daemon: If "daemon" is specified, the Monitor will run in the background.\n
!
! """
!
! arguments = ["start", "stop"]
! servernames = ["AsyncThreadedAppServer", "ThreadedAppServer"]
! optionalargs = ["daemon"]
if __name__=='__main__':
! global addr
!
! if os.name != 'posix':
! print "This service can only be run on posix machines (UNIX)"
! sys.exit()
!
! if len(sys.argv) == 1:
! usage()
! sys.exit()
!
! args = sys.argv[1:]
! if args[0] not in arguments:
! usage()
! sys.exit()
!
! if 1: ##setupPath:
! if '' not in sys.path:
! sys.path = [''] + sys.path
! try:
! import WebwarePathLocation
! wwdir =
os.path.abspath(os.path.join(os.path.dirname(WebwarePathLocation.__file__),".."))
! except Exception, e:
! print e
! usage()
! if not wwdir in sys.path:
! sys.path.insert(0,wwdir)
! sys.path.remove('')
! try:
! sys.path.remove('.')
! except:
! pass
!
!
! cfg = open(os.path.join(wwdir,"WebKit","Configs/AppServer.config"))
! cfg = eval(cfg.read())
! addr = ((cfg['Host'],cfg['Port']-1))
!
!
! if 'stop' in args:
! stop()
! sys.exit()
!
! for i in servernames:
! if i in args:
! serverName=i
!
!
! if 'daemon' in args: #fork and become a daemon
! daemon=os.fork()
! if daemon:
! sys.exit()
!
!
! main(args)
!
!
!
!
!
!
!
!
!
!
!
--- 1,165 ----
! #!/usr/bin/env python2.3
+ import sys
import socket
import time
import os
import signal
import string
from marshal import dumps
! ## USER CONFIG
+ serverclass = 'ThreadedAppServer'
+ wwpath = '/usr/share/webware'
+ appath = '/usr/share/disco'
+ interval = 10
+ try_limit = 100
+
+ ## END USER CONFIG
+
+ sys.path.append( appath + '/lib' )
+ sys.path.insert( 0, wwpath )
+ os.chdir( appath )
+
+ STATSTR = dumps( {'format':'STATUS'} )
+ STATSTR = dumps( len( STATSTR )) + STATSTR
+ QUITSTR = dumps( {'format':'QUIT'} )
+ QUITSTR = dumps( len( QUITSTR )) + QUITSTR
+
+ instance = None
+ debug_flag = False
+
+ class Monitor( object ):
+ is_running = False
+ do_restart = False
+
+ def __init__( self ):
+ for glbl in ('wwpath','appath','interval','try_limit', 'serverclass'):
+ setattr( self, glbl, globals()[glbl] )
+ cfg = file( os.path.join( self.appath, "Configs/AppServer.config" ))
+ cfg = eval( cfg.read() )
+ self.address = ((cfg['Host'],cfg['Port']-1))
+
+ def start( self ):
+ tryc = 0
+ while True and tryc < self.try_limit:
+ tryc += 1
+ pid = os.fork()
+ if pid:
+ # parent
+ time.sleep( self.interval / 2 )
+ if not self.is_alive():
+ debug( "failed!\n" )
+ debug( "Server did not start on try #%d\n" % tryc )
+ try: os.kill( pid, signal.SIGTERM )
+ finally: os.waitpid( pid, 0 )
+ else:
+ debug( "succeeded!\n" )
+ self.is_running = True
+ break
+ else:
+ # child
+ from WebKit.Launch import launchWebKit
+ debug( "Attemping to start server ..." )
+ launchWebKit( self.serverclass, self.appath, ['monitor'])
+ sys.exit( 0 )
+
+ def stop( self ):
+ self.is_running = False
+ if not self.is_alive():
+ debug( "Could not stop appserver, it's not running!\n" )
+ return 0
+ try:
+ s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ s.connect( self.address )
+ s.send( QUITSTR )
+ #s.shutdown( 1 )
+ resp = s.recv( 10 )
+ s.close()
+ debug( "AppServer response to shutdown request: %s\n" % resp )
+ except Exception, e:
+ import traceback
+ traceback.print_exc()
+ debug( "No Response to Shutdown Request, performing hard kill\n" )
+ pid = int( open( "appserverpid.txt", "r" ).read() )
+ try: os.kill( pid, signal.SIGINT )
+ finally: os.waitpid( pid, 0 )
+ return 0
+
+ def restart( self ):
+ self.stop()
+ self.start()
+ self.do_restart = False
+
+ def is_alive( self ):
+ try:
+ sts = time.time()
+ s = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
+ s.connect( self.address )
+ s.send( STATSTR )
+ #s.shutdown(1)
+ resp = s.recv( 9 )
+ mewait = time.time() - sts
+ debug( "ALIVE: [ Requests: %s ][ Delay: %s ]\n" % (resp, mewait) )
+ return True
+ except:
+ debug( "** AppServer appears to be dead **\n" )
+ return False
+
+ def loop( self ):
+ fd = file( "monitorpid.txt", "w" )
+ fd.write( str( os.getpid() ))
+ fd.close()
+ self.start()
+
+ while self.is_running:
+ time.sleep( self.interval )
+ if not self.is_running: break
+ if self.do_restart: self.restart()
+ if self.is_alive(): continue
+ else:
+ debug( "** Restarting AppServer **\n" )
+ self.start()
+
+ def stop( *args ): instance.stop()
+ signal.signal( signal.SIGINT, stop )
+ signal.signal( signal.SIGTERM, stop )
! def restart( *args ): instance.do_restart = True
! signal.signal( signal.SIGHUP, restart )
def usage():
! print "%s start | stop | restart" % sys.argv[0]
+ def debug( msg ):
+ if debug_flag:
+ sys.stdout.write( "(MON) %s" % msg )
+ sys.stdout.flush()
if __name__=='__main__':
! assert os.name == 'posix', "This will only run on UNIX"
!
! if len( sys.argv ) == 1:
! usage()
! sys.exit()
!
! pid = int( open( "monitorpid.txt", "r" ).read() )
! args = sys.argv[1:]
! if '-d' in args:
! debug_flag = True
! if 'stop' in args:
! os.kill( pid, signal.SIGINT )
! sys.exit( 0 )
! if 'restart' in args:
! os.kill( pid, signal.SIGHUP )
! sys.exit( 0 )
! if 'start' in args:
! if not debug_flag:
! pid = os.fork()
! if pid: sys.exit( 0 )
! instance = Monitor()
! instance.loop()
! sys.exit( 0 )
! usage()
! sys.exit( -1 )
diff -r -C3 Webware-0.8.1/WebKit/NewThreadedAppServer.py
Webware-patched/WebKit/NewThreadedAppServer.py
*** Webware-0.8.1/WebKit/NewThreadedAppServer.py Tue Dec 10 16:30:19 2002
--- Webware-patched/WebKit/NewThreadedAppServer.py Wed Oct 1 12:22:22 2003
***************
*** 132,138 ****
else: raise
for sock in input:
! print sock.getsockname()
self._requestID += 1
client, addr = sock.accept()
serverAddress = sock.getsockname()
--- 132,138 ----
else: raise
for sock in input:
! #print sock.getsockname()
self._requestID += 1
client, addr = sock.accept()
serverAddress = sock.getsockname()
***************
*** 140,145 ****
--- 140,147 ----
handler =
self._handlerCache[serverAddress].pop()
except IndexError:
handler =
self._socketHandlers[serverAddress](self, serverAddress)
+ # The monitor connections can be relatively frequent
+ if (handler.protocolName != 'monitor') or debug: print
sock.getsockname()
handler.activate(client, self._requestID)
self._requestQueue.put(handler)
***************
*** 383,412 ****
class MonitorHandler(Handler):
! protcolName = 'monitor'
def handleRequest(self):
-
- verbose = self.server._verbose
startTime = time.time()
- if verbose:
- print 'BEGIN REQUEST'
- print time.asctime(time.localtime(startTime))
conn = self._sock
- if verbose:
- print 'receiving request from', conn
-
BUFSIZE = 8*1024
-
dict = self.receiveDict()
-
if dict['format'] == "STATUS":
! conn.send(str(self.server._reqCount))
!
if dict['format'] == 'QUIT':
conn.send("OK")
conn.close()
! self.server.shutDown()
from WebKit.ASStreamOut import ASStreamOut
--- 385,403 ----
class MonitorHandler(Handler):
! protocolName = 'monitor'
def handleRequest(self):
startTime = time.time()
conn = self._sock
BUFSIZE = 8*1024
dict = self.receiveDict()
if dict['format'] == "STATUS":
! conn.send(str(self._server._reqCount))
if dict['format'] == 'QUIT':
conn.send("OK")
conn.close()
! self._server.initiateShutdown()
from WebKit.ASStreamOut import ASStreamOut
diff -r -C3 Webware-0.8.1/WebKit/ThreadedAppServer.py
Webware-patched/WebKit/ThreadedAppServer.py
*** Webware-0.8.1/WebKit/ThreadedAppServer.py Sat Jan 18 10:00:00 2003
--- Webware-patched/WebKit/ThreadedAppServer.py Wed Oct 1 12:22:22 2003
***************
*** 418,424 ****
if dict['format'] == 'QUIT':
conn.send("OK")
conn.close()
! self.server.shutDown()
--- 418,424 ----
if dict['format'] == 'QUIT':
conn.send("OK")
conn.close()
! self.server.initiateShutdown()