Python模块WSGI使用详解

WSGI(Web Server Gateway Interface):Web服务网关接口,是Python中定义的服务器程序和应用程序之间的接口。

Web程序开发中,一般分为服务器程序和应用程序。服务器程序负责对socket服务的数据进行封装和整理,而应用程序则负责对Web请求进行逻辑处理。

Web应用本质上也是一个socket服务器,用户的浏览器就是一个socket客户端。

我们先用socket编程实现一个简单的Web服务器:

import socket 

def handle_request(client):
  buf = client.recv(1024)
  print(buf)
  msg = "HTTP/1.1 200 OK\r\n\r\n" #HTTP头信息
  client.send(('%s' % msg).encode())
  msg = "Hello, World!"
  client.send(('%s' % msg).encode()) 

def main():
  ip_port = ("localhost", 8000)
  sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  sock.bind(ip_port)
  sock.listen(5) 

  while True:
    conn, addr = sock.accept()
    handle_request(conn)
    conn.close() 

if __name__ == "__main__":
  main()

上述代码中,main()函数就是服务器函数,handle_request()就是应用程序。
下面我们再用python的wsgiref模块来实现跟上述代码一样的Web服务器:

from wsgiref.simple_server import make_server 

def handle_request(env, res):
  res("200 OK",[("Content-Type","text/html")])
  body = "<h1>Hello World!</h1>"
  return [body.encode("utf-8")] 

if __name__ == "__main__":
  httpd = make_server("",8000,handle_request)
  print("Serving http on port 80000")
  httpd.serve_forever()

上面两份代码实现的效果是一样的,调用wsgiref模块则明显节省了代码量,是整个程序更加简洁。
wsgiref模块封装了socket服务端的代码,只留下一个调用的接口,省去了程序员的麻烦,程序员可以将精力放在Web请求的逻辑处理中。

以上述的代码为例,详细看一下wsgiref模块的源码中一些关键的地方:

if __name__ == "__main__":
  httpd = make_server("",8000,handle_request)
  print("Serving http on port 80000")
  httpd.serve_forever()

1、整个程序的入口为make_server()函数:

def make_server(host, port, app, server_class=WSGIServer, handler_class=WSGIRequestHandler):
  """Create a new WSGI server listening on `host` and `port` for `app`"""
  server = server_class((host, port), handler_class) #默认创建一个WSGIServer类
  server.set_app(app) #将应用程序,即逻辑处理函数传给类
  return server

2、make_server()函数默认生成一个WSGIServer类:

class WSGIServer(HTTPServer):
class HTTPServer(socketserver.TCPServer):
class TCPServer(BaseServer):

WSGIServer,HTTPServer两个类没有初始化函数,调用父类的初始化函数,TCPServer类的__init__()函数拓展了BaseServer

类的__init__()函数:

#BaseServer类的__init__()函数:
def __init__(self, server_address, RequestHandlerClass):
  """Constructor. May be extended, do not override."""
  self.server_address = server_address
  self.RequestHandlerClass = RequestHandlerClass
  self.__is_shut_down = threading.Event()
  self.__shutdown_request = False
#TCPServer类的__init__()函数:
def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True):
  """Constructor. May be extended, do not override."""
  BaseServer.__init__(self, server_address, RequestHandlerClass)
  self.socket = socket.socket(self.address_family,self.socket_type)
    if bind_and_activate:
      try:
        self.server_bind()
        self.server_activate()
      except:
        self.server_close()
        raise

TCPServer类的初始化函数还调用了server_bind(self),server_bind(self)两个函数:

def server_bind(self):
  """Called by constructor to bind the socket.May be overridden."""
  if self.allow_reuse_address:
    self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
  self.socket.bind(self.server_address)
  self.server_address = self.socket.getsockname()
def self.server_activate(self):
  """Called by constructor to activate the server.May be overridden."""
  self.socket.listen(self.request_queue_size)

