关于WSGI的阅读笔记

374 阅读8分钟

segmentfault.com/a/119000001… 原文的阅读笔记;

WSGI 是什么,因何而生? HTTP请求是如何到应用程序的? 实现一个简单的 WSGI Server 实现“高并发”的WSGI Server 第一次路由:PasteDeploy PasteDeploy 使用说明 webob.dec.wsgify 装饰器 第二次路由:中间件 routes 路由

  1. WSGI 是什么,因何而生? WSGI是 Web Server Gateway Interface 的缩写。 它是 Python应用程序(application)或框架(如 Django)和 Web服务器之间的一种接口,已经被广泛接受。 它是一种协议,一种规范,其是在 PEP 333提出的,并在 PEP 3333 进行补充(主要是为了支持 Python3.x)。这个协议旨在解决众多 web 框架和web server软件的兼容问题。有了WSGI,你不用再因为你使用的web 框架而去选择特定的 web server软件。

WSGI 接口有服务端和应用端两部分,服务端也可以叫网关端,应用端也叫框架端。服务端调用一个由应用端提供的可调用对象。如何提供这个对象,由服务端决定。例如某些服务器或者网关需要应用的部署者写一段脚本,以创建服务器或者网关的实例,并且为这个实例提供一个应用实例。另一些服务器或者网关则可能使用配置文件或其他方法以指定应用实例应该从哪里导入或获取。

1、两级结构 在这种结构里,uWSGI作为服务器,它用到了HTTP协议以及wsgi协议,flask应用作为application,实现了wsgi协议。当有客户端发来请求,uWSGI接受请求,调用flask app得到相应,之后相应给客户端。 这里说一点,通常来说,Flask等web框架会自己附带一个wsgi服务器(这就是flask应用可以直接启动的原因),但是这只是在开发阶段用到的,在生产环境是不够用的,所以用到了uwsgi这个性能高的wsgi服务器。

2、三级结构 这种结构里,uWSGI作为中间件,它用到了uwsgi协议(与nginx通信),wsgi协议(调用Flask app)。当有客户端发来请求,nginx先做处理(静态资源是nginx的强项),无法处理的请求(uWSGI),最后的相应也是nginx回复给客户端的。 多了一层反向代理有什么好处? 提高web server性能(uWSGI处理静态资源不如nginx;nginx会在收到一个完整的http请求后再转发给wWSGI) nginx可以做负载均衡(前提是有多个服务器),保护了实际的web服务器(客户端是和nginx交互而不是uWSGI)

blog.csdn.net/u013421629/… Tornado是不适用WSGI协议的

WSGI 不是服务器 不是Python模块 框架 API或任何软件,是一种规范,描述web server如何与web application通信的规范。 要实现WSGI协议,必须同时实现web server和web application,当前运行在WSGI协议智商的web框架有 Tornado, flask, Django

uwsgi 与WSGI一样是一个中通信协议,是uwsgi服务器独占协议,用于定义传输信息的类型(type of information),每一个uwsgi packet前 4type 为传输信息类型的描述,与WSGI协议是两种东西,据说该协议是fcgi协议的10倍快。

uWSGI 是一个web服务器,实现了WSGI协议、uwsgi协议、HTTP协议等。

WSGI协议主要包括 server和application两部分:

	WSGI server负责从客户端接收请求,将request转发给application,将application返回的response返回给客户端;
    WSGI application接收由server转发的request,处理请求,并将处理结果返回给server。application中可以包括多个栈式的中间件(middlewares),这些中间件需要同时实现server与application,因此可以在WSGI服务器与WSGI应用之间起调节作用;对服务器来说,中间件扮演应用程序,对应用程序来说,中间件扮演服务器。
    

