一、web应用服务
Web服务的本质:
1. 浏览器发送一个HTTP请求
2. 服务器收到请求,生成一个HTML文档
3. 服务器把HTML文档作为HTTP响应的Body发送给浏览器
4. 浏览器收到HTTP响应,从HTTP Body取出HTML文档并显示
- 最简单的Web应用服务就是先把HTML用文件保存好,用一个现成的HTTP服务器软件,接收用户请求,从文件中读取HTML,返回。
- 如果要动态生成HTML,就需要把上述步骤(如接受HTTP请求、解析HTTP请求、发送HTTP响应等)自己来实现。这需要你花大量的时间去读HTTP规范。
正确的做法是底层代码由专门的服务器软件实现,我们用Python专注于生成HTML文档。
二、python中的WSGI
WSGI是基于现存的CGI标准而设计的,是Python的CGI包装。WSGI没有官方的实现, 因为WSGI更像一个协议。
- Web服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)是为Python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口。
- WSGI(有时发音作’wiz-gee’)是作为Web服务器与Web应用程序或应用框架之间的一种低级别的接口,以提升可移植Web应用开发的共同点。
1、测试简单的WSGI服务器
- 编写
hello.py
作为一个Web应用程序
'''
application():符合WSGI标准的一个HTTP处理函数,必须由WSGI服务器来调用
environ:一个包含所有HTTP请求信息的dict对象
start_response:一个发送HTTP响应的函数,该函数发送了HTTP响应的Header,注意Header只能发送一次,也就是只能调用一次start_response()函数
'''
def application(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/html')])
return [b'<h1>Hello, World!</h1>']
- 编写
server.py
作为一个WSGI服务器
from wsgiref.simple_server import make_server
# 导入编写的application函数
from hello import application
# 创建一个服务器,IP地址为空,端口是8000,传入函数application
httpd = make_server('', 8000, application)
print('Serving HTTP on port 8000...')
# 开始监听HTTP请求:
httpd.serve_forever()
-
启动WSGI服务器
python server.py
-
使用客户端访问
打开浏览器,输入http://localhost:8000/ ,在浏览器正常显示“Hello, World!”
2、升级版
这次的服务器功能更为全面,同时也将使用flask框架
- 在WSGIServer.py文件下
# python3环境,python2与python3的StringIO模块所在位置不同,同时部分解码也有所差异
import socket
from io import StringIO
import sys
'''
WSGI服务器类
'''
class WSGIServer(object):
address_family = socket.AF_INET #地址家族:IPv4
socket_type = socket.SOCK_STREAM #传输模式:可靠的稳定的(TCP)
request_queue_size = 1 #请求队列大小
def __init__(self, server_address):
# 创建socket,利用socket获取客户端的请求
self.listen_socket = listen_socket = socket.socket(self.address_family, self.socket_type)
# 设置socket的工作模式,SOL_SOCKET设置级别,SO_REUSEADDR大意是允许服务器bind一个地址,即使这个地址当前已经存在已建立的连接
listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定socket地址
listen_socket.bind(server_address)
# socket active, 监听文件描述符
listen_socket.listen(self.request_queue_size)
# 获得serve的host name和port
host, port = self.listen_socket.getsockname()[:2]
self.server_name = socket.getfqdn(host)
self.server_port = port
self.headers_set = []
def set_app(self, application):
self.application = application
#启动WSGI server服务,不停的监听并获取socket数据。
def serve_forever(self):
listen_socket = self.listen_socket
while True:
#接受客户端请求
self.client_connection, client_address = listen_socket.accept()
#处理客户端请求
self.handle_one_request()
def handle_one_request(self): #处理请求
#1.获取请求字节数据
self.request_data = request_data = self.client_connection.recv(1024).decode()
#2.按照协议解析请求字节数据
self.parse_request(request_data)
#3.将请求数据构造成字典并作相应的设置
env = self.get_environ()
#4.给(Web应用程序)flask\tornado传递两个参数,environ,start_response,并获取application返回给WSGI的数据
result = self.application(env, self.start_response)
#5.把数据result返回给客户端
self.finish_response(result)
def parse_request(self, data): #处理socket的http协议
#splitlines()按照行('\r', '\r\n', \n')分隔,返回一个包含各行作为元素的列表,
format_data = data.splitlines()
if len(format_data):
request_line = data.splitlines()[0]
# 去除多余字符
request_line = request_line.rstrip('\r\n')
# ['GET', '/', 'HTTP/1.1']
(self.request_method, self.path, self.request_version) = request_line.split()
def get_environ(self): # 获取environ数据并设置当前server的工作模式
env = {}
env['wsgi.version'] = (1, 0)
env['wsgi.url_scheme'] = 'http'
env['wsgi.input'] = StringIO(self.request_data).getvalue()
env['wsgi.errors'] = sys.stderr
env['wsgi.multithread'] = False
env['wsgi.multiprocess'] = False
env['wsgi.run_once'] = False
# 必需的CGI变量
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): #设置HTTP响应的Header
server_headers = [('Date', 'Tue, 31 Mar 2015 12:54:48 GMT'), ('Server', 'WSGIServer 0.2')]
self.headers_set = [status, response_headers + server_headers]
def finish_response(self, result): #向客户端发送响应
try:
#格式化处理
status, response_headers = self.headers_set
response = 'HTTP/1.1 {status}\r\n'.format(status=status)
for header in response_headers:
response += '{0}: {1}\r\n'.format(*header)
response += '\r\n'
response = str.encode(response)
for data in result:
response += data
#发送响应
self.client_connection.sendall(response)
print(''.join(['> {line}\n'.format(line=line) for line in response.splitlines()]))
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__':
#sys.argv 是获取运行python文件的时候命令行参数,且以list形式存储参数
#sys.argv[0]表示代码本身文件路径
if len(sys.argv) < 2:
sys.exit('Provide a WSGI application object as module:callable')
app_path = sys.argv[1]
#split(':')指定分隔符为:并对字符串进行切片
module, application = app_path.split(':') # 第一个参数是文件名,第二个参数是文件内app的名字
module = __import__(module) #__import__()函数用于动态加载类和函数
application = getattr(module, application) # getattr() 函数用于返回一个对象属性值或获取对象方法的运行结果,getattr(object, name[, default]) -> value
#创建服务器
httpd = make_server(SERVER_ADDRESS, application)
print('WSGIServer: Serving HTTP on port {port} ...\n'.format(port=PORT))
#启动服务器
httpd.serve_forever()
- 在flaskapp.py文件下
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!\r\n' ,
mimetype = 'text/plain'
)
app = flask_app . wsgi_app
- 在终端中输入python.exe .\WSGIServer.py flaskapp:app
- 在浏览器窗口搜索栏中输入http://localhost:8888/hello
三、生产环境下的Web
1、Web服务器
每个web框架都不是专注于实现服务器方面的,因此,在生产环境部署的时候,使用的服务器也不会简单的使用web框架自带的服务器
Nginx
是一个异步框架的 Web服务器,也可以用作反向代理,负载平衡器 和 HTTP缓存。
正向代理是指浏览器主动请求代理服务器,代理服务器转发请求到对应的目标服务器。
反向代理则部署在Web服务器上,代理所有外部网络对内部网络的访问。浏览器访问服务器,必须经过这个代理,是被动的。
正向代理的主动方是客户端,反向代理的主动方是Web服务器
-
Gunicorn服务器
:依赖Nginx的代理行为,同Nginx
进行功能上的分离。由于不需要直接处理用户来的请求(都被Nginx先处理),Gunicorn不需要完成相关的功能,其内部逻辑非常简单:接受从Nginx来的动态请求,处理完之后返回给Nginx,由后者返回给用户。 -
uWSGI服务器
: uWSGI既不使用wsgi协议也不用FastCGI协议,而是自创了一个uwsgi的协议,uwsgi协议是一个uWSGI服务器自有的协议,它用于定义传输信息的类型,每一个uwsgi packet前4byte为传输信息类型描述,它与WSGI相比是两样东西。uWSGI 服务器自己实现了基于uwsgi协议的server部分,因此我们只需要在uwsgi的配置文件中指定application的地址,uWSGI 就能直接和应用框架中的WSGI application通信。
-
bjoern服务器
:是一个用C语言编写的,快速超轻量级的 Python WSGI服务器。它是最快速的,最小的并且是最轻量级的WSGI服务器。
如果单纯追求性能,那uWSGI会更好一点,而Gunicorn则会更易安装和结合gevent。
在Python的Web开发中,较为成熟稳定的服务器架构一般是Nginx + uWSGI + Django。
而实际上Nginx服务器并不是必须的,直接使用uWSGI + Djang完全是可以的,
但这样一来,直接将uWSGI服务器暴露给了浏览器客户端,由此会导致诸多隐患。
2、Web应用程序
-
常见的Python Web应用框架
Django:全能型Web框架 Flask:一个使用Python编写的轻量级Web框架 web.py:一个小巧的Web框架 Bottle:和Flask类似的Web框架 Tornado:Facebook的开源异步Web框架
四、Web中的HTTP状态码
-
2xx:成功
200 正常,请求已完成。 201 正常,紧接POST命令。 202 正常,已接受用于处理,但处理尚未完成。 203 正常,部分信息—返回的信息只是一部分。 204 正常,无响应—已接收请求,但不存在要回送的信息。
-
3xx:重定向
301 已移动,请求的数据具有新的位置且更改是永久的。 302 已找到,请求的数据临时具有不同 URI。 303 请参阅其它,可在另一 URI 下找到对请求的响应,且应使用 GET 方法检索此响应。 304 未修改,未按预期修改文档。 305 使用代理,必须通过位置字段中提供的代理来访问请求的资源。 306 未使用,不再使用,保留此代码以便将来使用。
-
4xx:客户机中出现的错误
400 错误请求,请求中有语法问题,或不能满足请求。 401 未授权,未授权客户机访问数据。 402 需要付款,表示计费系统已有效。 403 禁止,即使有授权也不需要访问。 404 找不到,服务器找不到给定的资源;文档不存在。 407 代理认证请求,客户机首先必须使用代理认证自身。 415 介质类型不受支持,服务器拒绝服务请求,因为不支持请求实体的格式。
-
5xx:服务器中出现的错误
500 内部错误,因为意外情况,服务器不能完成请求。 501 未执行,服务器不支持请求的工具。 502 错误网关,服务器接收到来自上游服务器的无效响应。 503 无法获得服务,由于临时过载或维护,服务器无法处理请求。