Monday, January 28, 2008

Django|一个py文件搭建的django站点

dpaste: #31935: jngo.py, by pbx

jngo.py pasted by pbx, 17:23 January 21st

Syntax: Python
    ‣ Plain   ‣ HTML   ‣ Copy
  1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40  41  42  43  44  45  46  47  48  49  50  51  52  53  54  55  56  57  58  59  60  61  62  63  64  65  66  67  68  69  70  71  72  73  74  75  76  77  78  79  80  81  82  83  84  85  86  87  88  89  90  91  92  93  94  95  96  97  98  99 100 101 102 103 104 105
#!/usr/bin/env python """ jngo -- The unhealthily compressed Django application.  Usage: ./jngo.py  Assuming a working install of Django (http://djangoproject.com/) and SQLite (http://sqlite.org), this script can be executed directly without any other  preparations -- you don't have to do `setup.py install`, it doesn't  need to be on your Python path, you don't need to set DJANGO_SETTINGS_MODULE, you don't need a webserver. You don't even need content -- the first time it's  run, it will create a SQLite database in the same directory as the script  and populate it with sample pages.  Features:  * Editable content on all pages * Dynamically generated navigation buttons * Optional private-access pages * Optional per-page comments * RSS feed of latest comments, with autodiscovery  Author: Paul Bissex <pb@e-scribe.com> URL: http://news.e-scribe.com/ License: MIT  FAQS:   Q: Should I use this as an example of excellent Django coding practices? A: Um, no. This is pretty much the opposite of excellent Django coding practices.  Q: Why did you do such a terrible thing? A: At first, it was just a perverse experiment. It ended up being a good way to refresh my memory on some Django internals, by trying all kinds of things that broke in weird ways. """  #--- Settings --- NAME = ROOT_URLCONF = "jngo" DEBUG = TEMPLATE_DEBUG = True SITE_ID = 3000 HOSTNAME_AND_PORT = "127.0.0.1:8000" DATABASE_ENGINE = "sqlite3" DATABASE_NAME = NAME + ".db" INSTALLED_APPS = ["django.contrib.%s" % app for app in "auth admin contenttypes sessions sites flatpages comments".split()] TEMPLATE_LOADERS = ('django.template.loaders.app_directories.load_template_source', NAME + '.template_loader') MIDDLEWARE_CLASSES = ('django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',  'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware') TEMPLATE_CONTEXT_PROCESSORS = (NAME + '.context_processor', "django.core.context_processors.auth", "django.core.context_processors.request")  #--- Template loader and templates --- def template_loader(t, _):     from django.template import TemplateDoesNotExist     try:         return {             'base.html': """<html><head><title>{{ flatpage.title }}</title><link rel='alternate' type='application/rss+xml' href='/feed/'><style type="text/css">body { margin: 15px 50px; background: #eee; color: #343; font-family: sans-serif; } ul { padding: 0; } li { display: inline; background: #383; padding: 4px 8px; margin: 3px; } li:hover { background: #252; } dd { border-bottom: 1px dotted #666; } a { color: #383; text-decoration: none; } li a { color: #fff; } .anav { background: #141; } .rnav a { color: #ff4; } .error { color: #e22; } #footer { border-top: 1px dotted #555; font-size: 80%; color: #555; margin-top: 15px } #comments { background: #ddd; margin-top: 20px; padding: 10px; } dt { font-weight: bold; margin-top: 1em; }</style></head><body><ul>{% for nav in navs %}<li class="{% ifequal nav.url flatpage.url %}anav {% endifequal %}{% if nav.registration_required %}rnav {% endif %}"><a href="{{ nav.url }}">{{ nav.title }}</a></li>{% endfor %}</ul>{% block content %}{% endblock %}<div id="footer">{% if request.user.is_staff %}<a href="javascript:(function(){if(typeof%20ActiveXObject!='undefined'){var%20x=new%20ActiveXObject('Microsoft.XMLHTTP')}else%20if(typeof%20XMLHttpRequest!='undefined'){var%20x=new%20XMLHttpRequest()}else{return;}x.open('GET',location.href,false);x.send(null);try{var%20type=x.getResponseHeader('x-object-type');var%20id=x.getResponseHeader('x-object-id');}catch(e){return;}document.location='/admin/'+type.split('.').join('/')+'/'+id+'/';})()">Edit this page</a> (as staff user <a href="/admin/">{{ request.user }}</a>)<br>{% endif %}Powered by <a href="http://djangoproject.com/">Django</a> {{ version }}<br></div></body></html>""",             'flatpages/default.html': """{% extends "base.html" %}{% load comments %}{% block content %}<h1>{{ flatpage.title }}</h1>{{ flatpage.content }}{% if flatpage.enable_comments %}<div id="comments">{% get_free_comment_list for flatpages.flatpage flatpage.id as comments %}<h3>Comments!</h3><dl>{% for comment in comments %}{% include "comment.html" %}{% endfor %}</dl>{% free_comment_form for flatpages.flatpage flatpage.id %}</div>{% endif %}{% endblock %}""",             'comments/free_preview.html': """{% extends "base.html" %}{% block content %}<h1>Comment preview</h1><dl>{% include "comment.html" %}</dl><form action='.' method='post'><input type='hidden' name='gonzo' value='{{ hash }}'><input type='hidden' name='options' value='{{ options }}'><input type='hidden' name='comment' value='{{ comment.comment }}'><input type='hidden' name='person_name' value='{{ comment.person_name }}'><input type='hidden' name='target' value='{{ target }}'><input type='submit' name='post' value='Post comment'></form>{% endblock %}""",              'comments/posted.html': """{% extends "base.html" %}{% block content %}<h1>Comment posted</h1><p>Thanks for posting!</p> <p><a href='{{ object.get_absolute_url }}'>Continue</a></p>{% endblock %}""",             'comment.html': """<dt>{{ comment.person_name }} said:</dt> <dd>{{ comment.comment }}</dd>""",             'registration/login.html': """{% extends "base.html" %}{% block content %}{% if form.has_errors %}<h2 class="error">Wrong!</h2>{% endif %}<p>This page is top secret, so you need to log in.</p><form method="post" action=".">Username: {{ form.username }}<br>Password: {{ form.password }}<br><input type="submit" value="login"><input type="hidden" name="next" value="{{ next }}"></form>{% endblock %}"""             }[t], ''     except KeyError:         raise TemplateDoesNotExist template_loader.is_usable = True  #--- Context processor --- def context_processor(request):     from django.contrib.flatpages.models import FlatPage     navs = FlatPage.objects.all().values("url", "title", "registration_required")     from django import get_version     return { 'navs': navs, 'version': get_version() }  #--- RSS Feed (hacky wrapper function needed because of jngo's one-file setup) --- def feed(*args, **kwargs):     from django.contrib.comments.feeds import LatestFreeCommentsFeed     return LatestFreeCommentsFeed(*args, **kwargs)  #--- URLconf --- from django.conf.urls.defaults import * urlpatterns = patterns("",      (r"^admin/", include("django.contrib.admin.urls")),      (r"^comments/", include("django.contrib.comments.urls.comments")),      (r"^accounts/login/$", "django.contrib.auth.views.login"),     (r"^(feed)/$", "django.contrib.syndication.views.feed", {'feed_dict': {'feed': feed}}),     )  #--- Execution --- if __name__ == "__main__":     import os, sys     from django.core.management import call_command     here = os.path.dirname(__file__)     sys.path.append(here)     os.environ["DJANGO_SETTINGS_MODULE"] = NAME     if not os.path.isfile(os.path.join(here, DATABASE_NAME)):         from django.contrib.auth.create_superuser import createsuperuser         from django.contrib.flatpages.models import FlatPage         from django.contrib.sites.models import Site         call_command("syncdb")         createsuperuser()         site_obj = Site.objects.create(id=SITE_ID, domain=HOSTNAME_AND_PORT)         FlatPage.objects.create(url="/", title="Home", content="Welcome to %s!" % NAME).sites.add(site_obj)         FlatPage.objects.create(url="/stuff/", enable_comments=True, title="Stuff", content="This is a page about stuff.").sites.add(site_obj)         FlatPage.objects.create(url="/topsecret/", title="Top Secret", content="Now you know.", registration_required=True).sites.add(site_obj)     call_command("runserver", HOSTNAME_AND_PORT) 
Hold — this paste will be retained until nobody has looked at it for 30 days.

