挺久没写博客了,因为博主开始了今年另一段美好的实习经历,学习加做项目,时间已排满;很感谢今年这两段经历,让我接触了golang和python,学习不同语言,可以跳出之前学习c/c++思维的限制,学习golang和python的优秀特性以及了解在不同的场景,适用不同的语言;而之前学习linux和c/c++,也使我很快就上手golang和python;
我学习的习惯,除了学习如何使用,还喜欢研究源码,学习运行机制,这样用起来才会得心应手或者说,使用这些语言或框架,就和平时吃饭睡觉一样,非常自然;因为最近有接触到bottle和flask web框架,所以想看下这两个的源码,但是这两个框架是基于python自带的http,因此就有了这篇文章;
python http简单例子
python http框架主要有server和handler组成,server主要是用于建立网络模型,例如利用epoll监听socket;handler用于处理各个就绪的socket;先来看下python http简单的使用:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
import sys
from http.server import HTTPServer,SimpleHTTPRequestHandler
ServerClass = HTTPServer
HandlerClass = SimpleHTTPRequestHandler
if__name__ = = '__main__' :
port = int (sys.argv[ 2 ])
server_address = (sys.argv[ 1 ],port)
httpd = ServerClass(server_address,HandlerClass)
sa = httpd.socket.getsockname()
print ( "Serving HTTP on" ,sa[ 0 ], "port" ,sa[ 1 ], "..." )
try :
httpd.serve_forever()
except KeyboardInterrupt:
print ( "\nKeyboard interrupt received, exiting." )
httpd.server_close()
sys.exit( 0 )
|
运行上述例子,可以得到如下:
1
|
python3 myhttp.py 127.0 . 0.1 9999
|
此时如果在当前文件夹新建一个index.html文件,就可以通过 http://127.0.0.1:9999/index.html 访问了index.html页面了。
这个例子的server类用的是HTTPServer,handler类是SimpleHTTPRequestHandler,因此当HTTPServer监听到有request到来时,就把这个request丢给SimpleHTTPRequestHandler类求处理;ok,了解这些之后,我们开始分别分析下server和handler.
http之server
http模块的设计充分利用了面向对象的继承多态,因为之前有看了会tfs文件系统的代码,所以再看python http时,没那么大的压力;先给出server的继承关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
+------------------+
+------------+| tcpserver基类 |
| BaseServer +-------->| 开启事件循环监听 |
+-----+------+ | 处理客户端请求 |
| +------------------+
v +-----------------+
+------------+| httpserver基类 |
| TCPServer +-------->+设置监听socket |
+-----+------+ | 开启监听 |
| +-----------------+
v
+------------+
| HTTPServer |
+------------+
|
继承关系如上图所示,其中BaseServer和TCPServer在文件socketserver.py,HTTPServer在http/server.py;我们先看下来BaseServer;
BaseServer
因为BaseServer是所有server的基类,因此BaseServer尽可能抽象出所有server的共性,例如开启事件监听循环,这就是每个server的共性,因此这也是BaseServer主要做的使;我们来看下BaseServer主要代码部分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
defserve_forever( self , poll_interval = 0.5 ):
self .__is_shut_down.clear()
try :
with_ServerSelector()asselector:
selector.register( self , selectors.EVENT_READ)
whilenotself.__shutdown_request:
ready = selector.select(poll_interval)
ifready:
self ._handle_request_noblock()
self .service_actions()
finally :
self .__shutdown_request = False
self .__is_shut_down. set ()
|
代码中的selector其实就是封装了select,poll,epoll等的io多路复用,然后将服务自身监听的socket注册到io多路复用,开启事件监听,当有客户端连接时,此时会调用self._handle_request_noblock()来处理请求;接下来看下这个处理函数做了啥;
1
2
3
4
5
6
7
8
9
10
11
12
13
|
def_handle_request_noblock( self ):
try :
request, client_address = self .get_request()
exceptOSError:
return
ifself.verify_request(request, client_address):
try :
self .process_request(request, client_address)
except :
self .handle_error(request, client_address)
self .shutdown_request(request)
else :
self .shutdown_request(request)
|
_handle_request_noblock函数是一个内部函数,首先是接收客户端连接请求,底层其实是封装了系统调用accept函数,然后验证请求,最后调用process_request来处理请求;其中get_request是属于子类的方法,因为tcp和udp接收客户端请求是不一样的(tcp有连接,udp无连接)
我们接下来再看下process_request具体做了什么;
1
2
3
4
5
6
7
8
9
|
defprocess_request( self , request, client_address):
self .finish_request(request, client_address)
self .shutdown_request(request)
# -------------------------------------------------
deffinish_request( self , request, client_address):
self .RequestHandlerClass(request, client_address, self )
defshutdown_request( self , request):
self .close_request(request)
|
process_request函数先是调用了finish_request来处理一个连接,处理结束之后,调用shutdown_request函数来关闭这个连接;而finish_request函数内部实例化了一个handler类,并把客户端的socket和地址传了进去,说明,handler类在初始化结束的时候,就完成了请求处理,这个等后续分析handler时再细看;
以上就是BaseServer所做的事,这个BaseServer不能直接使用,因为有些函数还没实现,只是作为tcp/udp的抽象层;总结下:
先是调用serve_forever开启事件监听;
然后当有客户端请求到来时,将请求交给handler处理;
TCPServer
由上述BaseServer抽象出的功能,我们可以知道TCPServer或UDPServer应该完成的功能有,初始化监听套接字,并绑定监听,最后当有客户端请求时,接收这个客户端;我们来看下代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
BaseServer = = >
def__init__( self , server_address, RequestHandlerClass):
"""Constructor. May be extended, do not override."""
self .server_address = server_address
self .RequestHandlerClass = RequestHandlerClass
self .__is_shut_down = threading.Event()
self .__shutdown_request = False
#--------------------------------------------------------------------------------
TCPServer = = >
def__init__( self , server_address, RequestHandlerClass, bind_and_activate = True ):
BaseServer.__init__( self , server_address, RequestHandlerClass)
self .socket = socket.socket( self .address_family,
self .socket_type)
ifbind_and_activate:
try :
self .server_bind()
self .server_activate()
except :
self .server_close()
raise
|
TCPServer初始化时先是调用基类BaseServer的初始化函数,初始化服务器地址,handler类等,然后初始化自身的监听套接字,最后调用server_bind绑定套接字,server_activate监听套接字
1
2
3
4
5
6
7
8
|
defserver_bind( self ):
ifself.allow_reuse_address:
self .socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 )
self .socket.bind( self .server_address)
self .server_address = self .socket.getsockname()
defserver_activate( self ):
self .socket.listen( self .request_queue_size)
|
TCPServer还实现了另一个函数,那就是接收客户端请求,
1
2
|
defget_request( self ):
returnself.socket.accept()
|
之前如果有学过linux编程,那么看这些代码应该会觉得很熟悉,因为函数名和Linux提供的系统调用名一模一样,这里也不多说了;
TCPServer其实已经把基于tcp的服务器主体框架搭起来了,因此HTTPServer在继承TCPServer基础上,只是重载了server_bind函数,设置reuse_address等;
ok,这里分析下上述例子程序的开启过程;
httpd = ServerClass(server_address,HandlerClass)这行代码在初始化HTTPServer时,主要是调用基类TCPServer的初始化方法,初始化了监听的套接字,并绑定和监听;
httpd.serve_forever()这行代码调用的是基类BaseServer的serve_forever方法,开启监听循环,等待客户端的连接;
如果有看过redis或者一些后台组件的源码,对这种并发模型应该很熟悉;ok,分析了server之后,接下来看下handler是如何处理客户端请求的。
http之handler
handler类主要分析tcp层的handler和http应用层的handler,tcp层的handler是不能使用的,因为tcp层只负责传输字节,但是并不知对于接收到的字节要如何解析,如何处理等;因此应用层协议如该要使用TCP协议,必须继承TCP handler,然后实现handle函数即可;例如,http层的handler实现handle函数,解析http协议,处理业务请求以及结果返回给客户端;先来看下tcp层的handler
tcp层handler
tcp层handler主要有BaseRequestHandler和StreamRequestHandler(都在socketserver.py文件),先看下BaseRequestHandler代码,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
classBaseRequestHandler:
def__init__( self , request, client_address, server):
self .request = request
self .client_address = client_address
self .server = server
self .setup()
try :
self .handle()
finally :
self .finish()
defsetup( self ):
pass
defhandle( self ):
pass
deffinish( self ):
pass
|
之前在看server时,知道处理客户端请求就是在handler类的初始化函数中完成;由这个基类初始化函数,我们知道处理请求大概经历三个过程:
- setup对客户端的socket做一些设置;
- handle真正处理请求的函数;
- finish关闭socket读写请求;
这个BaseRequestHandler是handler top level 基类,只是抽象出handler整体框架,并没有实际的处理;我们看下tcp handler,
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
classStreamRequestHandler(BaseRequestHandler):
timeout = None
disable_nagle_algorithm = False
defsetup( self ):
self .connection = self .request
ifself.timeoutisnotNone:
self .connection.settimeout( self .timeout)
ifself.disable_nagle_algorithm:
self .connection.setsockopt(socket.IPPROTO_TCP,
socket.TCP_NODELAY, True )
self .rfile = self .connection.makefile( 'rb' , self .rbufsize)
self .wfile = self .connection.makefile( 'wb' , self .wbufsize)
deffinish( self ):
ifnotself.wfile.closed:
try :
self .wfile.flush()
exceptsocket.error:
pass
self .wfile.close()
self .rfile.close()
|
tcp handler实现了setup和finish函数,setup函数设置超时时间,开启nagle算法以及设置socket读写缓存;finish函数关闭socket读写;
由上述两个tcp层的handler可知,要实现一个基于http的服务器handler,只需要继承StreamRequestHandler类,并实现handle函数即可;因此这也是http层handler主要做的事;
http层handler
由之前tcp层handler的介绍,我们知道http层handler在继承tcp层handler基础上,主要是实现了handle函数处理客户端的请求;还是直接看代码吧;
1
2
3
4
5
6
|
defhandle( self ):
self .close_connection = True
self .handle_one_request()
whilenotself.close_connection:
self .handle_one_request()
|
这就是BaseHTTPRequestHandler的handle函数,在handle函数会调用handle_one_request函数处理一次请求;默认情况下是短链接,因此在执行了一次请求之后,就不会进入while循环在同一个连接上处理下一个请求,但是在handle_one_request函数内部会进行判断,如果请求头中的connection为keep_alive或者http版本大于等于1.1,则可以保持长链接;接下来看下handle_one_request函数是如何处理;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
defhandle_one_request( self ):
try :
self .raw_requestline = self .rfile.readline( 65537 )
iflen( self .raw_requestline) > 65536 :
self .requestline = ''
self .request_version = ''
self .command = ''
self .send_error(HTTPStatus.REQUEST_URI_TOO_LONG)
return
ifnotself.raw_requestline:
self .close_connection = True
return
ifnotself.parse_request():
return
mname = 'do_' + self .command
ifnothasattr( self , mname):
self .send_error(
HTTPStatus.NOT_IMPLEMENTED,
"Unsupported method (%r)" % self .command)
return
method = getattr ( self , mname)
method()
self .wfile.flush()
except socket.timeout as e:
self .log_error( "Request timed out: %r" , e)
self .close_connection = True
return
|
这个handle_one_request执行过程如下:
- 先是调用parse_request解析客户端http请求内容
- 通过"do_"+command构造出请求所对于的函数method
- 调用method函数,处理业务并将response返回给客户端
这个BaseHTTPRequestHandler是http handler基类,因此也是无法直接使用,因为它没有定义请求处理函数,即method函数;好在python为我们提供了一个简单的SimpleHTTPRequestHandler,该类继承了BaseHTTPRequestHandler,并实现了请求函数;我们看下get函数:
1
2
3
4
5
6
7
8
9
10
|
# SimpleHTTPRequestHandler
# ---------------------------------------------
defdo_GET( self ):
"""Serve a GET request."""
f = self .send_head()
iff:
try :
self .copyfile(f, self .wfile)
finally :
f.close()
|
这个get函数先是调用do_GET函数给客户端返回response头部,并返回请求的文件,最后调用copyfile函数将请求文件通过连接返回给客户端;
以上就是http模块最基础的内容,最后,总结下例子程序handler部分:
- server把请求传给SimpleHTTPRequestHandler初始化函数;
- SimpleHTTPRequestHandler在初始化部分,对这个客户端connection进行一些设置;
- 接着调用handle函数处理请求;
- 在handle函数接着调用handle_one_request处理请求;
- 在handle_one_request函数内部,解析请求,找到请求处理函数;
- 我之前的访问属于get访问,因此直接调用do_GET函数将index.html文件返回给客户端;
python http模块到此已经分析结束;不知道大家有没发现,python自带的http模块使用起来不是很方便,因为它是通过请求方法来调用请求函数,这样当同一方法调用次数非常多时,例如get和post方法,会导致这个请求函数异常庞大,代码不好编写,各种情况判断;当然SimpleHTTPRequestHandler只是python提供的一个简单例子而已;
当然,python官方提供了针对http更好用的框架,即wsgi server和wsgi application;接下来文章先分析python自带的wsgiref模块以及bottle,后面再分析flask;