python WSGI那些事

》你知道WSGI吗

其实,我也不是太清楚,但是我知道一丢丢。记得第一份实习,当时用的是tornado,我就想看看这个东东到底是啥玩意,可是,我看了很久很久,始终没看明白!(听说你英语不咋地啊。。。。)落下,不议论。


画了一个分割线,有那么一天,一个牛逼的同事解释了这个东东,茅塞顿开)))===》

》 你不可不知道的Socket

如果你写过socket,对于以下脚本应该非常熟悉,我们写了一个server,然后用一个client访问

  • my_server.py

      #coding:utf-8
    
      import socket
      # Server
    
      #create an INET, STREAMing socket
      serversocket = socket.socket(
          socket.AF_INET, socket.SOCK_STREAM)
      #bind the socket to a public host,
      # and a well-known port
      serversocket.bind(('localhost', 8082))
      #become a server socket
      serversocket.listen(5)
    
      response="""HTTP/1.1 200 OK\r\n
      Date: Fri, 22 May 2009 06:07:21 GMT\r\n
      Content-Type: text/html; charset=UTF-8\r\n
      \r\n
      <html>\r\n
            <head></head>\r\n
            <body>\r\n
                  hello<!--body goes here-->\r\n
            </body>\r\n
      </html>\r\n
      """
      while True:
          client_sock,client_addr = serversocket.accept();
          while True:
              try:
                  recv_mess = client_sock.recv(1024)
                  if not recv_mess:
                      client_sock.close()
                  print recv_mess
                  client_sock.sendall(response)
                  client_sock.close()
              except Exception as e:
                  client_sock.close()
                  break
    
  • my_client.py

   #coding:utf-8

    import socket



    s = socket.socket(
        socket.AF_INET, socket.SOCK_STREAM)
    #now connect to the web server on port 80
    # - the normal http port
    s.connect(("localhost", 8082))

    s.sendall('Hello, world')
    data = s.recv(1024)

    s.close()
    print 'Received', repr(data)

然后 ,你会问这和http有什么关系,好吧,如果这样的话,就只能说你和我一样是白的不能再白的小白了!
如果你在浏览器中访问:http://localhost:8082/, 神奇的事情发生了。自己去看http协议吧 & 。)

》 封装

如果我们这样些后端代码,估计python也快走向没落了。我们变得优雅一点,于是就有了,Django,flask,tornado等(我就用过这几个%)),我们先看一个分装好的类SocketServer下的TCPServer,源代码非常推荐大家看看!!!!
来个流程图

python WSGI那些事
Httpserver.jpg

其实我已经没有什么好说的了,但是,我看到这里不知道如何做处理,如何写View。然后我就去看flask的源码,还不能解决,又去看werkzeug中的代码,然后就懂了。其实你看BaseHTTPRequestHandler的handle时就应该有点思路了。没错,就是在handle中加入处理逻辑。

》再来说说这个WSGI

先给个链接:http://www.nowamagic.net/academy/detail/1330310

核心是这段代码:
  def application(environ, start_response):
        start_response('200 OK', [('Content-Type', 'text/html')])
        return '<h1>Hello, web!</h1>'

其实我们熟悉的django等叫做WebApplication,这个environ是由一个叫做WebServer传给我们的,也是由他完成最后的socket处理。当然还有一个中间件,看装饰器就明白了。
这个environ中是封装好的request,包括路由,请求方法,请求头,body等,start_response会处理请求头,
好吧,语言无力,直接看代码, 非常清晰

 def handle_one_request(self):
    """Handle a single HTTP request."""
    self.raw_requestline = self.rfile.readline()
    if not self.raw_requestline:
        self.close_connection = 1
    elif self.parse_request():
        return self.run_wsgi()


def run_wsgi(self):
    if self.headers.get('Expect', '').lower().strip() == '100-continue':
        self.wfile.write(b'HTTP/1.1 100 Continue\r\n\r\n')

    self.environ = environ = self.make_environ()
    headers_set = []
    headers_sent = []

    def write(data):
        assert headers_set, 'write() before start_response'
        if not headers_sent:
            status, response_headers = headers_sent[:] = headers_set
            try:
                code, msg = status.split(None, 1)
            except ValueError:
                code, msg = status, ""
            self.send_response(int(code), msg)
            header_keys = set()
            for key, value in response_headers:
                self.send_header(key, value)
                key = key.lower()
                header_keys.add(key)
            if 'content-length' not in header_keys:
                self.close_connection = True
                self.send_header('Connection', 'close')
            if 'server' not in header_keys:
                self.send_header('Server', self.version_string())
            if 'date' not in header_keys:
                self.send_header('Date', self.date_time_string())
            self.end_headers()

        assert isinstance(data, bytes), 'applications must write bytes'
        self.wfile.write(data)
        self.wfile.flush()
            def start_response(status, response_headers, exc_info=None):
        if exc_info:
            try:
                if headers_sent:
                    reraise(*exc_info)
            finally:
                exc_info = None
        elif headers_set:
            raise AssertionError('Headers already set')
        headers_set[:] = [status, response_headers]
        return write

    def execute(app):
        application_iter = app(environ, start_response)
        try:
            for data in application_iter:
                write(data)
            if not headers_sent:
                write(b'')
        finally:
            if hasattr(application_iter, 'close'):
                application_iter.close()
            application_iter = None

    try:
        execute(self.server.app)
    except (socket.error, socket.timeout) as e:
        self.connection_dropped(e, environ)
    except Exception:
        if self.server.passthrough_errors:
            raise
        from werkzeug.debug.tbtools import get_current_traceback
        traceback = get_current_traceback(ignore_system_exceptions=True)
        try:
            # if we haven't yet sent the headers but they are set
            # we roll back to be able to set them again.
            if not headers_sent:
                del headers_set[:]
            execute(InternalServerError())
        except Exception:
            pass
        self.server.log('error', 'Error on request:\n%s',
                        traceback.plaintext)

》OVER

多看源码,这一部分结束,以后看看模板和ORM