Sunday, January 27, 2008

Jython|Writing Servlets in Jython

Jython Webapp Tutorial - Part 1 - Writing Servlets in Jython

Jython Webapp Tutorial - Part 1 - Writing Servlets in Jython

Sean McGrath
http://www.propylon.com
http://seanmcgrath.blogspot.com
10 October 2004

Tutorial Start Page

Here is what we are going to cover in this section of the tutorial:

Pre-requisites

You must have a Java Development Kit installed on your machine. I used JDK 1.4.1_05 which is installed in d:\j2sdk1.4.1_05 on my machine. Make sure you can compile java programs sucessfully on your platform. When working with dynamic languages such as jython on the JVM, it is important to be able to distinguish between problems at the basic Java level and problems at the dynamic language level.

Installing Jython

Download Jython from www.jython.org/download.html

Jython is shipped as a single class file which you run in order to install it on your system.

The installation instructions are straighforward: www.jython.org/install.html

As always, there are platform-specific nuances. Check out your platform on /www.jython.org/platform.html.

My setup is that I have Jython installed in d:\jython-2.1. I have added that directory onto my Windows path so that I can invoke Jython from the command line from any directory.

When you first fire up Jython from a command window it will search through your classpath and create a cache of all the stuff it finds there.

You should see something like this: (click for large version)

You will find yourself at the Jython command prompt: Jython 2.1 on java1.4.1_05 (JIT: null) Type "copyright", "credits" or "license" for more information. >>>>

Welcome to interactive Java :-)

Note to emacs users. Jython works just fine from the emacs shell. Unlike CPython, it does not require the "-i" switch in order to recognize an interactive session. No, I don't know why that is.

Now we can do something predictable and boring: >>>print 1+2 3

You are stunned into silence, I know.

To get out of Jython's interactive shell you can press ^c twice or press ^d once. I prefer the former because the latter causes Emacs to close its interactive shell window. ^z doesn't work for me on Windows. No, I don't know why that is.

Pedants, purists and epicures may prefer: >>> raise SystemExit

Which closes Jython gracefully.

Now for something more interesting. Fire up Jython again and try this: >>>import java.lang.String

Nothing happens. That's good. You have now succesfully loaded the Java String class into Jython. Because we have an interactive shell, we can poke around to find stuff. Try this:

>>>dir (java.lang.String)

This time you will get output. Something like this:

['CASE_INSENSITIVE_ORDER', 'bytes', 'compareTo', 'compareToIgnoreCase', 'concat',<br> 'contentEquals', 'copyValueOf', 'endsWith', 'equalsIgnoreCase', 'getBytes', 'getChars',<br> 'indexOf', 'intern', 'lastIndexOf', 'matches', 'regionMatches', 'replace', 'replaceAll',<br> 'replaceFirst', 'split', 'startsWith', 'substring', 'toCharArray', 'toLowerCase',<br> 'toUpperCase', 'trim', 'valueOf']

This is a list of all the attributes of the java.lang.String class that Jython can see. Now lets make use of that information:

First, create a java.lang.String object called S. Initialize it to the obligatory "Hello world":

>>> S = java.lang.String("Hello world")

Now convert the string to lowercase using the toLowerCase method:

>>> S.toLowerCase() 'hello world'

Cute eh? This is the sort of seamless integration with Java classes that brings so much power to Jython and greases the wheels of application development and debugging something wonderfull.

So now that Jython is up and running, we turn our attention to Tomcat.

Installing Tomcat

You can download Tomcat from jakarta.apache.org/tomcat/

The version I used is Tomcat 5-0-28. I have installed mine in d:/data/tomcat-5-0-28.

You need to set the CATALINA_HOME environment variable to point to the root of your Tomcat install. You also need the JAVA_HOME environment variable pointing at your Java install but you probably already have that.

Start up Tomcat by executing the startup command in the bin subdirectory of your Tomcat install. You should see something like this:

Tomcat fires up a second window where all subsequent action takes place. It should look something like this:

Now to point a web browser at it. By default, Tomcat fires up on port 8080. Point at browser at localhost:8080 and you should see this:

So far, so far. Now lets proceed to create a Java servlet the classical (baroque?) way in order to make sure that all our Java kit is functioning as required.

Configuring Tomcat to run Jython Servlets

First things first. We need to ensure that we can run Java servlets before we venture into Jython servlets.

Make sure that your Java compiler can see the javax.servlet jar file that Tomcat provides. It lives in common\lib\servlet-api.jar.

You can put this on your CLASSPATH or you can specify it on the command line to javac like this:

javac -classpath %CLASSPATH%;%CATALINA_HOME%\common\lib\servlet-api.jar

Create a subdirectory in %CATALINA_HOME\webapps called jythondemo.

Create a WEB-INF subdirectory and then create a classes subdirectory of WEB-INF.

In the classes directory, create a Java file called ServletTest.java with this code:

import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class ServletTest extends HttpServlet { public void doGet (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } public void doPost (HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType ("text/html"); PrintWriter toClient = response.getWriter(); toClient.println ("<html><head><title>Servlet Test</title>" + "<body><h1>Servlet Test</h1></body></html>"); } }

This thorougly unexciting Servlet simply creates a HTML test page to be sent back to the browser.

Compile it into a .class file. By default, the .class file will be in the same directory. That is fine for our purposes. Now we need to tell Tomcat about our jythondemo application. We do this by creating a web.xml file in the WEB-INF subdirectory of the jythondemo directory. Here are the required contents of the web.xml file:

<web-app> <servlet> <servlet-name>ServletTest</servlet-name> <servlet-class>ServletTest</servlet-class> </servlet> <servlet-mapping> <servlet-name>ServletTest</servlet-name> <url-pattern>/ServletTest</url-pattern> </servlet-mapping> </web-app>

Shutdown your Tomcat process if you have not already done so and start it up again. The shutdown command is: %CATALINA_HOME\bin\shutdown

When you bring Tomcat back up again, the following URL should work : localhost:8080/jythondemo/ServletTest.

All going well, you will see this:

Now we can proceed to create the Jython version of this servlet. Jython servlets work by means of an intermediate Java servlet know as PyServlet. Simply put, this is the servlet that Tomcat runs and it, in turn, invokes Jython programs. Got it? So, the first thing we need to do is tee up PyServlet.

PyServlet lives in jython.jar. The preferred way to make jython.jar available to you webapps it to put it in a WEB-INF/lib subdirectory of your context - in this case in a lib sub directory of the jythondemo directory : jythondemo/WEB-INF/lib.

Now we tell Tomcat about PyServlet and tell it that PyServlet is to be invoked whenever a request for a resource matching *.py is made. To do that, we make the following additions to the web.xml file:

<servlet> <servlet-name>PyServlet</servlet-name> <servlet-class>org.python.util.PyServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>PyServlet</servlet-name> <url-pattern>*.py</url-pattern> </servlet-mapping>

