引言:上一章讲了关于HTTPServer的原理,这一次通过分析源码来了解更多的细节
看看HTTPServer类的组织结构:
HTTPServer的主要工作
一.提供了一些基础的比如说listen,bind此类共有操作
二.完成了一个 _handle_events()的公有回调函数,此函数是 IOLoop的基础,此函数为每一个连接创建一个单独的 IOStream 对象
三.start函数,启动HTTPServer,并设置相应的参数(如根据CPU个数来设置进程数等)
从HTTPServer类的构造函数可以看出,最重要的参数是设置回调函数,此回调函数用于处理request对象
每次有HTTP的请求,都会通过HTTPConnection 封装一个HTTPRequest对象,这个对象包含了HTTP请求的所有信息
所以在HTTPServer层,需要对这个对象进行一番处理后调用 request.write将结果返回给客户端
此回调函数会先注册到HTTPServer,然后注册到HTTPConnection 里面,因为request这个对象是由HTTPConnection对象产生
def _handle_events(self, fd, events):
while True:
try:
connection, address = self._socket.accept()
except socket.error, e:
if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN):
return
raise
#SSL 选项
if self.ssl_options is not None:
assert ssl, "Python 2.6+ and OpenSSL required for SSL"
try:
connection = ssl.wrap_socket(connection,
server_side=True,
do_handshake_on_connect=False,
**self.ssl_options)
except ssl.SSLError, err:
if err.args[0] == ssl.SSL_ERROR_EOF:
return connection.close()
else:
raise
except socket.error, err:
if err.args[0] == errno.ECONNABORTED:
return connection.close()
else:
raise
try:
if self.ssl_options is not None:
stream = iostream.SSLIOStream(connection, io_loop=self.io_loop)
else:
#为每一个 connection 创建一个 iostream 实例,以后的IO操作由此实例负责
#IOLoop只负责 accept这个连接
stream = iostream.IOStream(connection, io_loop=self.io_loop)
#将 stream对象和对应的 address , callback加入到HTTPConnection 中
#HTTPConnection稍后会有介绍
#这里的 request_callback 是由Demo里 httpserver.HTTPServer(handle_request) 传递进来
#现代的 HTTP 框架都采用这种模式
#创建一个 handle_request 这个 回调函数嵌套的注册到下层,直到真正处理request
#一般情况是回调继续传递下去直到遇到一个类方法能够传递 request 对象给这个函数
HTTPConnection(stream, address, self.request_callback,
self.no_keep_alive, self.xheaders)
except:
logging.error("Error in connection callback", exc_info=True)
通过调用HTTPConnection,然后传递stream,address和request_callback到HTTPConnection可以看到,处理request的回调函数注册到了HTTPConnection.
还需要注意的地方就是,每一次有一个连接的到来,IOLoop都只负责处理accept此连接,然后后面的IO操作就交给IOStream来处理
在start()函数中,会为每个进程创建一个单独的IOLoop,然后此IOLoop的回调函数统一采用_handle_events()
_handle_events()函数的处理流程总体来说是这样:
1.注册到本进程的IOLoop中
2.当有事件发生,只注册了READ事件,也就是只接受新连接,每次有连接到来,都回调_handle_events()
3.accept此新连接,然后为此新连接创建一个IOStream对象,以后此IOStream负责本连接的所有IO操作,这里是一层抽象,实际
在IOStream的读写事件也是注册到了本进程的IOLoop中,只不过回调函数不一样,因为注册时候的描述符不同。
调用方式是通过handle[fd]()这种方式调用,所以对于监听套接口每次都只会调用_handle_events()而对于其他的IOStream的连接
fd会调用在read_bytes(),read_utils()中注册的回调函数
再看看在HTTPServer中的 start()函数
def start(self, num_processes=1):
assert not self._started
self._started = True
if num_processes is None or num_processes <= 0:
num_processes = _cpu_count()
if num_processes > 1 and ioloop.IOLoop.initialized():
logging.error("Cannot run in multiple processes: IOLoop instance "
"has already been initialized. You cannot call "
"IOLoop.instance() before calling start()")
num_processes = 1
if num_processes > 1:
logging.info("Pre-forking %d server processes", num_processes)
#根据 处理器个数来决定 fork多少个进程
for i in range(num_processes):
if os.fork() == 0:# fork() == 0 表示子进程
import random
from binascii import hexlify
try:
# If available, use the same method as
# random.py
seed = long(hexlify(os.urandom(16)), 16)
except NotImplementedError:
# Include the pid to avoid initializing two
# processes to the same value
seed(int(time.time() * 1000) ^ os.getpid())
random.seed(seed)
#为每个进程创建一个IOLoop实例
self.io_loop = ioloop.IOLoop.instance()
#为每个IOLoop 添加回调函数,这里采用统一回调方式,和IOStream 一样
self.io_loop.add_handler(
self._socket.fileno(), self._handle_events,
ioloop.IOLoop.READ)
return
os.waitpid(-1, 0)#预防僵尸进程,Unix 环境编程介绍很多
else:
if not self.io_loop:
self.io_loop = ioloop.IOLoop.instance()
self.io_loop.add_handler(self._socket.fileno(),
self._handle_events,
ioloop.IOLoop.READ)
可以在代码注释中看到会根据每一个CPU一个IOLoop实例的方式处理,至于中间的产生随机数是为什么,如果有人知道请告知我
在start()函数的最后可以看到add_handle将监听套接口和_handle_events()函数注册到了IOLoop中,这就是上面所讲的HTTPServer处理连接的过程
总结:总结不想写了,土逼Continue...