可以看到server.bind()函数调用了socket.bind()函数,而server_activate()调用了socket.listen()函数:

3、server.set_app(app),此处传入Web请求的处理逻辑:

def set_app(self,application):
  self.application = application

4、httpd.serve_forever()函数调用BaseServer类的_handle_request_noblock()函数处理多路请求:

def _handle_request_noblock(self):
  try:
    request, client_address = self.get_request() #get_request()调用了socket.accept()函数
  except OSError:
    return
  if self.verify_request(request, client_address):
    try:
      self.process_request(request, client_address)
    except:
      self.handle_error(request, client_address)
      self.shutdown_request(request)
  else:
    self.shutdown_request(request) 
def process_request(self, request, client_address):
  self.finish_request(request, client_address)
  self.shutdown_request(request)#shutdown_request()调用socket.close()关闭socket 

def finish_request(self, request, client_address):
  """Finish one request by instantiating RequestHandlerClass."""
  self.RequestHandlerClass(request, client_address, self) 

5、process_request()函数调用了finish_request()函数,简介调用了make_server函数的默认参数WSGIRequestHandler类:

class WSGIRequestHandler(BaseHTTPRequestHandler):
class BaseHTTPRequestHandler(socketserver.StreamRequestHandler):
class StreamRequestHandler(BaseRequestHandler):

#调用BaseRequestHandler类的初始化函数:
def __init__(self, request, client_address, server):
  self.request = request
  self.client_address = client_address
  self.server = server
  self.setup()
  try:
    self.handle()
  finally:
    self.finish()

6、初始化函数调用之后调用WSGIRequestHandler类的handle()函数获取server的逻辑处理函数:

def handle(self):
  """Handle a single HTTP request"""
  try:
    handler = ServerHandler(self.rfile, stdout, self.get_stderr(), self.get_environ())
    handler.request_handler = self   # backpointer for logging
    handler.run(self.server.get_app()) #此处调用server的逻辑处理函数
  finally:
    stdout.detach()

7、BaseHandler类的handler.run()函数执行逻辑处理:

def run(self, application):
   try:
    self.setup_environ()
    self.result = application(self.environ, self.start_response)
    self.finish_response()
  except:
    try:
      self.handle_error()
    except:
      self.close()
      raise  # ...and let the actual server figure it out. 

self.environ:一个包含所有HTTP请求信息的dict对象
self.start_response:一个发送HTTP响应的函数。

在application函数中,调用:

res("200 OK",[("Content-Type","text/html")])

这样就发送了HTTP响应的头信息

8、BaseHandler类的setup_environ()函数获取HTTP请求的头信息:

def setup_environ(self):
  """Set up the environment for one request"""
  env = self.environ = self.os_environ.copy() 

os_environ= read_environ() 

read_environ()函数: 

def read_environ():
  """Read environment, fixing HTTP variables"""
  enc = sys.getfilesystemencoding()
  esc = 'surrogateescape'
  try:
    ''.encode('utf-8', esc)
  except LookupError:
    esc = 'replace'
  environ = {} 

  # Take the basic environment from native-unicode os.environ. Attempt to
  # fix up the variables that come from the HTTP request to compensate for
  # the bytes->unicode decoding step that will already have taken place.
  for k, v in os.environ.items():
    if _needs_transcode(k): 

      # On win32, the os.environ is natively Unicode. Different servers
      # decode the request bytes using different encodings.
      if sys.platform == 'win32':
        software = os.environ.get('SERVER_SOFTWARE', '').lower() 

        # On IIS, the HTTP request will be decoded as UTF-8 as long
        # as the input is a valid UTF-8 sequence. Otherwise it is
        # decoded using the system code page (mbcs), with no way to
        # detect this has happened. Because UTF-8 is the more likely
        # encoding, and mbcs is inherently unreliable (an mbcs string
        # that happens to be valid UTF-8 will not be decoded as mbcs)
        # always recreate the original bytes as UTF-8.
        if software.startswith('microsoft-iis/'):
          v = v.encode('utf-8').decode('iso-8859-1') 

        # Apache mod_cgi writes bytes-as-unicode (as if ISO-8859-1) direct
        # to the Unicode environ. No modification needed.
        elif software.startswith('apache/'):
          pass 

        # Python 3's http.server.CGIHTTPRequestHandler decodes
        # using the urllib.unquote default of UTF-8, amongst other
        # issues.
        elif (
          software.startswith('simplehttp/')
          and 'python/3' in software
        ):
          v = v.encode('utf-8').decode('iso-8859-1') 

        # For other servers, guess that they have written bytes to
        # the environ using stdio byte-oriented interfaces, ending up
        # with the system code page.
        else:
          v = v.encode(enc, 'replace').decode('iso-8859-1') 

      # Recover bytes from unicode environ, using surrogate escapes
      # where available (Python 3.1+).
      else:
        v = v.encode(enc, esc).decode('iso-8859-1') 

    environ[k] = v
  return environ