The full web.xml now looks like this:

<web-app> <servlet> <servlet-name>ServletTest</servlet-name> <servlet-class>ServletTest</servlet-class> </servlet> <servlet> <servlet-name>PyServlet</servlet-name> <servlet-class>org.python.util.PyServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>ServletTest</servlet-name> <url-pattern>/ServletTest</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>PyServlet</servlet-name> <url-pattern>*.py</url-pattern> </servlet-mapping> </web-app>

Now it is time to create a Jython equivalent of the Java Servlet above. Here it is (JythonServlet1.py): from javax.servlet.http import HttpServlet class JythonServlet1 (HttpServlet): def doGet(self,request,response): self.doPost (request,response) def doPost(self,request,response): toClient = response.getWriter() response.setContentType ("text/html") toClient.println ("<html><head><title>Servlet Test</title>" + "<body><h1>Servlet Test</h1></body></html>")

Is that it? I hear you say? Yes. Put it into the jythondemo subdirectory.

Restart your Tomcat one more time just as before. Now this URI should work: localhost:8080/jythondemo/JythonServlet1.py

Same output as before right? Now comes the fun part. Change the servlet in some trivial way. e.g. change the text in the HTML page it produces. What do you need to do to re-deploy the changed Jython servlet. These three things:

  1. zilch
  2. nada
  3. nothing

What do you need to do in order to deploy a second Jython servlet, called, says JythonServlet2.py? Apart from creating the file and putting it into the jythondemo directory, you have to do these three things:

  1. zilch
  2. nada
  3. nothing

Welcome to Java rapid development :-)

You might want to take a moment or two to contemplate the significance of the simplcity of all this. Think how easy you can make it to change stuff in installations of your software by making some/all of the servlets .py files. "Configuration files" takes on a whole new meaning doesn't it?

Running Jython Servlets from the command line

