cy_88 发表于 2018-6-1 12:15:25

openstack nova 基础知识——wsgi

激励自己的话:
在微博上看到一句话:一件事成功的概率只有1%,但是要是做上100遍的话,成功的概率将会是63%,这句话对现在的我真是一个极大的鼓励。


正文:
在nova源码中看到了wsgi这个模块,不懂,于是谷歌去,又全是英文啊!
官方网站:http://wsgi.readthedocs.org/en/latest/index.html


1.什么是wsgi?

Web服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)是Python应用程序或框架和Web服务器之间的一种接口,已经被广泛接受, 它已基本达成它了可移植性方面的目标。WSGI 没有官方的实现, 因为WSGI更像一个协议. 只要遵照这些协议,WSGI应用(Application)都可以在任何实现(Server)上运行, 反之亦然。


2.如何实现WSGI?

看了几个例子,发现有这么几个“角色”:application、server、client,它们之间的关系是client发送请求给server,server转发这个请求给application,然后application进行一系列操作之后,将响应发送给server,server再将响应转发给client,server就起一个转发的作用。
WSGI就在application和server之间发挥作用:
首先是在server中定义了一个start_response(status,response_headers,exc_info=None)方法,返回值是一个write()函数,也就是在必须在执行过start_response()函数之后,才能执行write()方法。还定义了environ变量,即环境变量,里面存放的是server接收到的client端的环境变量。WSGI就是规定了server要将start_response和environ发送给application。
然后就是application就要有接收这两个参数的“接口”了,这个“接口”可以以各种方式实现:方法、函数、类都可以。接受到之后,就要在application端调用start_resopnse()方法,而且这个调用一定要在return之前。WSGI就是规定了application要实现接收这两个参数的“接口”,并且执行start_response()。
server在接收到application的响应之后,就开始write()数据给client,首先要判断start_response()方法执行了没有,如果没有执行,那么就报出:AssertionError("write() before start_response()") 这样的异常。如果执行了start_response(),那么write()就顺利执行。


经过这样的“接口”设计,就可以让application在任何server上运行了?Maybe吧!



3.如何使用wsgi?
这里具一个简单的例子,是从官网上看到的,觉得很简单,很具有代表性:


  查看文本打印

[*]  #! /usr/bin/env python
[*]  
[*]  # Our tutorial's WSGI server
[*]  from wsgiref.simple_server import make_server
[*]  
[*]  def application(environ, start_response):
[*]     “”“这个就是application,实现了接收这两个参数的接口,并且在下面调用了start_response()”“”
[*]     # Sorting and stringifying the environment key, value pairs
[*]     response_body = ['%s: %s' % (key, value)
[*]                    for key, value in sorted(environ.items())]
[*]     response_body = '\n'.join(response_body)
[*]  
[*]     status = '200 OK'
[*]     response_headers = [('Content-Type', 'text/plain'),
[*]                    ('Content-Length', str(len(response_body)))]
[*]     start_response(status, response_headers)
[*]  
[*]     return
[*]  
[*]  # Instantiate the WSGI server.
[*]  # It will receive the request, pass it to the application
[*]  # and send the application's response to the client
[*]  # 这个是server,在它内部定义了start_response()方法和environ变量,并且调用application
[*]  httpd = make_server(
[*]     'localhost', # The host name.
[*]     8051, # A port number where to wait for the request.
[*]     application # Our application object name, in this case a function.
[*]     )
[*]  
[*]  # Wait for a single request, serve it and quit.
[*]  httpd.handle_request()
  查看文本打印

[*]  #! /usr/bin/env python
[*]  
[*]  # Our tutorial's WSGI server
[*]  from wsgiref.simple_server import make_server
[*]  
[*]  def application(environ, start_response):
[*]     “”“这个就是application,实现了接收这两个参数的接口,并且在下面调用了start_response()”“”
[*]     # Sorting and stringifying the environment key, value pairs
[*]     response_body = ['%s: %s' % (key, value)
[*]                    for key, value in sorted(environ.items())]
[*]     response_body = '\n'.join(response_body)
[*]  
[*]     status = '200 OK'
[*]     response_headers = [('Content-Type', 'text/plain'),
[*]                    ('Content-Length', str(len(response_body)))]
[*]     start_response(status, response_headers)
[*]  
[*]     return
[*]  
[*]  # Instantiate the WSGI server.
[*]  # It will receive the request, pass it to the application
[*]  # and send the application's response to the client
[*]  # 这个是server,在它内部定义了start_response()方法和environ变量,并且调用application
[*]  httpd = make_server(
[*]     'localhost', # The host name.
[*]     8051, # A port number where to wait for the request.
[*]     application # Our application object name, in this case a function.
[*]     )
[*]  
[*]  # Wait for a single request, serve it and quit.
[*]  httpd.handle_request()


如果想看server内部是如何实现的话,可以看一下PEP333上的这个例子:


  查看文本打印

