Python Web编程概念梳理
主要讲解三个关键字
- 服务器
- WSGI
- 框架
前言
学编程有两种方式,一种是自上而下,一种是自下而上。
自上而下呢就是直接拿一些成熟的框架学习,看完一个简单的教程就能依葫芦画瓢作出一些小项目,比较容易获取成就感。Python Web服务端编程就是这样的,有许许多多成熟的框架,Django,flask,tornado,web.py.......学习一下这些框架的教程就能配合一个前端作出一些小网站。然后随着使用次数的增加,会发现很多的疑惑,去寻找答案,就慢慢的学习了底层的知识,感觉对框架的理解上了一个档次。
自下而上呢就是直接从底层开始学习,拿Python Web开发来说,可以从计算机网络,socket编程等等开始,但这一过程一般来说比较痛苦。
我呢就是自上而下的学习过程。我接触框架之前,掌握的只有python语法,基本的算法和数据结构等一系列学校里的专业课程。(计网当时还没学) 加入一个实验室后,学长就让我们用tornado框架做一个小网站,然后我就按照教程一点一点写,当然是写的很茫然,不知道为什么要引入这个,为什么引入那个,也不知道为什么别人就能给我发请求了。而且框架的那些条条框框根本记不住,每次写都得重新找教程,把一个大概的模板写好,然后开始写业务函数。
写过几个以后,居然觉得怎么这么简单,只要写几个业务函数,功能多就多写几个业务函数(年轻啊)。但是学着学着又觉得不对劲,用了很久就跟刚开始用一样,谈不上提升,因为写来写去都是在写业务函数。(如果有过来人指导我学习方向,一定轻松很多)
服务器
我之前一直不理解为什么需要apache,nginx这些服务器,也不知道他们的作用,因为我觉得python自己就有一些服务器的库,且tornado本身也有个服务器,所以在我之前的概念了,服务器就很模糊甚至不重要。
Python Web包括客户端和服务端,都是通过对系统提供的套接字(socket)编程来实现请求与响应,socket是操作系统提供的接口,但是各种编程语言都对它进行了封装成库,但是没有改变功能,只是是他更易使用。WEB服务器主要就是实现http协议,而http的底层协议是Tcp协议。
一个简单的TCP客户端和服务器
import socket
PORT = 7890
def server():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind('127.0.0.1', PORT)
sock.listen(5)
print('listening at port: ', PORT)
while true:
sc, sockname = sock.accept()
data = sc.recv(4096)
print('received data: ', data)
sc.sendall(b'hello world')
sc.close()
def client():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect('127.0.0.1', PORT)
sock.sendall(b'hi, server')
reply = sock.recv(4096)
print('server said: ', reply)
sock.close()
server通过bind()监听请求,一旦有请求就生成一个套接字与客户端连接,然后双方通过套接字发送信息。(明白大概意思就行,我也不会socket底层编程)
TCP协议是socket已经提供的,web服务器需要做的就是实现http协议以及如何提高服务器的效率,比如单线程,多线程,多进程,异步等等。而Python中有socket库,所以是可以用Python自己实现一个服务器的(只谈可操作性,我不会别人会-_-)。
那用Python写的http服务器与apache这些web服务器有什么区别呢?
apache这些服务器主要是返回静态内容的,我们自己编写的http服务器主要是返回一些动态内容。这里强调一下是主要,因为对于两种服务器来说两种内容都是可以支持的,不过是各有所长。所以都用或者只用一个都是可以的,这取决于不同的Web部署方案。但是apache这种服务器由于只要接受http请求并响应,所以在这方面做了很多优化,使其速度非常快,且更通用。
三种方案
apache这类Web服务器只接受和响应静态请求,将动态请求转发给Python服务器。这样需要运行两个守护进程来监听请求
apache接受全部请求,自己处理静态请求,动态请求调用CGI脚本执行后拿到结果返回。这样只有一个服务器
不使用apache这类服务器,直接使用Python服务器。
当然服务器的功能还有很多,比如缓存,负载,反向代理,安全等等,所以需要考虑很多方面来决定如何选择所要的服务器。
WSGI
WSGI的全称是Web Server Gateway Interface,即Web服务器网关接口。具体的来说,WSGI是一个规范,定义了Web服务器如何与Python应用程序进行交互,使得使用Python写的Web应用程序可以和Web服务器(包含上面两种)对接起来。WSGI一开始是在PEP-0333中定义的,最新版本是在Python的PEP-3333定义的。(不懂吧,那就继续看)
为什么要有WSGI呢?
一般来说后端是不需要编写服务器的,因为有很多现成的解决方案,所以只需要写一些应用程序来供调用。
目前最广泛的部署方案是:
首先,部署一个Web服务器专门用来处理HTTP协议层面相关的事情,比如如何在一个物理机上提供多个不同的Web服务(单IP多域名,单IP多端口等)这种事情。
然后,部署一个用各种语言编写(Java, PHP, Python, Ruby等)的应用程序,这个应用程序会从Web服务器上接收客户端的请求,处理完成后,再返回响应给Web服务器,最后由Web服务器返回给客户端。
由上一节内容可以知道,服务器有很多种,不同的服务器接口不同,那么采用这种方案,Web服务器与应用程序就需要知道如何交互,就出现了很多规范,最早的一个是CGI,然后有Java专用的Servlet规范以及Python专用的WSGI规范。定义规范就是为了定义统一的标准,提升程序的移植性。
在WSGI中定义了两个角色,Web服务器端称为server或者gateway,应用程序端称为application或者framework。server端接受用户请求后根据规范调用application端。这也是大多数框架里运行时都需要一个application的原因。
application对象定义形式
def app(environ, start_response):
pass
environ参数是一个Python字典,存放与客户端相关的信息,start_response是一个可调用对象,接受两个必选参数和一个可选参数。
application一个简单的示例
def app(environ, start_response):
host = environ.get('HTTP_HOST', '127.0.0.1')
path = environ.get('PATH_INFO', '/')
if ':' in host:
host, port = host.split(':', 1)
if '?' in path:
path, query = path.split('?', 1)
headers = [('Content-Type', 'text/plain; charset=utf-8')]
if environ['REQUEST_METHOD'] != 'GET':
start_response('501 Not Implemented', headers)
yield b'501 Not Implemented'
elif host != '127.0.0.1' or path != '/':
start_response('404 Not Found', headers)
yield b'404 Not Found'
else:
start_response('200 OK', headers)
框架
从上面那个app的示例代码可以看出,如果我们不使用Web框架,就需要自己根据environ的信息来作一系列的判断,而且这些判断是繁琐的,却意义不大的。
让框架帮我们做完这些苦力活,帮助我们过滤掉与服务不符的主机名,路径以及方法等没用的请求,我们就可以专心的处理业务函数。框架能帮我们将environ分析为一个对象,直接供我们调用,并帮我们返回除了响应体以外的一些http相关的信息。当然框架还可以实现很多其他东西,比如Django的admin等。
但是有一种框架比较特殊,那就是tornado这类支持协程或绿色线程的异步服务器,所以导致我在学tornado的时候没遇到过WSGI,但是看其他框架却经常出现这些词汇。
至于框架的选择,我给不出太多的意见。主流的就是Django和Flask,但遗憾的是在我什么都不懂的时候,实验室要求我们使用tornado框架。不是说tornado不好,但是tornado这种编程风格确实是新手不友好的,且文档,学习资料与Django比起来少了太多太多,之后我会换一个框架开始学习,Django和Flask中选择一个,因为这些社区环境昊,教程比较多,适合自学。
水平不高,如有错误请指出!谢谢![email protected]