在上原文中,我给大伙儿留了一个问题:怎样在错误服务器编码作一切改动的前提下,根据该服务器运作Djando运用、Flask运用和Pyramid应用,一起达到这种不一样网络框架的需要?看完本文,你就可以回应这个问题了。
之前,你挑选的Python网络框架可能限定所可以采用的网络服务器,相反也是。假如框架和服务器在设计方案时便是能够互相搭配的,那你就不会遭遇这个问题:
可是假如你尝试将设计方案不相对应的服务器与框架相融合,那么你毫无疑问便会遇到下边这张图所呈现的这个问题:
这就代表着,你大部分只有应用可以正常运转的服务器与框架组成,而不可以选择你期待应用的服务器或框架。
那样,你如何保证能够在没有改动网络服务器编码或网络框架代码的情况下,应用自己选择的服务器,而且配对好几个不一样的网络框架呢?因为处理这个问题,就出现了Python Web服务器网关ip插口(Python Web Server Gateway Interface,通称“WSGI”)。
WSGI的发生,让开发者能将网络框架与网络服务器的挑选隔开起来,不会再互相限定。如今,你能真真正正地将不一样的网络服务器与网络开发设计框架开展混和配搭,挑选满足自己要求的组成。比如,你可以用Gunicorn或Nginx/uWSGI或Waitress服务器来运作Django、Flask或Pyramid运用。恰好是因为服务器和框架均适用WSGI,才真真正正得以实现二者之间的随意混和配搭。
因此,WSGI便是我还在上一篇文章中常留问题的答案。你网络服务器务必完成一个服务器端WSGI插口,而现阶段全部当代Python网络框架都已实现了框架端WSGI插口,那样开发者不用改动服务器的编码,就能够适用某一网络框架。
网络服务器和网络框架适用WSGI协议书,不但让运用开发者挑选满足自身要求的组成,从而有益于服务器和框架的开发者,因为他们能将注意力集中在自已擅长的领域,而非互相掣肘。别的计算机语言也有着相近的插口:比如Java的Servlet API和Ruby的Rack。
空口无凭,我想你毫无疑问在想:“无代码无实情!”既然这样,我一直在这里得出一个非常简单的WSGI服务器完成:
# Tested with Python 2.7.9, Linux & Mac OS X
import socket
import StringIO
import sys
class WSGIServer(object):
address_family = socket.AF_INET
socket_type = socket.SOCK_STREAM
request_queue_size = 1
def __init__(self, server_address):
# Create a listening socket
self.listen_socket = listen_socket = socket.socket(
self.address_family,
self.socket_type
)
# Allow to reuse the same address
listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# Bind
listen_socket.bind(server_address)
# Activate
listen_socket.listen(self.request_queue_size)
# Get server host name and port
host, port = self.listen_socket.getsockname[:2]
self.server_name = socket.getfqdn(host)
self.server_port = port
# Return headers set by Web framework/Web application
self.headers_set =
def set_app(self, application):
self.application = application
def serve_forever(self):
listen_socket = self.listen_socket
while True:
# New client connection
self.client_connection, client_address = listen_socket.accept
# Handle one request and close the client connection. Then
# loop over to wait for another client connection
self.handle_one_request
def handle_one_request(self):
self.request_data = request_data = self.client_connection.recv(1024)
# Print formatted request data a la \'curl -v\'
print(\'\'.join(
\'< {line}n\'.format(line=line)
for line in request_data.splitlines
))
self.parse_request(request_data)
# Construct environment dictionary using request data
env = self.get_environ
# It\'s time to call our application callable and get
# back a result that will become HTTP response body
result = self.application(env, self.start_response)
# Construct a response and send it back to the client
self.finish_response(result)
def parse_request(self, text):
request_line = text.splitlines[0]
request_line = request_line.rstrip(\'rn\')
# Break down the request line into components
(self.request_method, # GET
self.path, # /hello
self.request_version # HTTP/1.1
) = request_line.split
def get_environ(self):
env = {}
# The following code snippet does not follow PEP8 conventions
# but it\'s formatted the way it is for demonstration purposes
# to emphasize the required variables and their values
#
# Required WSGI variables
env[\'wsgi.version\'] = (1,0)
env[\'wsgi.url_scheme\'] = \'http\'
env[\'wsgi.input\'] = StringIO.StringIO(self.request_data)
env[\'wsgi.errors\'] = sys.stderr
env[\'wsgi.multithread\'] = False
env[\'wsgi.multiprocess\'] = False
env[\'wsgi.run_once\'] = False
# Required CGI variables
env[\'REQUEST_METHOD\'] = self.request_method # GET
env[\'PATH_INFO\'] = self.path # /hello
env[\'SERVER_NAME\'] = self.server_name # localhost
env[\'SERVER_PORT\'] = str(self.server_port) # 8888
return env
def start_response(self, status, response_headers, exc_info=None):
# Add necessary server headers
server_headers = [
(\'Date\', \'Tue, 31 Mar 2015 12:54:48 GMT\'),
(\'Server\', \'WSGIServer 0.2\'),
]
self.headers_set = [status, response_headers server_headers]
# To adhere to WSGI specification the start_response must return
# a \'write\' callable. We simplicity\'s sake we\'ll ignore that detail
# for now.
# return self.finish_response
def finish_response(self, result):
try:
status, response_headers = self.headers_set
response = \'HTTP/1.1 {status}rn\'.format(status=status)
for header in response_headers:
response = \'{0}: {1}rn\'.format(*header)
response = \'rn\'
for data in result:
response = data
# Print formatted response data a la \'curl -v\'
print(\'\'.join(
\'> {line}n\'.format(line=line)
for line in response.splitlines
))
self.client_connection.sendall(response)
finally:
self.client_connection.close
SERVER_ADDRESS = (HOST, PORT) = \'\', 8888
def make_server(server_address, application):
server = WSGIServer(server_address)
server.set_app(application)
return server
if __name__ == \'__main__\':
if len(sys.argv) < 2:
sys.exit(\'Provide a WSGI application object as module:callable\')
app_path = sys.argv[1]
module, application = app_path.split(\':\')
module = __import__(module)
application = getattr(module, application)
httpd = make_server(SERVER_ADDRESS, application)
print(\'WSGIServer: Serving HTTP on port {port} ...n\'.format(port=PORT))
httpd.serve_forever
以上的编码比第一部分的网络服务器完成编码要长的多,可是这种编码具体也不会过长,仅有不上150行,大伙儿了解下去并不会太艰难。上边这一服务器的作用也越多——它能够运作你应用自己喜欢的架构所作出去的网络技术应用,无论你挑选Pyramid、Flask、Django或者其它适用WSGI协议书的架构。
你不相信?你可以自己测试一下,看一下结果如何。将以上编码储存为 webserver2.py
,或是立即从我的Github仓库免费下载。假如你运作该文档时没给予一切主要参数,那样程序流程便会出错并撤出。
$ python webserver2.py
Providea WSGI application object as module:callable
以上程序流程设计的目的,便是运作你研发的网络技术应用,但是你还要达到一些它规定。要运作网络服务器,你只必须组装Python就可以。可是要运作应用Pyramid、Flask和Django等架构研发的网络技术应用,你还要先组装这种架构。大家下面组装这三种架构。我侧重于应用 virtualenv
组装。请依照下边的提醒建立并激话一个虚拟器,随后组装这三个互联网架构。
$ [sudo] pip install virtualenv
$ mkdir ~/envs
$ virtualenv ~/envs/lsbaws/
$ cd ~/envs/lsbaws/
$ ls
bin include lib
$ source bin/activate
(lsbaws) $ pip install pyramid
(lsbaws) $ pip install flask
(lsbaws) $ pip install django
下面,你必须建立一个网络技术应用。大家最先建立Pyramid运用。将下边的编码储存为 pyramidapp.py
文档,放至webserver2.py
所属的文件中,或是立即从我的Github仓库免费下载该文档:
from pyramid.config import Configurator
from pyramid.response import Response
def hello_world(request):
return Response(
\'Hello world from Pyramid!n\',
content_type=\'text/plain\',
)
config = Configurator
config.add_route(\'hello\', \'/hello\')
config.add_view(hello_world, route_name=\'hello\')
app = config.make_wsgi_app
如今,你能通过自身研发的网站服务器来运行里面的Pyramid运用。
(lsbaws) $ python webserver2.py pyramidapp:app
WSGIServer:ServingHTTP on port8888...
在运作 webserver2.py
时,你告知自己的服务器去载入pyramidapp
控制模块里的app
可启用目标(callable)。你网络服务器现在可以接受HTTP要求,并把要求转站至你Pyramid运用。运用现阶段只有解决一个路由器(route):/hello。在浏览器的搜索框键入http://localhost:8888/hello
,按住回车,观查会有什么原因:
你还能够在命令行应用 curl
指令,来检测服务器运行情况:
$ curl -v http://localhost:8888/hello
...
下面大家建立Flask运用。多次重复上边的流程。
from flask import Flask
from flask import Response
flask_app = Flask(\'flaskapp\')
@flask_app.route(\'/hello\')
def hello_world:
return Response(
\'Hello world from Flask!n\',
mimetype=\'text/plain\'
)
app = flask_app.wsgi_app
将里面的编码储存为 flaskapp.py
,或是立即从我的Github仓库免费下载文档,并运作:
(lsbaws) $ python webserver2.py flaskapp:app
WSGIServer:ServingHTTP on port8888...
随后在浏览器搜索框键入 http://localhost:8888/hello
,并按住回车键:
一样,在命令行应用 curl
指令,看一下服务器是否会回到Flask运用产生的消息:
$ curl -v http://localhost:8888/hello
...
这一服务器是否也可以适用Django运用?试一试就知道了!但是接下来的实际操作更加繁杂一些,我提议大伙儿复制全部库房,并运用当中的 djangoapp.py
文档。下边的编码将一个名字叫做helloworld
的Django运用加上至现阶段的Python途径中,随后导进了此项目地WSGI运用。
import sys
sys.path.insert(0, \'./helloworld\')
from helloworld import wsgi
app = wsgi.application
将里面的编码储存为 djangoapp.py
,并运用你开发的服务器运作这一Django运用。
(lsbaws) $ python webserver2.py djangoapp:app
WSGIServer:ServingHTTP on port8888...
一样,在浏览器中键入 http://localhost:8888/hello
,并按住回车:
下面,和之前几回一样,你根据命令行应用 curl
指令开展检测,确认了这一Djando运用取得成功处理了你发送的申请:
$ curl -v http://localhost:8888/hello
...
你有没有依照上边的过程检测?你做到了让服务器适用所有三种架构吗?要是没有,请尽可能亲自动手实际操作。阅读文章编码至关重要,但这系列产品文章内容的目的是为了再次开发,而这代表你必须自身亲力亲为。最好是是你自己重新输入每一个编码,并保证程序执行结论符合预期。
经由上边的详细介绍,你应当早已了解到WSGI的强劲之处:它可以让你随意混和配搭互联网服务器和架构。WSGI为Python网络服务器与Python网络架构中间的互动带来了一个简约的插口,并且很容易在服务器端和架构端完成。下边的字符串常量各自展示了服务器端和架构端WSGI插口:
def run_application(application):
\"\"\"Server code.\"\"\"
# This is where an application/framework stores
# an HTTP status and HTTP response headers for the server
# to transmit to the client
headers_set =
# Environment dictionary with WSGI/CGI variables
environ = {}
def start_response(status, response_headers, exc_info=None):
headers_set[:] = [status, response_headers]
# Server invokes the ‘application\' callable and gets back the
# response body
result = application(environ, start_response)
# Server builds an HTTP response and transmits it to the client
…
def app(environ, start_response):
\"\"\"A barebones WSGI app.\"\"\"
start_response(\'200 OK\', [(\'Content-Type\', \'text/plain\')])
return [\'Hello world!\']
run_application(app)
下边给各位解释一下以上编码的原理:
- 互联网架构给予一个命名为
application
的可启用目标(WSGI协议书并没特定如何做到这一目标)。 - 服务器每一次从HTTP手机客户端接受要求以后,启用
application
。它会向可启用目标传送一个名字叫做environ
的词典做为主要参数,在其中包括了WSGI/CGI的许多变量值,和一个名叫start_response
的可启用目标。 - 架构/运用形成HTTP状态码及其HTTP回应报头(HTTP response headers),随后将二者传送至
start_response
,等候服务器储存。除此之外,架构/运用还将回到回应的文章正文。 - 服务器将状态码、回应报头和回应文章正文组成HTTP回应,并返还给手机客户端(这一步并不属于WSGI协议书)。
下边这张图形象化地说明了WSGI插口的状况:
有一点要提醒大家,如果你应用以上架构开发网络技术应用的情况下,你解决是指更高层住宅级的逻辑性,并不会立即解决WSGI协议书有关的规定,但是我比较清楚,既然你正在看本文,你一定对架构端WSGI插口特别感兴趣。因此,人们下面在不使用Pyramid、Flask或Djando架构的情况下,自身开发一个简约的WSGI网络技术应用/互联网架构,并应用WSGI服务器运作该运用:
def app(environ, start_response):
\"\"\"A barebones WSGI application.
This is a starting point for your own Web framework :)
\"\"\"
status = \'200 OK\'
response_headers = [(\'Content-Type\', \'text/plain\')]
start_response(status, response_headers)
return [\'Hello world from a simple WSGI application!n\']
将以上代码保存为 wsgiapp.py
文档,或是立即从我的Github仓库免费下载,随后运用网站服务器运作该运用:
(lsbaws) $ python webserver2.py wsgiapp:app
WSGIServer:ServingHTTP on port8888...
在浏览器中键入下面的图里的详细地址,之后按回车。结论应该是这样的:
你刚自身撰写了一个简约的WSGI互联网架构!太难以置信了。
下面,大家再次剖析缺少对象给手机客户端的目标。下边这张图展现的你根据HTTP手机客户端启用Pyramid运用后,网络服务器形成的HTTP回应:
图中里的回应与你在第一篇中见到的一些相近,可是也是有显著不同点。举例说明,在其中就出现了你以前没有看到过的4歌HTTP报头:Content-Type,Content-Length,Date和Server。这些事互联网缺少对象的回应目标一般都是会包括的报头。但是,这四个都不是必须的。报头的目的是传送相关HTTP要求/回应的附加消息。
既然你早已对WSGI插口拥有更加深入的了解,下边这张图对回应目标的具体内容进行了更详尽的说明,说明了每一条内容是怎样形成的。
到现在为止,我还没有详细介绍过 environ
词典的主要内容,但简单来说,这是一个务必包含着WSGI协议书所选定的一些WSGI和CGI自变量。网络服务器从HTTP要求中获得词典需要的值。下边这张图展现是指词典的详细内容:
互联网架构根据该词典给予的信息内容,依据规定的路由器和要求方式等基本参数来确定应用哪一个主视图(views),从哪儿载入要求文章正文,及其怎样导出错误报告。
目前为止,你早已取得成功建立了自身的适用WSGI协议书的网站服务器,还使用不一样的互联网架构开发设计了好几个网络技术应用。此外,你还是自身研发了一个简约的互联网架构。本篇文章详细介绍的具体内容不可以说不丰富多彩。大家下面回顾一下WSGI网站服务器如何处理HTTP要求:
- 最先,服务器启动并载入互联网架构/运用给予的
application
可启用目标 - 随后,网络服务器载入一个要求信息内容
- 随后,网络服务器对申请开展分析
- 随后,网络服务器应用请求数据建立一个名字叫做
environ
的词典 - 随后,网络服务器以
environ
词典和start_response
可启用目标做为主要参数,启用application
,并得到运用产生的回应文章正文。 - 随后,网络服务器依据启用
application
目标后返回的数据信息,及其start_response
设定的状态码和回应标题文字,搭建一个HTTP回应。 - 最终,网络服务器将HTTP回应回到至手机客户端。
之上便是第二部分的全部内容。你现在已经有着了一个正常运转的WSGI网络服务器,能够适用根据遵循WSGI协议书的互联网架构所作的网络技术应用。最棒的是,这种网络服务器能够不用作一切代码改动,就能够与好几个互联网架构相互配合应用。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。