[*]  import os, sys
[*]  
[*]  def run_with_cgi(application):
[*]  
[*]      environ = dict(os.environ.items())
[*]      environ['wsgi.input']      = sys.stdin
[*]      environ['wsgi.errors']       = sys.stderr
[*]      environ['wsgi.version']      = (1, 0)
[*]      environ['wsgi.multithread']= False
[*]      environ['wsgi.multiprocess'] = True
[*]      environ['wsgi.run_once']   = True
[*]  
[*]      if environ.get('HTTPS', 'off') in ('on', '1'):
[*]        environ['wsgi.url_scheme'] = 'https'
[*]      else:
[*]        environ['wsgi.url_scheme'] = 'http'
[*]  
[*]      headers_set = []
[*]      headers_sent = []
[*]  
[*]      def write(data):
[*]        if not headers_set:
[*]               raise AssertionError("write() before start_response()")
[*]  
[*]        elif not headers_sent:
[*]               # Before the first output, send the stored headers
[*]               status, response_headers = headers_sent[:] = headers_set
[*]               sys.stdout.write('Status: %s\r\n' % status)
[*]               for header in response_headers:
[*]                 sys.stdout.write('%s: %s\r\n' % header)
[*]               sys.stdout.write('\r\n')
[*]  
[*]        sys.stdout.write(data)
[*]        sys.stdout.flush()
[*]  
[*]      def start_response(status, response_headers, exc_info=None):
[*]        if exc_info:
[*]              try:
[*]                  if headers_sent:
[*]                    # Re-raise original exception if headers sent
[*]                    raise exc_info[0], exc_info[1], exc_info[2]
[*]              finally:
[*]                  exc_info = None   # avoid dangling circular ref
[*]        elif headers_set:
[*]              raise AssertionError("Headers already set!")
[*]  
[*]        headers_set[:] =
[*]        return write
[*]  
[*]      result = application(environ, start_response)
[*]      try:
[*]        for data in result:
[*]              if data:    # don't send headers until body appears
[*]                  write(data)
[*]        if not headers_sent:
[*]              write('')   # send headers now if body was empty
[*]      finally:
[*]        if hasattr(result, 'close'):
[*]              result.close()
  查看文本打印

[*]  import os, sys
[*]  
[*]  def run_with_cgi(application):
[*]  
[*]      environ = dict(os.environ.items())
[*]      environ['wsgi.input']      = sys.stdin
[*]      environ['wsgi.errors']       = sys.stderr
[*]      environ['wsgi.version']      = (1, 0)
[*]      environ['wsgi.multithread']= False
[*]      environ['wsgi.multiprocess'] = True
[*]      environ['wsgi.run_once']   = True
[*]  
[*]      if environ.get('HTTPS', 'off') in ('on', '1'):
[*]        environ['wsgi.url_scheme'] = 'https'
[*]      else:
[*]        environ['wsgi.url_scheme'] = 'http'
[*]  
[*]      headers_set = []
[*]      headers_sent = []
[*]  
[*]      def write(data):
[*]        if not headers_set:
[*]               raise AssertionError("write() before start_response()")
[*]  
[*]        elif not headers_sent:
[*]               # Before the first output, send the stored headers
[*]               status, response_headers = headers_sent[:] = headers_set
[*]               sys.stdout.write('Status: %s\r\n' % status)
[*]               for header in response_headers:
[*]                 sys.stdout.write('%s: %s\r\n' % header)
[*]               sys.stdout.write('\r\n')
[*]  
[*]        sys.stdout.write(data)
[*]        sys.stdout.flush()
[*]  
[*]      def start_response(status, response_headers, exc_info=None):
[*]        if exc_info:
[*]              try:
[*]                  if headers_sent:
[*]                    # Re-raise original exception if headers sent
[*]                    raise exc_info[0], exc_info[1], exc_info[2]
[*]              finally:
[*]                  exc_info = None   # avoid dangling circular ref
[*]        elif headers_set:
[*]              raise AssertionError("Headers already set!")
[*]  
[*]        headers_set[:] =
[*]        return write
[*]  
[*]      result = application(environ, start_response)
[*]      try:
[*]        for data in result:
[*]              if data:    # don't send headers until body appears
[*]                  write(data)
[*]        if not headers_sent:
[*]              write('')   # send headers now if body was empty
[*]      finally:
[*]        if hasattr(result, 'close'):
[*]              result.close()
  
4.eventlet中的wsgi


因为nova中用的是eventlet中的wsgi,所以再来看一下wsgi在eventlet中是怎么实现的。其实不用看源码也可以知道evenlet中,将和服务器建立的连接放在了绿色线程中,socket用的也是绿化过的socket。简单的使用,如下示例:


  查看文本打印

[*]  from eventlet import wsgi
[*]  import eventlet
[*]  
[*]  def hello_world(env, start_response):
[*]      start_response('200 OK', [('Content-Type', 'text/plain')])
[*]      return ['Hello, World!\r\n']
[*]  
[*]  wsgi.server(eventlet.listen(('', 8090)), hello_world)
  查看文本打印

[*]  from eventlet import wsgi
[*]  import eventlet
[*]  
[*]  def hello_world(env, start_response):
[*]      start_response('200 OK', [('Content-Type', 'text/plain')])
[*]      return ['Hello, World!\r\n']
[*]  
[*]  wsgi.server(eventlet.listen(('', 8090)), hello_world)
  



参考资料:
http://webpython.codepoint.net/wsgi_tutorial
http://www.python.org/dev/peps/pep-0333/
http://eventlet.net/doc/modules/wsgi.html
  
页: [1]
查看完整版本: openstack nova 基础知识——wsgi