WSGI协议其实是定义了一种server与application解耦规范, 即可以有多个实现WSGI server的服务器,也可以有多个实现WSGI application的框架,那么就可以选择任意的server和application组合实现自己的web应用。例如 uWSGI和Gunicorn都是实现了WSGI server协议的服务器,Django flask是实现了WSGI application协议的web框架,可以根据项目实际情况搭配使用。

Django 如何实现自身的WSGI


WSGI规定每个Python程序application必须是一个可调用对象(实现了__call__函数的方法或者类),接受两个参数environ(WSGI的环境信息)和start_response(开始响应请求的函数),并且返回iterable

environ和start_response是由HTTP server提供并实现
environ 变量是包含了环境信息的字典
application 内部在返回前调用 start_response
start_response 也是一个callable,接受两个必须的参数,status(HTTP状态)和response_headers(响应消息的头)可调用对象要返回一个值,这个值是可迭代的。

可调用程序

# 1. 可调用对象是一个函数

def application(environ, start_response):
	response_body = 'The request method was %s' % environ['REQUEST_METHOD']
    status = '200 OK'
    
    response_headers = [('Content-Type', 'text/plain'), ('Content-Length', str(len(response_body)))]
    
    # 调用服务器程序提供的 start_response,填入两个参数
    start_response(status, response_headers)
    
    # 返回必须是一个 iterable
    
    return [response_body]
    
# 2. 可调用对象是一个类
class AppClass:
	'''
     这里的可调用对象就是一个类,调用它就能生成可以迭代的结果。
     使用方法类似 :
     for result in AppClass(env, start_response):
     	do_somthing(result)
    '''
    def __init__(self, environ, start_response):
    	self.environ = environ
        self.start = start_response
        
    def __iter__(self):
    	status = '200 ok '
        response_headers = [(Content-Type, 'text/plain')]
        self.start(status, response_headers)
 		yield "hello world !\n"
# 3. 可调用对象是一个实例
  
classj AppClass:
	"""
    这里的可调用对象就是一个AppClass实例,使用方法类似于:
	app = AppClass()
    for result in app(environ, start_response):
    	do_somthing(result)
    
    
    def __init__(self):
    	pass
    
    def __call__(self, environ, start_response):
    	status = '200 ok '
        response_headers = [('Content-type', 'text/plain')]
        self.start(status, response_headers)
        yield "hello world! \n"
    """
                   
 
 

服务器程序端 上面说过,标准要能够确切的执行,必须要求程序段和服务器端共同遵守。上面提到,envrion和start_response都是服务端提供的。

准备 environ 参数
定义 start_response 函数
调用程序端的可调用对象
import os,sys

def run_with_cgi(application):
	# 准备environ 参数,这是一个字典,这里面的内容是一次 HTTP请求的环境变量
    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
    environ['wsgi.url_scheme']	= 'http'
    
    headers_set = []
    headers_sent = []
    
    # 把应答的结果输出到终端
def write(data):
    sys.stdout.write(data)
    sys.stdout.flush()
        
# 实现 start_response 函数,根据程序端传过来的status 和response_headers 参数
# 设置状态和头部
def start_response(status, response_headers, exc_info=None):
	headers_set[:] = [status, response_headers]
	return write
    
# 调用客户端的可调用对象,把准备好的参数传递进去
result = application(environ, start_response)

# 处理得到的结果,这里简单的把结果输出到标准输出

try:
	for data in result:
    	if data:
        	write(data)
            
 finally:
 	if hasattr(result, 'close'):
    	result.close()
 
    
    

a[: :] = ['1', '2', '3'] a ['1', '2', '3']

django WSGI application

WSGI application 应该实现一个可调用对象 iter,例如 函数、方法、类(包含 call 方法)。需要接受两个参数:一个字典,该字典可以包含客户端请求信息及其他心态,可以认为是请求上下文,一般叫做environment(编码中多简写为environ 或 env),一个用于发送HTTP的相应状态(HTTP status)、响应头(HTTP headers)的回调函数,也就是start_response()。通过回调函数将响应状态和响应头返回给server,同时返回正文(response body), 响应正文是可迭代的、并包含多个字符串。

