[Zope] Re: Zope and mod_python

Tres Seaver tseaver at zope.com
Fri Feb 4 11:39:53 EST 2005


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Pascal Peregrina wrote:
| Hi,
|
| I have successfully (I think) integrated Zope with Apache/mod_python, just
| to make some tests.

Wow!  That is awesome.

| I would like to know if you think that this can be interesting in terms of
| performance (specially number of simultaneous requests being handled,
| compared to one "real" Zope instance, even with number of threads
| increased).

Good questions:  we would need to do a fair amount of difficult testing
to know for sure.  Certainly it is a big win for shops which are already
comfortable with managing large-scale dynamic Apache installations.

| Sorry in advance if I missed something important and if this is not
| interesting at all :)

Not a all.

| By now I have not been able to get the logging working,

Zope's logging relies on having a single "gateway" in the appserver
process which is used by the worker threads.  I think your mod_python
child processes are like Zope worker threads without the "parent" (which
has the gateway).  I wonder if configuring it to use syslog would Just
Work (TM)?

| and I am not
| completely sure about clean shutdown when Apache is stopped, but I will
| continue investigating...

Doesn't Apache / mod_python allow you to register a "graceful shutdown"
callback?

| Software :
| + httpd-2.0.52
| + mod_python-3.1.3
| + Python-2.3.4
| + psyco-1.4 (only to use Zope Speedpack product)
| + Zope 2.7.4
| + Several Zope products (Localizer, CMF 1.5.0, ...)
|
| Architecture :
| + One Zeo instance
| + Apache / mod_python
| and that's all :)
|
| I created a zhandler package (very first draft) containing :
| + __init__.py : Zope init
| + zhandler.py : the handler itself (it uses ZPublisher)
|
| Then, from standard build/install/configuration of all the software above,
| this is the configuration changes I did :
| + Created a Zeo instance
| + httpd.conf (global settings) :
| SetHandler mod_python
| PythonInterpreter zinterpreter
| PythonHandler zhandler.zhandler
| PythonDebug On
| + and this is the zope.conf file (I called it zhandler.conf) I used
| (basically it only consisted in removing all <server> occurences) :
|
############################################################################
| ###
| # Welcome to Zope 2.
|
############################################################################
| ###
| #
| # This is the Zope configuration file.  The Zope configuration file
| # shows what the default configuration directives are, and show
| # examples for each directive.  To declare a directive, make sure that
| # you add it to a line that does not begin with '#'.  Note that comments
| # are only allowed at the beginning of a line: you may not add comments
| # after directive text on the same line.
|
| # ZConfig "defines" used for later textual substitution
|
| %define INSTANCE /export/home/pperegri/20050203/instances/zope_rw
| %define ZOPE /export/home/pperegri/20050203/opt/Zope-2.7.4-0
|
| instancehome $INSTANCE
| products /export/home/pperegri/20050203/opt/Products
| debug-mode off
| zeo-client-name zcmd
|
| <eventlog>
|   level all
|   <logfile>
|     path $INSTANCE/log/cmdline_event.log
|     level info
|   </logfile>
| </eventlog>
|
| <logger access>
|   level WARN
|   <logfile>
|     path $INSTANCE/log/cmdline_Z2.log
|     format %(message)s
|   </logfile>
| </logger>
|
| <zodb_db temporary>
|     mount-point /temp_folder
|     <temporarystorage>
|       name sessions
|     </temporarystorage>
|     container-class Products.TemporaryFolder.TemporaryContainer
| </zodb_db>
|
| <zodb_db remote>
|     mount-point /
|     cache-size 40000
|    <zeoclient>
|      cache-size 256MB
|      server 192.168.11.163:9001
|      read-only off
|      storage 1
|      name zeostorage
|      var $INSTANCE/var
|    </zeoclient>
| </zodb_db>
|
|
| + __init.py__
| __all__ = ['zhandler']
|
|
zope_pythonpath='/export/home/pperegri/20050203/opt/Zope-2.7.4-0/lib/python'
| zope_conf='/export/home/pperegri/test/conf/zhandler.conf'
|
| import sys
| if not zope_pythonpath in sys.path:
|     sys.path.append(zope_pythonpath)
|
| import Zope
| if not Zope._began_startup:
|     Zope.configure('/export/home/pperegri/test/conf/zhandler.conf')
|     Zope.app()
|
|
| + zhandler.py
| import mod_python.apache
| import os
| import sys
|
| # this will save memory
| #os.environ = {}
|
| import ZPublisher
|
| class ZopeStdout(mod_python.apache.NullIO):
|
|     """
|     Class that allows writing to the socket directly for Zope.
|     """
|
|     def __init__(self, req):
|         self.req = req
|         self.pos = 0
|
|     def write(self, result):
|         if not result or self.pos: return
|         pos1=result.find('\r\n\r\n')
|         pos2=result.find('\n\n')
|         if pos1!=-1 and pos2!=-1 and pos1>pos2:
|             ss = result.split('\n\n', 1)
|         else:
|             ss = result.split('\r\n\r\n', 1)
|             if len(ss) < 2:
|                 ss = result.split('\n\n', 1)
|         ss[0]=ss[0].replace('\r\n', '\n')
|         for line in ss[0].split('\n'):
|             if line.find(':')!=-1:
|                 h,v=line.split(':',1)
|                 if h.lower() == "status":
|                     status = int(v.split()[0])
|                     self.req.status = status
|                 elif h.lower() == "content-type":
|                     self.req.content_type = v
|                     self.req.headers_out[h] = v
|                 else:
|                     self.req.headers_out.add(h, v)
|         if len(ss)==2:
|             self.req.write(ss[1])
|             self.pos+=len(ss[1])
|
|     def tell(self): return self.pos
|
| def handler(req):
|     save_env, si, so = mod_python.apache.setup_cgi(req)
|     sys.stdout = ZopeStdout(req)
|     if 'REQUEST_URI' in os.environ:
|         os.environ['PATH_INFO']=os.environ['REQUEST_URI'].split('?')[0]
|         os.environ['PATH_TRANSLATED']=os.environ['PATH_INFO']
|     if 'HTTP_CONNECTION' in os.environ:
|         os.environ['CONNECTION_TYPE']=os.environ['HTTP_CONNECTION']
|     os.environ['SCRIPT_NAME']=''
|     names=os.environ.keys()
|     for name in names:
|         if not name.startswith('HTTP_') and not
name.startswith('PATH_') and
| not name.startswith('SERVER_') and not name in
|
['CONNECTION_TYPE','GATEWAY_INTERFACE','REQUEST_METHOD','REMOTE_ADDR','SCRIP
| T_NAME','QUERY_STRING']:
|             del os.environ[name]
|     try:
|         ZPublisher.publish_module('Main', stdin=sys.stdin,
|                                   stdout=sys.stdout, stderr=sys.stderr,
|                                   environ=os.environ)
|     finally:
|         mod_python.apache.restore_nocgi(save_env, si, so)
|     return mod_python.apache.OK

Very impressive work.

Tres.
- --
===============================================================
Tres Seaver                                tseaver at zope.com
Zope Corporation      "Zope Dealers"       http://www.zope.com
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.4 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org

iD8DBQFCA6VZGqWXf00rNCgRAkhcAJ4m6pDA5q6CkKeYyg2IkXUNLgBc0wCfWWy0
npGh1X7k3Q8kqLmJLfq8q6w=
=zdNn
-----END PGP SIGNATURE-----



More information about the Zope mailing list