9、BaseHandler类的start_response()函数:

def start_response(self, status, headers,exc_info=None):
  """'start_response()' callable as specified by PEP 3333"""
  if exc_info:
    try:
      if self.headers_sent:
        # Re-raise original exception if headers sent
        raise exc_info[0](exc_info[1]).with_traceback(exc_info[2])
    finally:
      exc_info = None    # avoid dangling circular ref
  elif self.headers is not None:
    raise AssertionError("Headers already set!") 

  self.status = status
  self.headers = self.headers_class(headers)
  status = self._convert_string_type(status, "Status")
  assert len(status)>=4,"Status must be at least 4 characters"
  assert status[:3].isdigit(), "Status message must begin w/3-digit code"
  assert status[3]==" ", "Status message must have a space after code" 

  if __debug__:
    for name, val in headers:
      name = self._convert_string_type(name, "Header name")
      val = self._convert_string_type(val, "Header value")
  return self.write

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

您可能感兴趣的文章:

  • 解决python3中自定义wsgi函数,make_server函数报错的问题
  • 使用Nginx+uWsgi实现Python的Django框架站点动静分离
  • 在Linux系统上通过uWSGI配置Nginx+Python环境的教程
  • 在Mac OS上使用mod_wsgi连接Python与Apache服务器
  • 详解使用Nginx和uWSGI配置Python的web项目的方法
  • 深入解析Python中的WSGI接口
  • 在Windows服务器下用Apache和mod_wsgi配置Python应用的教程
  • 利用Python脚本在Nginx和uwsgi上部署MoinMoin的教程
  • 详解Python程序与服务器连接的WSGI接口
  • 在Debian下配置Python+Django+Nginx+uWSGI+MySQL的教程
(0)