Python (and thus Jython) takes the concept of application testing very seriously indeed. As well as being sound engineering practice, application testing is critical in dynamically typed programming languages because so much of the language machinery executes at run time. (There is actually a compile-time step in Jython execution. Behind the scences, the jython interpreter creates JVM byte code on the fly. You don't see it happening though.)

We won't get into a debate about the merits/de-merits of dynamic typing. I'm a firm advocate of dynamic typing and my shortest argument as to why I advocate it is this:-

Now you either believe this or you don't. If you don't - nothing I could say here will convince you otherwise. All I can say is, if you have never tried a dynamic typing approach, stick with me until the end of this tutorial. At least try it once. You will not be sorry. What is the worst that can happen? You will end up with more ammunition to fight the dynamic typing zealots. How bad can that be?

A useful device for testing servlets is to be able to run them "in batch" without requiring the machinery of a servlet engine. Setting up a scenario in which servlets can be executed standalone is very simple with Jython. Take a look at the following Jython Servlet: JythonServlet2.py:

import sys from java.io import * from javax.servlet.http import HttpServlet from JythonServletUtils import * class JythonServlet2 (HttpServlet): def doGet(self,request,response): self.doPost (request,response) def doPost(self,request,response): toClient = response.getWriter() response.setContentType ("text/html") toClient.println ("<html><head><title>Servlet Test 2</title>" + "<body><h1>Servlet Test 2</h1></body></html>") if __name__ == "__main__": JS2 = JythonServlet2() dummyRequest = DummyHttpRequest() dummyResponse = DummyHttpResponse() JS2.doPost (dummyRequest,dummyResponse)

The last 5 lines are a test harness for the servlet. Simply put, when Jython executes this program from the command line it will execute these 4 lines:

JS2 = JythonServlet2() dummyRequest = DummyHttpRequest() dummyResponse = DummyHttpResponse() JS2.doPost (dummyRequest,dummyResponse)

If it isn't - either because this program has been imported into another program or because it is being executed by a servlet engine - these lines are not executed. What do these lines do?

In English this is what is going on:

When executed this servlet produces the following output: D>jython JythonServlet2.py Content-Type:text/html <html><head><title>Servlet Test 2</title><body><h1>Servlet Test 2</h1></body></html>

There is no need for Tomcat to be running for this to work. We are faking it in the interests of having a simple, robust testing strategy for servlets

So, all the magic to make this work must be in the JythonServletUtils module right? Well, here it is, there is no magic:

from java.lang import System class DummyHttpRequest: pass class DummyHttpResponse: def setContentType(self,t): System.out.println ("Content-Type:%s" % t) def getWriter (self): return System.out

All I have done is fake up a couple of classes and implemented the few methods that my servlets actually use. Obviously, I can extend this with more methods from the HttpRequest/HttpResponse classes as required. I have complete flexibility in what these fake methods do so I can tee them up to fit by test strategy.

In this case the key thing I have done is to say that calling getWriter on the HttpResponse object will return stdout and thus my servlets can run standalone writing their output to stdout rather than to servlet containers.

Accessing the Jython library

Jython ships with a bevvy of useful modules. These, combined with all the class files and jar files of the entire world create the vista of code re-use opportunities perused by the discerning Jythoneer.

Take Jython's calendar module for example. It prints nicely formatted calendars. I can never remember the details of what classes/methods it has. I don't need to, to be honest. I just fire up Jython and poke around:

Lets fire up Jython...

Jython 2.1 on java1.4.1_05 (JIT: null) Type "copyright", "credits" or "license" for more information. >>>

I know the module is called calendar. Lets take a look...

>>> import calendar >>> dir(calendar) ['EPOCH', 'FRIDAY', 'February', 'January', 'MONDAY', 'SATURDAY', 'SUNDAY', 'THURSDAY', 'TUESDAY', 'WEDNESDAY', '__all__', '__doc__', '__file__', '__name__', '_center', '_colwidth', '_firstweekday', '_spacing', 'calendar', 'day_abbr', 'day_name', 'error', 'firstweekday', 'format3c', 'format3cstring', 'isleap', 'leapdays', 'localtime', 'mdays', 'mktime', 'month', 'month_abbr', 'month_name', 'monthcalendar', 'monthrange', 'prcal', 'prmonth', 'prweek', 'setfirstweekday', 'timegm', 'week', 'weekday', 'weekheader']

The calendar thing looks promising. I wonder what it is?

>>> type (calendar.calendar) <jclass org.python.core.PyFunction at 15530730>

Hmmm. Its a function. Sounds promising. Any docstrings I wonder?

>>> calendar.calendar.__doc__ "Returns a year's calendar as a multi-line string."

Sounds good. Lets have one of them...

>>> c = calendar.calendar() Traceback (innermost last): File "<console>", line 1, in ? TypeError: calendar() takes at least 1 argument (0 given)

Oops! It needs at least one argument. What are the chances that the first parameter is a year?

>>> c = calendar.calendar(2004)

So far, so good. What type is c?

>>>type(c) <jclass org.python.core.PyString at 25002217>

Houston? We have a string! Lets take a look...

>>> c ' 2004 Jauary February March Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su 1 2 3 4 1 1 2 3 4 5 6 7 5 6 7 8 9 10 11 2 3 4 5 6 7 8 8 9 10 11 12 13 14 12 13 14 15 16 17 18 9 10 11 12 13 14 15 15 16 17 18 19 20 21 19 20 21 22 23 24 25 16 17 18 19 20 21 22 22 23 24 25 26 27 28 26 27 28 29 30 31 23 24 25 26 27 28 29 29 30 31 April May June Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su 1 2 3 4 1 2 1 2 3 4 5 6 5 6 7 8 9 10 11 3 4 5 6 7 8 9 7 8 9 10 11 12 13 12 13 14 15 16 17 18 10 11 12 13 14 15 16 14 15 16 17 18 19 20 19 20 21 22 23 24 25 17 18 19 20 21 22 23 21 22 23 24 25 26 27 26 27 28 29 30 24 25 26 27 28 29 30 28 29 30 31 July August September Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su 1 2 3 4 1 1 2 3 4 5 5 6 7 8 9 10 11 2 3 4 5 6 7 8 6 7 8 9 10 11 12 12 13 14 15 16 17 18 9 10 11 12 13 14 15 13 14 15 16 17 18 19 19 20 21 22 23 24 25 16 17 18 19 20 21 22 20 21 22 23 24 25 26 26 27 28 29 30 31 23 24 25 26 27 28 29 27 28 29 30 30 31 October November December Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su Mo Tu We Th Fr Sa Su 1 2 3 1 2 3 4 5 6 7 1 2 3 4 5 4 5 6 7 8 9 10 8 9 10 11 12 13 14 6 7 8 9 10 11 12 11 12 13 14 15 16 17 15 16 17 18 19 20 21 13 14 15 16 17 18 19 18 19 20 21 22 23 24 22 23 24 25 26 27 28 20 21 22 23 24 25 26 25 26 27 28 29 30 31 29 30 27 28 29 30 31 '

So, how hard would it be to invoke this function from within a Servlet? No problem at all as long as Tomcat knows where the Jython library of goodies lives. It is a good idea to create self-contained servlet contexts - it makes them more portable. To this end, we will hoof all the Jython library stuff into the jythondemo context. The place to put it all is jythondemo\WEB-INF\lib\Lib

I normally just xcopy over the entire \jython2-1\lib directory and suggest you do the same.

You should at this stage have the following directory structure:

jythondemo WEB-INF classes lib cachedir packages Lib cachedir packages encodings jxxload_help pawt test xml dom ext reader html parsers xmlproc sax drivers2 utils

To test that it we have access to the Jython library, here is a servlet JythonServlet3.py that returns a calendar for the current year as its response. Notice the use of the test harness mentioned previously. This allows me to test the servlet outside of Tomcat. Note also the use of the Jython time module which provides a localtime function. The first element in the list returned by localtime is the current year.

import sys,calendar,time from java.io import * from javax.servlet.http import HttpServlet from JythonServletUtils import * class JythonServlet3 (HttpServlet): def doGet(self,request,response): self.doPost (request,response) def doPost(self,request,response): toClient = response.getWriter() response.setContentType ("text/html") toClient.println ("<html><head><title>Servlet Test 3</title>") toClient.println ("<body><h1>Calendar</h1><pre>%s</pre></body></html>" % calendar.calendar(time.localtime()[0])) if __name__ == "__main__": JS3 = JythonServlet3() dummyRequest = DummyHttpRequest() dummyResponse = DummyHttpResponse() JS3.doPost (dummyRequest,dummyResponse)

Pointing a webbrowser at localhost:8080/JythonServlet3.py should product this output:

Summary and pointers to other stuff

In this section we have seen how to set up Jython to work within Tomcat. The same procedure - with a few tweaks - will make it work in any Servlet container. We have seen how easy it is to modify existing servlets and add new ones once Jython is properly set up. We have seen how to set up servlets so that they can easily be tested outside of a servlet container environment. Finally, we have seen a few examples of how poking around with Jython's interactive mode really speeds up finding stuff and finding out how it works.

A full treatment of Jython Servlets would take a whole book. Some of the fun stuff that we have not covered:

  • You can compile jython to native java class files using the Jython compiler - jythonc.
  • You can use Jython in JSP pages
  • You can implement beans (of all forms) in Jython. For example, you can create beans for use in JSP pages.
  • You can implement tag libraries in Jython

In the next section of this tutorial, we will look at accessing the rich and varied and mission critical world of asynchronous messaging via the JMS API from Jython. Actually, we might do some XML work first. I haven't decided yet.


Tutorial Start Page

Thursday, January 24, 2008

Danjango| ServerArrangements - Django

ServerArrangements - Django Code - Trac

Server arrangements

Because Django uses WSGI, it can run on any WSGI-compatible Web server. Here's how to run Django on various server arrangements.

Apache

Apache with mod_python

Apache with FastCGI

Apache with SCGI

  • SCGI can be used with Apache like this. It builds on stuff from the Apache+FCGI documentation above.

The material at simon.bofh.ms has been hard to get - server sometimes responds, sometimes not IME. In any event, #3047 has patches, and seems to be on track to become the official way to do SCGI with Django.

Apache with mod_wsgi

lighttpd

lighttpd with Apache

lighttpd with FastCGI

lighttpd with SCGI

FastCGI

Twisted.web2

nginx

I've been serving up my django site via nginx for a short while now and wanted to report my setup so that others may give it a shot. My performance over the previous setup with lighttpd is a bit better. This setup uses much less cpu and memory to achieve much better throughput and response time. The simple syntax of the nginx.conf allows you to setup a cluster of backend processes quite easily to handle load. Currently this is my nginx.conf relevant to my django site. You can reference the above links for more information about additional options.

upstream djangoserv { server 127.0.0.1:8801; }  server { listen 80; root /path/to/app; server_name test.local.domain; access_log /path/to/logs/appname-access.log main; error_log /path/to/logs/appname-error.log;  location /styles  { 	root /path/to/styles; }  location /javascripts  { 	root /path/to/javascripts; }  location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|zip|tgz|gz|rar|bz2|doc|xls|exe|pdf|ppt|txt|tar|mid|midi|wav|bmp|rtf|js|mov) {         access_log   off;         expires      30d;  }  location / {                         # host and port to fastcgi server                         fastcgi_pass 127.0.0.1:8801;                         fastcgi_param PATH_INFO $fastcgi_script_name;                         fastcgi_param REQUEST_METHOD $request_method;                         fastcgi_param QUERY_STRING $query_string;                         fastcgi_param SERVER_NAME $server_name;                         fastcgi_param SERVER_PORT $server_port;                         fastcgi_param SERVER_PROTOCOL $server_protocol;                         fastcgi_param CONTENT_TYPE $content_type;                         fastcgi_param CONTENT_LENGTH $content_length;                         fastcgi_pass_header Authorization;                         fastcgi_intercept_errors off; } }  

Once you fire up nginx you will need to start your nginx fastcgi processes. I simply use:

python2.4 manage.py runfcgi method=threaded host=127.0.0.1 port=8801   

Django behind/inside Zope

It's possible to query a Django site from Zope or Plone and return the result. This allows you to include a Django site inside a pre-existing Zope/Plone site. It's good for custom content that you don't want to use existing Zope technologies to develop. For the code, see this partly documented file. It's in a temporary location for the time being; for more info, e-mail jeff (at) bitprophet (dot) org.

CGI

Running Django as a traditional CGI is possible and would work the same as running any other sort of Python CGI script, but is generally not recommended.

With traditional CGI, the program which will be run -- in this case, Django plus a Django-powered application -- is loaded from disk into memory each time a request is served, which results in a significant amount of processing overhead and much slower responses. FastCGI and SCGI, in contrast, load the code only once -- when the server starts up -- and keep it in memory as long as the server is running, resulting in much faster responses.

If that hasn't put you off and you still need CGI, take a look at #2407.

CherryPy and WSGI

Devserver - Django built-in development server

  • Once again, this isn't for production use, but the official documentation may help. Ticket #2996 has a patch to daemonize which may be useful

Differences between Devserver and production

Django|Running Django using SCGI through LightTPD

Coder Who Says Py: Ignorant -> Newbie: Running Django using SCGI through LightTPD

Ignorant -> Newbie: Running Django using SCGI through LightTPD

To get Django using SCGI under LightTPD turned out to be a pain. No one had fully documented what was required to get it to work. It also turned out to be a slight pain in the ass for me since I of course decided to do things in LightTPD that are not totally simple.

First thing is to get Django executing under SCGI. That requires downloading django-scgi.py (I think it is by some guy named Georg Bauer, but I can't read German). Next, you need to install flup so that you have a way to drive the WSGI interface for Django. With those two things installed you have what you need to run Django using SCGI.

But the step people neglect to mention prior to running anything is that there is some configuring. You need to set your DJANGO_SETTINGS_MODULE environment variable. That requires tweaking your PYTHONPATH so that your web site's Django directory is on it. I wonder how much work it would be to cut all of this out and just write a LightTPD WSGI module?

With that all done I launched the SCGI process to be bound to a IP and port and run as a daemon (run django-scgi.py with no arguments to get the listing of options). Now you need to get LightTPD running.

For that I needed to adapt the Django docs for running it with FastCGI to use SCGI. The LightTPD docs for SCGI tell you to read the FastCGI docs anyway, so that switched over fine. But when I pulled up the admin interface, it looked all ugly. Turned out the CSS was not being served.

When running Django as its own web server it knows where to fetch all of its own files. But when you use it as a SCGI process, it no longer fetches its own media files. That required tweaking LightTPD so that it had an aliasing rule for any addresses that go to ``/media/`` to actually fetch from django/contrib/admin/media/ and have LightTPD serve that. That meant having a conditional rule for my www.drbrett.ca host that had LightTPD serve up the media files while everything else went through the SCGI process (my personal static media goes through drbrett.ca as a separate LightTPD rule). In the end, the LightTPD rule ended up being:


$HTTP["host"] == "www.drbrett.ca" {
$HTTP["url"] =~ "^/media/" {
alias.url = ("/media/" => "/home/brett/installed/lib/python2.4/site-packages/django/contrib/admin/media/")
}
else $HTTP["url"] !~ "^/media/" {
scgi.server = ("/" =>
(
("host" => "127.0.0.1",
"port" => 8080,
"check-local" => "disable")
)
)
}
}

More work than I was expecting, but now I have django-scgi.py running flup to maintain some SCGI process of Django with LightTPD handling all static file serving as needed.

Blah. As I have said before, I will have my LightTPD config file online for people to work off of once I get my site up and have a personal files section so that no one else has to do this all from scratch.

Django|Alrond | The performance test of 6 leading frameworks

Alrond | The performance test of 6 leading frameworks

As I already wrote in the previous posts, several months ago I chose framework for make a site. Most important for me were simple using of him, stability and performance.
For absence of the concrete developed information about speed comparison of frameworks in the Internet, (I have found only one testing , but it turned out naked enough. It was limited to three frameworks RubyOnRails, Django, Symfony and didn't contain absolutely any detail), I conducted the detail analysis for decision of the 6 best frameworks in all these parameters.


Update 04.Feb.2007: Pylons, RoR 1.2.1, CakePHP + eAccelerator, Zend, Turbogears with Cheetah, Jinja, Genshi

So, the purpose of testing was the decision of work speed of frameworks and their comparisons with each other on speed of pages generating and on maximum quantity of inquiries by the given configuration.
I have chosen the test model, at which the controller rendered the appointed template and generated the answer. As a result turned out the HTML-file "Hello World!" (below there are codes for everyone framework). The Database in the test is not used, because it itself limits the speed important.

I did not plan to compare functionality of frameworks, community and work with database. It was important to me to determine work speed of MVC-framework, by as small as possible influence of external factors.
Therefore I used sheaf Nginx + FCGI through IP:Port

The following frameworks have been tested:
Conducted the test on following hardware and software:
CPU: AMD OpteronT Processor 146 (2 GHz) Memory: 2 GB OS: Debian 3.1 (Linux 2.6.14) Web-Server: nginx/0.5.5 
Versions of frameworks and programming languages:
  • CodeIgniter 1.5.1
  • Catalyst 5.7006 Rev.5996
  • Django Rev.4254 (28Dec.2006)
  • RoR 1.1.6
  • RoR 1.2.1
  • Symfony 1.0beta2 SVN-Rev 3122
  • Turbogears 1.0b3 Rev.2323
  • Python 2.4.4
  • Python Psyco 1.5.2
  • Flup Rev.2303
  • Ruby 1.8.5-p12
  • mongrel 0.3.13.4
  • PHP 5.2.0 (cgi-fcgi)
  • perl, v5.8.4
  • CPAN ver 1.8802
Programs for test:
Siege 2.65 http://www.joedog.org/JoeDog/Siege Http_load 12.03.2006 http://www.acme.com/software/http_load/ ab 2.0.41-dev Rev: 1.141 
Technique of testing:
  • Measurement of memory (ps aux: VSZ "virtual set size" and RSS "resident set size").
  • Testing Apache Benchmark (2 times in succession)
  • ab -c 5 -n 1000 http://project.com/
  • Measurement of memory and of used processor-time (ps aux: VSZ, RSS, %CPU, %MEM).
  • Restart of Framework.
  • Testing Apache Benchmark (2 times in succession)
  • ab -c 100 -n 10000 http://project.com/
  • Measurement of memory and of used processor-time.
  • Restart of Framework.
  • Measurement with http_load
  • http_load -rate 10 -seconds 5 project.com
  • Measurement of memory and of used processor-time.
  • Restart of Framework.
  • Test Siege with 50 concurrent users during one minute.
  • siege -d1 -t1M -c50 project.com
  • Test Siege with 200 concurrent users during one minute.
  • siege -d1 -t1M -c200 project.com
  • Test Siege with 300 concurrent users during one minute.
  • siege -d1 -t1M -c300 project.com
  • Measurement of memory and of used processor-time.
Comments to a technique:

For the beginning I have used only Nginx with the elementary config, at which on any inquiry the dot transparent gif-file in the 43b size comes, generated by a server.
This test determined an approximate maximal capacity of the server. In front I shall tell that a stock of its capacity is 10 times more then production capacity of the fastest framework.
All frameworks were started as FastCGI 127.0.0.1:PORT. If framework specified development- and production- modes, I worked with production.
In nginx the identical config is used for all frameworks.
New start of framework I conducted to clear statistics among tests different programs.

My subjective additions on every framework by installation and start:
  1. CodeIgniter
    It is easy and quick to organize it. I haven't any problems with it. For start I used spawn-php.sh with five processes.
    There is pretty bright framework for PHP.
  2. Catalyst
    For the beginning it is necessary to install CPAN and a couple of modules for FCGI. Installation looked a little involved.
    Start isn't simple too. For the beginning it is necessary to start Framework
    ./CatInABox/start.sh
    Then the project itself
    ./script/world_fastcgi.pl -l 127.0.0.1:9500 -n 5
  3. Django
    It is easy to install from repository. Projects are created easy too. I started with two methods prefork and threaded. But the python doesn't work so well with treads.
    python manage.py runfcgi method=threaded host=127.0.0.1 port=8801 python manage.py runfcgi method=prefork host=127.0.0.1 port=8801
    I also tested the framework with the acceleration module of psyco. In manage.py put:
    from django.core.management import execute_manager import psyco psyco.full()
  4. RubyOnRails
    Following the instruction from a site it is easy to install, but it turned out difficult to start it. Though on the Internet I saw different configurations for sheaf Nginx + FastCGI, I failed to start it together with nginx.
    However with lighttpd it all started normally. Therefore I have taken advantage of the "official" recommendation to start it through a server mongrel.
    Owing to Alexey Kovyrin it is possible to estimate a multiplication factor.
    Nginx+FastCGI is faster approximately in 1.29 times than Nginx + Mongrel.
    I started 5 servers mongrel:
    mongrel_rails start -d -e production --port 8501 --pid /tmp/rb1.pid mongrel_rails start -d -e production --port 8502 --pid /tmp/rb2.pid mongrel_rails start -d -e production --port 8503 --pid /tmp/rb3.pid mongrel_rails start -d -e production --port 8504 --pid /tmp/rb4.pid mongrel_rails start -d -e production --port 8505 --pid /tmp/rb5.pid 
    In a few days after test realization a new version RoR 1.2.1 came out.
    Of course I has also tested it with all parameters, has only renewed framework, checked all specifications. Results are shocking a little: factor productivity halved in comparison with previous version in all tests!
  5. Symfony
    It wasn't complicated to install it, but then was worn out with the project itself. There is difficult enough. For start I also used spawn-php.sh with five processes.
  6. TurboGears
    By installation were appearing some problems, which were solved with some efforts.
    For start in threaded mode I used a script, I only changed
    from fcgi import WSGIServer 	to from flup.server.fcgi  import WSGIServer  WSGIServer(application=wsgiApp).run()                                                                                                                                                                	to WSGIServer(application=wsgiApp, bindAddress=
    ('127.0.0.1', 8900)).run()

Configuration files of web-server: Project codes for every framework: Start script of PHP-FCGI: Web-server results for comparison: Results for frameworks each taken separately: Results:
  1. Test Apache Benchmark
    • Table of inquiry working speed
      Table of inquiry working speed

      * 1) in second time framework is down
      * 2) a multiplication factor according to Kovyrin = 1.29. For conversion of performance from mongrel into fastcgi.
    • Diagram of results. Test „ab c 5 n 1000".
      Diagram of results. Test ab c 5 n 1000
    • Diagram of results. Test „ab c 100 n 10000".
      Diagram of results. Test ab c 100 n 10000
    • Memory expense before and after tests
      Memory expense before and after tests
      * 3) The python is made so, that it restarts the processes at strong load in prefork-mode. Therefore it is impossible to determine real load of processor and maximal use of memory.
    • VSZ "virtual set size"
      VSZ
    • RSS "resident set size"
      RSS
    • CPU loads
      CPU loads
  2. Test http_load
    • Table with results
      Table with results
    • Diagram of time for connect
      Diagram of time for connect
    • Diagram with time of first-response
      Diagram with time of first-response
  3. Siege test
    • General table with results
      General table with results
    • Transaction diagram
      Transaction diagram
    • Response time
      Response time
    • Longest transaction
      Longest transaction
    • Memory expense after tests
      Memory expense after tests
    • Processor load
      Processor load

Conclusions:

Results are visible according to tables and diagrams.
Some short additions:
Django has the least use of the processor.
Catalyst has surprised. By excessive test it overloads the processor. But losses of inquiries were not observed.
RoR 1.2.1 also loads the processor under big load.
It was interesting by TurboGears, which has shown low time use CPU in test "ab", but in the siege-test had the worst result.
Catalyst has appeared the greatest memory use. By RoR this more likely happens because of start through mongrel.
PHP-frameworks borrow many resources CPU even in a quiet condition.

Average connect time is approximate equal for all frameworks. The first response time differs strongly from each other. Django is exception, having the least "average time", and the highest "maximal time". Python-frameworks have proved good oneself, and RoR has disappointed.

During the siege-test by CodeIgniter, Symfony, RoR 1.2.1 and 1.1.6 losses were observed at a large number of "concurrent users".
RoR is fast by the small load, but it sharply loses productivity at a plenty of users.

Psyco module accelerates Django on 15-30 %, but it will be worth the grown memory use VSZ on 80 % in a prefork-mode and on 400 % (!!!) in threaded-mode. Use of RSS memory increases in 2-2.5 times.
The Prefork-mode takes away more memory, but for it the system comes out, which stably works on extreme loads, and the smaller use of the processor resources.
In a threaded-mode Django hanged under the big load and did not answer on inquiries.
The same happened with TurboGears too. This is due to the fact that python works badly in a treaded-mode.

Comments from Ivan Sagalaev:

"- FastCGI - server is started in a threaded-mode. It is known, that the Python works in treads essentially slow through GIL (Global Interpreter Lock), because of which all treads wait each other: the interpreter can be used of only one of these. Therefore in Unix at the opportunity servers with python-code should be started in a prefork-mode – it will be faster. By the way, in Windows this situation is reverse, because there creation of process is essentially slower than in Unix. And as far as I know, Python however hangs there less.
- About Psyco. Django-developers have tested it, and have found something interesting. On 32-bit systems it really gives an increase in productivity. And on 64 bit - on the contrary productivity sinks. It happens, because the processor switches under Psyco to a 32-bit mode and does not use all advantages of one's own architecture. If it is so, than this fact, that Django worked more quickly under Psyco, very likely means, that Linux and Python don't use 64 bits system (in spite of work on Opteron). That's right?"

Yes, it is 32 bit system.

Distribution of places according to this test:
  1. Django has won over the nearest competitors with the approximate triple superiority.
  2. Second and third places have divided TurboGears and RoR 1.1.6, because they are equally fast, but behave differently at different loads, overtaking each other.
  3. .
  4. Catalyst. To be honest, I expected a good deal more from Perl-framework.
  5. CodeIgniter. Just as expected PHP-frameworks have appeared the slowest. But CodeIgniter is to recommend those who wish to program only on PHP, and also to have handy system.
  6. Results of RoR 1.2.1 shocked "a little": falling of productivity in 2-4 times in comparison with 1.1.6 version. The response-time in http_load is also more in 2 times and excessively high CPU-load under high stream - all this attests to possible error in the new version.
  7. Symfony has got last place. It is very complicated and slow framework. A difference with Django is up to 35 (!!!) times.

P.S. Because of the addition to Django of the module Psyco in test I have conducted at many requests a short test for PHP with eAccelerator-0.9.5.
I have used 2 times in succession the following commands:
ab -c 5 -n 1000 http://ci.test.com/ ab -c 100 -n 10000 http://ci.test.com/
As a result CodeIgniter works 6,5 times faster, gives out to 600 resquests / sek.
The difference with Django became to 2 times.
Here are the results

django|6种流行的WEB框架性能测试

6种流行的WEB框架性能测试 - 非鱼观点 - 互联网观察

6种流行的WEB框架性能测试

星期一, 十一月 26. 2007

原文地址: http://www.alrond.com/en/2007/jan/25/performance-test-of-6-leading-frameworks/

原文对六种流行的WEB开发框架进行了简单的性能测试,分别是:
CodeIgniter on PHP
Catalyst on Perl
Django on Python
Django on Python with Psyco
RubyOnRails on Ruby
Symfony on PHP
TurboGears on Python

比较的标准首先是大流量下的稳定性,其次是性能。测试的方法很简单,页面只输出一个Hello world字符串,不涉及任何数据库操作,否则整个网站的性能就会因为数据库的影响而趋向于集中。

测试用的硬件和系统:
CPU: AMD OpteronT Processor 146 (2 GHz)
Memory: 2 GB
OS: Debian 3.1 (Linux 2.6.14)
Web-Server: nginx/0.5.5

测试软件:
Siege 2.65 http://www.joedog.org/JoeDog/Siege
Http_load 12.03.2006 http://www.acme.com/software/http_load/
ab 2.0.41-dev Rev: 1.141

这套硬件和软件系统应该说还是相当有代表性的,所以其结果很值得参考。具体的测试参数和测试细节还有结果的详细数字和图标可以到原文链接去看,这里只讲一下最终的结果。

第一名:Django 占用CPU最少,性能超过第二名好几倍,使用threaded模式比prefork模式要快15%左右,(在 一个中国的作者的测试情况下,threaded模式比prefork模式快近十倍),但是在高负载的情况下,threaded模式会死掉,而且无法自动重启,必须手工重启WEB服务器,这对于生产环境服务器是不可接受的。另外,使用Psyco来加速的话,两种方式下速度都可以提高20%左右,但是内存占用量会增加1-3倍,如果硬件资源没问题,就可以考虑这种方式。

第二名和第三名分别是TurboGears和RoR 1.1.6,它们速度差不多,但是不同负载量下的表现不同。
RoR 1.2.1的速度比1.1.6要慢2-4倍,而且在高负载下CPU占用也大一倍。

PHP的框架速度是最慢的,比Django慢了35倍。但是在加载了eAccelerator加速器以后速度有大幅提升,只比Django慢两倍了。

作者后来又做了一次额外测试,增加了几个新兴的框架,但是最终结果并没有变,还是证明Django是最佳选择。而且,Python非常容易上手。另外,同样基于Python的框架Pylons也不错。

django|的几种运行方法性能对比

django的几种运行方法性能对比 - 老黄纸条箱

django的几种运行方法性能对比

By HD on October 14, 2007 8:00 PM | 固定链接 | 评论 (6) | 引用通告 (1)

我现在手上有以下几种django的情行:

  • django自带web server
  • django+lighttpd+fastcgi+threaded
  • django+lighttpd+fastcgi+prefork
  • django+lighttpd+scgi+threaded
    • django+lighttpd+scgi+prefork

我对这几个完全没感觉。不知道的情况下会认为fastcgi更快更稳定,scgi可能都没听说过,而自带的应该很灵活,但是应该性能上完全不行的。
真实的情况呢?用数据来说话罢:

所有的测试均为我的笔记本,这里测试的环境都一样。
OS:OS X 10.4.10
CPU:2.16GHz Intel Core Duo
RAM:2G 667MHz SDRAM
lighttpd:lighttpd @1.4.18_0+darwin_8
python:python25 @2.5.1_1+darwin_8
django:0.97-pre-SVN-unknown

测试工具:apache ab
测试压力:50个并发、连续5000次访问

测试服务器内容:就是一个url请求,echo出Hello World。

使用django的自带server:
Concurrency Level: 50
Time taken for tests: 8.511 seconds
Complete requests: 5000
Failed requests: 122
(Connect: 70, Length: 52, Exceptions: 0)
Broken pipe errors: 0
Total transferred: 759220 bytes
HTML transferred: 103530 bytes
Requests per second: 587.48 [#/sec] (mean)
Time per request: 85.11 [ms] (mean)
Time per request: 1.70 [ms] (mean, across all concurrent requests)
Transfer rate: 89.20 [Kbytes/sec] received

它在连接访问的末期在控制台上报出了许多exception。每秒587次的处理能力还是相当不错的,不过稳定性不好还是需要大家仔细考虑的。

再来看看lighttpd+fastcgi+threaded的结果:
Concurrency Level: 50
Time taken for tests: 8.447 seconds
Complete requests: 5000
Failed requests: 1
(Connect: 0, Length: 1, Exceptions: 0)
Broken pipe errors: 0
Total transferred: 862279 bytes
HTML transferred: 104979 bytes
Requests per second: 591.93 [#/sec] (mean)
Time per request: 84.47 [ms] (mean)
Time per request: 1.69 [ms] (mean, across all concurrent requests)
Transfer rate: 102.08 [Kbytes/sec] received
哈哈,处理能力明显比django自己的server要好。不过在最后一次也出现了问题,更讨厌的是它在出现问题后就死悄悄了,必需重启lighttpd和django才能缓过来。稳定第一,这样的稳定问题还是不能让我容忍的!

是不是lighttpd+fastcgi+prefork方式会稳定的多呢?来看看:
Concurrency Level: 50
Time taken for tests: 57.002 seconds
Complete requests: 5000
Failed requests: 5000
(Connect: 5000, Length: 0, Exceptions: 0)
Broken pipe errors: 0
Total transferred: 0 bytes
HTML transferred: 0 bytes
Requests per second: 87.72 [#/sec] (mean)
Time per request: 570.02 [ms] (mean)
Time per request: 11.40 [ms] (mean, across all concurrent requests)
Transfer rate: 0.00 [Kbytes/sec] received
真的没有错误了。不过让人无法忍受!感觉就一个字:"慢!"每秒87次的处理能力能让人杀了我!虽然稳定。。。但是看着大片大片的cpu空着,感觉对不起我这PP的BB哟。

大将出场!牛B的scgi来了,lighttpd+scgi+threaded强强联合:
Concurrency Level: 50
Time taken for tests: 6.202 seconds
Complete requests: 5000
Failed requests: 0
Broken pipe errors: 0
Total transferred: 934460 bytes
HTML transferred: 105000 bytes
Requests per second: 806.19 [#/sec] (mean)
Time per request: 62.02 [ms] (mean)
Time per request: 1.24 [ms] (mean, across all concurrent requests)
Transfer rate: 150.67 [Kbytes/sec] received
这才是真正牛B的每秒806次处理,而且没有问题,而且cpu很稳当。爽就一个字!

是不是scgi在prefork方式下也很强呢?来看看lighttpd+scgi+prefork的成绩:
Concurrency Level: 50
Time taken for tests: 56.730 seconds
Complete requests: 5000
Failed requests: 5000
(Connect: 5000, Length: 0, Exceptions: 0)
Broken pipe errors: 0
Total transferred: 0 bytes
HTML transferred: 0 bytes
Requests per second: 88.14 [#/sec] (mean)
Time per request: 567.30 [ms] (mean)
Time per request: 11.35 [ms] (mean, across all concurrent requests)
Transfer rate: 0.00 [Kbytes/sec] received
山口再次吹来了凉风,只能用"凄惨"来形容。又是一个稳定而无任何性能而言的成绩。

看了这些数字,总结一下:
1.django服务器的prefork是稳定第一,但是显然不如thread状态下的速度快
2.thread处理速度是快,但是到一个临界状态后出错哪可是一踏里糊涂呀
3.scgi比fcgi的简洁不是盖的,对于cpu的消耗就是小,而且实现比较稳定

后话:
除了django自己的server外,其它的模式如果你改了程序,就需要kill -HUP django启动的cgi服务。在开发时还是使用django自己的server罢。但是在生产环境中重启server还是有点不爽。 :)

Host|Apache与Tomcat的三种连接方式介绍

Apache与Tomcat的三种连接方式介绍 - Java天下社区 - www.javatx.cn - 服务器及中间件



作者:刘冬
 
首先我们先介绍一下为什么要让Apache与Tomcat之间进行连接。事实上Tomcat本身已经提供了HTTP服务,该服务默认的端口是8080,装好tomcat后通过8080端口可以直接使用Tomcat所运行的应用程序,你也可以将该端口改为80。

既然Tomcat本身已经可以提供这样的服务,我们为什么还要引入Apache或者其他的一些专门的HTTP服务器呢?原因有下面几个:

1.  提升对静态文件的处理性能
2.  利用Web服务器来做负载均衡以及容错
3.  无缝的升级应用程序

这三点对一个web网站来说是非常之重要的,我们希望我们的网站不仅是速度快,而且要稳定,不能因为某个Tomcat宕机或者是升级程序导致用户访问不了,而能完成这几个功能的、最好的HTTP服务器也就只有apache的http  server了,它跟tomcat的结合是最紧密和可靠的。

接下来我们介绍三种方法将apache和tomcat整合在一起。

一.  JK

这是最常见的方式,你可以在网上找到很多关于配置JK的网页,当然最全的还是其官方所提供的文档。JK本身有两个版本分别是1和2,目前1最新的版本是1.2.19,而版本2早已经废弃了,以后不再有新版本的推出了,所以建议你采用版本1。

JK是通过AJP协议与Tomcat服务器进行通讯的,Tomcat默认的AJP  Connector的端口是8009。JK本身提供了一个监控以及管理的页面jkstatus,通过jkstatus可以监控JK目前的工作状态以及对到tomcat的连接进行设置,如下图所示




在这个图中我们可以看到当前JK配了两个连接分别到8109和8209端口上,目前s2这个连接是停止状态,而s1这个连接自上次重启后已经处理了47万多个请求,流量达到6.2个G,最大的并发数有13等等。我们也可以利用jkstatus的管理功能来切换JK到不同的Tomcat上,例如将s2启用,并停用s1,这个在更新应用程序的时候非常有用,而且整个切换过程对用户来说是透明的,也就达到了无缝升级的目的。关于JK的配置文章网上已经非常多了,这里我们不再详细的介绍整个配置过程,但我要讲一下配置的思路,只要明白了配置的思路,JK就是一个非常灵活的组件。

JK的配置最关键的有三个文件,分别是  

httpd.conf    
Apache服务器的配置文件,用来加载JK模块以及指定JK配置文件信息

workers.properties  
到Tomcat服务器的连接定义文件

uriworkermap.properties    
URI映射文件,用来指定哪些URL由Tomcat处理,你也可以直接在httpd.conf中配置这些URI, 但是独立这些配置的好处是JK模块会定期更新该文件的内容,使得我们修改配置的时候无需重新启动Apache服务器。

其中第二、三个配置文件名都可以自定义。下面是一个典型的  httpd.conf  对JK的配置

#  (httpd.conf)
#  加载mod_jk模块
LoadModule  jk_module  modules/mod_jk.so

#
#  Configure  mod_jk
#

JkWorkersFile  conf/workers.properties
JkMountFile  conf/uriworkermap.properties
JkLogFile  logs/mod_jk.log
JkLogLevel  warn

接下来我们在Apache的conf目录下新建两个文件分别是  workers.properties、uriworkermap.properties 。这两个文件的内容大概如下

#
#  workers.properties
#


#  list  the  workers  by  name

worker.list=DLOG4J,  status

#  localhost  server  1
#  ------------------------
worker.s1.port=8109
worker.s1.host=localhost
worker.s1.type=ajp13

#  localhost  server  2
#  ------------------------
worker.s2.port=8209
worker.s2.host=localhost
worker.s2.type=ajp13
worker.s2.stopped=1

worker.DLOG4J.type=lb
worker.retries=3
worker.DLOG4J.balanced_workers=s1,  s2
worker.DLOG4J.sticky_session=1

worker.status.type=status

以上的workers.properties配置就是我们前面那个屏幕抓图的页面所用的配置。首先我们配置了两个类型为ajp13的worker分别是s1和s2,它们指向同一台服务器上运行在两个不同端口8109和8209的Tomcat上。接下来我们配置了一个类型为lb(也就是负载均衡的意思)的worker,它的名字是DLOG4J,这是一个逻辑的worker,它用来管理前面配置的两个物理连接s1和s2。最后还配置了一个类型为status的worker,这是用来监控JK本身的模块。有了这三个worker还不够,我们还需要告诉JK,哪些worker是可用的, 所以就有worker.list  =  DLOG4J,  status  这行配置。

接下来便是URI的映射配置了,我们需要指定哪些链接是由Tomcat处理的,哪些是由Apache直接处理的,看看下面这个文件你就能明白其中配置的意义

/*=DLOG4J
/jkstatus=status

!/*.gif=DLOG4J
!/*.jpg=DLOG4J
!/*.png=DLOG4J
!/*.css=DLOG4J
!/*.js=DLOG4J
!/*.htm=DLOG4J
!/*.html=DLOG4J

相信你已经明白了一大半了:所有的请求都由DLOG4J这个worker进行处理,但是有几个例外,/jkstatus请求由status这个worker处理。另外这个配置中每一行数据前面的感叹号是什么意思呢?感叹号表示接下来的URI不要由JK进行处理,也就是Apache直接处理所有的图片、css文件、js文件以及静态html文本文件。

通过对workers.properties和uriworkermap.properties的配置,可以有各种各样的组合来满足我们前面提出对一个web网站的要求。您不妨动手试试!

二.  http_proxy

这是利用Apache自带的mod_proxy模块使用代理技术来连接Tomcat。在配置之前请确保是否使用的是2.2.x版本的Apache服务器。因为2.2.x版本对这个模块进行了重写,大大的增强了其功能和稳定性。

http_proxy模式是基于HTTP协议的代理,因此它要求Tomcat必须提供HTTP服务,也就是说必须启用Tomcat的HTTP  Connector。一个最简单的配置如下

ProxyPass  /images  !
ProxyPass  /css  !
ProxyPass  /js  !
ProxyPass  /   http://localhost:8080/

在这个配置中,我们把所有  http://localhost  的请求代理到  http://localhost:8080/  ,这也就是Tomcat的访问地址,除了images、css、js几个目录除外。我们同样可以利用mod_proxy来做负载均衡,再看看下面这个配置

ProxyPass  /images  !
ProxyPass  /css  !  
ProxyPass  /js  !

ProxyPass  /  balancer://example/
<Proxy  balancer://example/>
BalancerMember  http://server1:8080/
BalancerMember  http://server2:8080/
BalancerMember  http://server3:8080/
</Proxy>

配置比JK简单多了,而且它也可以通过一个页面来监控集群运行的状态,并做一些简单的维护设置。




三.  ajp_proxy

ajp_proxy连接方式其实跟http_proxy方式一样,都是由mod_proxy所提供的功能。配置也是一样,只需要把http://  换成  ajp://  ,同时连接的是Tomcat的AJP  Connector所在的端口。上面例子的配置可以改为

ProxyPass  /images  !
ProxyPass  /css  !  
ProxyPass  /js  !

ProxyPass  /  balancer://example/
<Proxy  balancer://example/>
BalancerMember  ajp://server1:8080/
BalancerMember  ajp://server2:8080/
BalancerMember  ajp://server3:8080/
</Proxy>

采用proxy的连接方式,需要在Apache上加载所需的模块,mod_proxy  相关的模块有
mod_proxy.so、mod_proxy_connect.so、mod_proxy_http.so、mod_proxy_ftp.so、mod_proxy_ajp.so,  其中mod_proxy_ajp.so只在Apache  2.2.x中才有。如果是采用  http_proxy  方式则需要加载  mod_proxy.so  和  mod_proxy_http.so;如果是  ajp_proxy  则需要加载  mod_proxy.so和mod_proxy_ajp.so这两个模块。

四.  三者比较

相对于JK的连接方式,后两种在配置上是比较简单的,灵活性方面也一点都不逊色。但就稳定性而言就不像JK这样久经考验,毕竟Apache  2.2.3推出的时间并不长,采用这种连接方式的网站还不多,因此,如果是应用于关键的互联网网站,还是建议采用JK的连接方式。


相关网址

Apache   http://httpd.apache.org
Tomcat  http://tomcat.apache.org
JK文档  http://tomcat.apache.org/connectors-doc/

关于作者

刘冬,一直使用J2EE/J2ME从事移动互联网方面的开发。您可以通过Java自由人网站来跟他联系,网址是: http://www.dlog.cn/javayou  ,另外他的邮件地址是javayou@gmail.com