class WSGIHandler(base.BaseHandler):
	initLock = Lock()
    request_class = WSGIRequest
    def __call__(self, environ, start_response):
		# 加载中间件
        if self._request_middleware is None:
			with self.initLock:
				try:
					if self._request_middleware is None:
						self.load_middleware()
                except:
					self._request_middleware = None raise
    	set_script_prefix(get_script_name(environ))
        				       signals.request_started.send(sender=self.__class__, environ=environ)
                              
        try:
        	request = self.request_class(environ)
        except UnicodeDecodeError:
			logger.warning('Bad Request (UnicodeDecodeError)', exc_info=sys.exc_info(), extra={'status_code': 400,})
  			response = http.HttpResponseBadRequest()
        else:
        	response = self.get_response(request)
        response._handler_class = self.__class__ status = '%s %s' % (response.status_code, response.reason_phrase)
        response._handlers =  [(str(k), str(v)) for k, v in response.items()]
		for c in response._handlers.append((str('Set-Cookie'), str(c.output(header='))))
        # server 提供的回调方法,将响应的header和status返回给server
        start_response(force_str(status), response_headers)
        
        if getattr(response, 'file_to_stream', None) is not None and environ.get('wsgi.file_wrapper'):
			response = environ['wsgi.file_wrapper'](response.file_to_stream)
        return response

加载所有中间件,以及执行框架相关的操作,设置当前线程脚本前缀,发送请求信号;处理请求,调用get_response()方法处理当前请求,该方法的主要逻辑是通过urlconf找到对应的view和callback, 按顺序执行各种middleware和callback。调用由server传入的start_response()方法 将响应header与status返回给server。返回响应正文 django WSGI server

负责获取HTTP请求,将请求传递给WSGI application,由application请求后返回 response。以Django内建server为例,通过runserver 运行Django,在启动时都会调用run 方法,创建一个WSGIserver的实例,之后在调用其server_forever()方法启动服务


def run(addr, port, wsgi_handler, ipv6=False, threading=False):
	server_address = (addr, port)
    if threading:
		httpd_cls = type(str('WSGIserver'), (socketserver.ThreadingMixIn, WSGIServer), {})
    else:
    	httpd_cls = WSGIServer
    httpd = httpd_cls(server_address, WSGIRequestHandlers, ipv6=ipv6)
    if threading:
		httpd.daemon_threads = True httpd.set_app(wsgi_handler)
        httpd.server_forever()
        

WSGI server

WSGIServer

run() 方法会创建一个WSGIserver 实例,主要作用是接收客户端请求,将请求传递给application,然后将application返回的response返回给客户端。 创建实例时会指定HTTP请求的handler: WSGIRequestHandler类 通过set_app和get_app方法设置和获取WSGIApplication实例wsgi_handler处理HTTP请求是,调用handler_request方法,会创建 WSGIRequestHandler实例处理HTTP请求; WSGIServer中get_request方法通过socket接收请求数据

WSGIRequestHandler

由WSGIserver在调用handler_request时创建的实例,传入 request client_address wsgiserver三个参数,__init__方法在实例化的同时还会调用自身的handle方法,handle方法会创建ServerHandler实例,然后调用其run方法处理请求

ServerHandler

WSGIRequestHandler 在其handle方法中调用 run方法,传入 self.sever.get_app()参数,获取WSGIApplication,然后调用实例(call),获取Request,其中会传入start_response回调,用来处理返回的hander和status。通过Application获取response以后,通过finish_response返回response

WSGIHandler

WSGI协议中的Application,接收两个参数,environ 字典包含了客户端请求的信息以及其他信息,可以认为是请求上下文,start_response 用于发送status和header的回调函数

实际环境使用的WSGI服务器


gunicorn

uwsgi

fcgi

bjoern