相关推荐

  • 使用Nginx+uWsgi实现Python的Django框架站点动静分离

    由于: Django处理静态文件不太友好: 以后有可能需要处理php或者其他资源的请求: 所以考虑结合nginx,使用nignx做它擅长的路由分发功能:同时做动静分离,即Http请求统一由Nginx进行分发,静态文件由Nginx处理,并返回给客户端:而动态的请求,则分发到uWsgi,由uWsgi再分发给Django进行处理.即客户端 <-> nginx <-> socket <-> uwsgi <-> Django 一.环境 系统:centOS 6 pyth

  • 解决python3中自定义wsgi函数,make_server函数报错的问题

    #coding:utf-8 from wsgiref.simple_server import make_server def RunServer(environ, start_response): start_response('200 OK', [('Content-Type', 'text/html')]) return '<h1>Hello, web!</h1>' if __name__ == '__main__': httpd = make_server('localho

  • 详解使用Nginx和uWSGI配置Python的web项目的方法

    基于python的web项目,常见的部署方法有: fcgi:用spawn-fcgi或者框架自带的工具对各个project分别生成监听进程,然后和http服务互动. wsgi:利用http服务的mod_wsgi模块来跑各个project. 不过还有个uwsgi,它既不用wsgi协议也不用fcgi协议,而是自创了一个uwsgi的协议,据作者说该协议大约是fcgi协议的10倍那么快.uWSGI的主要特点如下: 超快的性能. 低内存占用(实测为apache2的mod_wsgi的一半左右). 多app管理

  • 在Linux系统上通过uWSGI配置Nginx+Python环境的教程

    1.安装ubuntu有uwsgi的ppa: add-apt-repository ppa:stevecrozz/ppa apt-get update apt-get install uwsgi 2. 用uwsgi代替mod_wsgi Nginx 的整体配置这里不说了,假设已经明白 Nginx的基本配置,那么uwsgi就类似这么配置: location / { include uwsgi_params uwsgi_pass 127.0.0.1:9090 } 再比如django就是: .......

  • 在Debian下配置Python+Django+Nginx+uWSGI+MySQL的教程

    最近尝试把项目迁移到Python环境下,特别新装了一台干净的Debian系统,准备重新配置环境,上网找了一些运行Python Web的环境方案,最后敲定Nginx+uWSGI组合,Nginx用得比较多,熟练些:uWSGI据说性能不错,想尝试一下. 网上大部分教程都是要求到uWSGI官方网站下载源码包,然后通过编译的方式安装,比如对于一台新Debian系统,可以通过下面的命令安装: apt-get update apt-get upgrade apt-get install build-essen

  • 详解Python程序与服务器连接的WSGI接口

    了解了HTTP协议和HTML文档,我们其实就明白了一个Web应用的本质就是: 浏览器发送一个HTTP请求: 服务器收到请求,生成一个HTML文档: 服务器把HTML文档作为HTTP响应的Body发送给浏览器: 浏览器收到HTTP响应,从HTTP Body取出HTML文档并显示. 所以,最简单的Web应用就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回.Apache.Nginx.Lighttpd等这些常见的静态服务器就是干这件事情的. 如果要动

  • 在Windows服务器下用Apache和mod_wsgi配置Python应用的教程

    最近开发了一个 Google Analytics 相关的应用,但需要在 Windows 下部署,结合网上的相关经验,最终选择了 apache+mod_wsgi 这样的配置. 修改python应用 复制代码 代码如下: Note that mod_wsgi requires that the WSGI application entry point be called 'application'. If you want to call it something else then you wou

  • 在Mac OS上使用mod_wsgi连接Python与Apache服务器

    一.安装mod_wsgi 3.4: ./configure --with-apxs=/Users/levin/dev/apache2.2.27/bin/apxs --with-python=/usr/bin/python make make install 编辑httpd.conf使Apache导入模块mod_wsgi.so以及引入vhost配置文件: LoadModule wsgi_module modules/mod_wsgi.so Include conf/extra/httpd-vhos

  • 利用Python脚本在Nginx和uwsgi上部署MoinMoin的教程

    在 CentOS 下使用 apache+mod_wsgi 部署了 MoinMoin,但是编辑和保存页面很慢,于是准备使用 nginx+uwsgi 重新部署 本文假定已经按照官方指引 Quick MoinMoin on CentOS完成了 apache 和 mod_wsgi 之外的基础安装 安装 Nginx 默认情况下,CentOS 下没有 nginx 的源,需要自己手动添加,访问 http://nginx.org/en/linux_packages.html#stable 下载 CentOS 6

  • 深入解析Python中的WSGI接口

    概述 WSGI接口包含两方面:server/gateway 及 application/framework. server调用由application提供的可调用对象. 另外在server和application之间还可能有一种称作middleware的中间件. 可调用对象是指:函数.方法.类或者带有callable方法的实例. 关于application 函数.方法.类及带有callable方法的实例等可调用对象都可以作为the application object. WSGI协议要求: th

随机推荐