WSGI
之前对python的HTTP Server的理解有点混乱,既可以通过BaseHTTPServer启动一个python http server,那么python wsgi又是干什么的?今天闲下心来整理一下uwsgi到底是什么?参考python官方文档对WSGI的定义(http://docs.python.org/2/library/wsgiref.html):
The Web Server Gateway Interface (WSGI) is a standard interface between web server software and web applications written in Python。
WSGI是一个在web服务器和web应用程序之间的标准接口。其实这个很好理解就像java的Servlet一样,实现了Servlet规范的服务器才是靠谱的java web服务器,python也是,只是定义叫USGI。下面是一个官方文档给的一个例子:
from wsgiref.simple_server import make_server # A relatively simple WSGI application. It's going to print out the def simple_app(environ, start_response): status = '200 OK' headers = [('Content-type', 'text/plain')] start_response(status, headers) ret = ["%s: %s\n" % (key, value) for key, value in environ.iteritems()] return ret httpd = make_server('', 8080, simple_app) print "Serving on port 8080..." httpd.serve_forever()
这里用到了wsgiref.simple_server。官方文档:This module implements a simple HTTP server (based on BaseHTTPServer) that serves WSGI applications. Each server instance serves a single WSGI application on a given host and port. If you want to serve multiple applications on a single host and port, you should create a WSGI application that parses PATH_INFO to select which application to invoke for each request. (E.g., using the shift_path_info() function from wsgiref.util.)
可以看到wsgiref.simple_server其实也是基于BaseHTTPServer实现的。
关于wsgi的细节可以参考:
http://webpython.codepoint.net/wsgi_tutorial
http://docs.python.org/2/library/wsgiref.html
http://lucumr.pocoo.org/2007/5/21/getting-started-with-wsgi/
SocketServer
SocketServer更简单了,和java的SocketSerer一个意思,参考:http://docs.python.org/2/library/socketserver.html
There are five classes in an inheritance diagram, four of which represent synchronous servers of four types: +------------+ | BaseServer | +------------+ | v +-----------+ +------------------+ | TCPServer |------->| UnixStreamServer | +-----------+ +------------------+ | v +-----------+ +--------------------+ | UDPServer |------->| UnixDatagramServer | +-----------+ +--------------------+
默认上面这几个类是同步的,如果你打开两个 telnet 会话来连接同一个服务端, 那么第二个会话只有在关闭第一个会话之后才能连接上。如果一个实际的服务端像这个工作,那么什么事都将做不了。这就是为什么大部分实际的服务端都采用线程或子进程来处理多个连接。
SocketServer 模块定义了两个有用的类来处理多个连接:ThreadingMixIn 和 ForkingMixIn 。一个继承自 ThreadingMixIn 的 SocketServer 类会对每个进来的请求自动产生一个新的线程来处理。而继承自 ForkingMixIn 的子类对每个进来的请求将会自动产生一个新的子进程。个人喜欢 ThreadingMixIn 更甚一点,因为线程比子进程更高效和易移植。同时线程与父线程的通信也比采用子进程来得得更加的容易。
SocketServer模块已经定义了几个常用的多线程版本SocketServer:
class ForkingUDPServer(ForkingMixIn, UDPServer): pass class ForkingTCPServer(ForkingMixIn, TCPServer): pass class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass
BaseHTTPServer
参考:http://docs.python.org/2/library/basehttpserver.html
BaseHTTPServer module 定义了两个class用来实现HTTP servers (Web servers)。一般情况下都不是用来直接使用的,因为太简陋了。
1.class HTTPServer(server_address, RequestHandlerClass)
HTTPServer是SocketServer.TCPServer的子类,所以也实现 SocketServer.BaseServer接口,他负责创建并监听HTTP socket,把请求转发给handler处理。
2.class BaseHTTPRequestHandler(request, client_address, server)
BaseHTTPRequestHandler用来handle HTTP请求,它自己本身并不能实际处理请求,一般通过它子类来处理各种HTTP请求(例如GET,POST), BaseHTTPRequestHandler提供了一系列对象和方法供子类扩展使用。
一个简单的例子:
from BaseHTTPServer import HTTPServer from BaseHTTPServer import BaseHTTPRequestHandler server_address = ('', 8787) #address handler_class = BaseHTTPRequestHandler #handler httpd = HTTPServer(server_address, handler_class) httpd.serve_forever()
下面是一个多线程的例子参考(http://stackoverflow.com/questions/14088294/multithreaded-web-server-in-python):
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler from SocketServer import ThreadingMixIn import threading class Handler(BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) self.end_headers() message = threading.currentThread().getName() self.wfile.write(message) self.wfile.write('\n') return class ThreadedHTTPServer(ThreadingMixIn, HTTPServer): """Handle requests in a separate thread.""" if __name__ == '__main__': server = ThreadedHTTPServer(('localhost', 8080), Handler) print 'Starting server, use <Ctrl-C> to stop' server.serve_forever()