首先看tornado支持的读操作。
read_until_regex(self, regex, callback=None, max_bytes=None):支持正则的读取
read_until(self, delimiter, callback=None, max_bytes=None):支持结束符的读取
read_bytes(self, num_bytes, callback=None, streaming_callback=None, partial=False):支持指定数量的读取
以read_bytes为例,其余的也差不多
def read_bytes(self, num_bytes, callback=None, streaming_callback=None,
partial=False):
"""Asynchronously read a number of bytes.
If a ``streaming_callback`` is given, it will be called with chunks
of data as they become available, and the final result will be empty.
Otherwise, the result is all the data that was read.
If a callback is given, it will be run with the data as an argument;
if not, this method returns a `.Future`.
If ``partial`` is true, the callback is run as soon as we have
any bytes to return (but never more than ``num_bytes``)
.. versionchanged:: 4.0
Added the ``partial`` argument. The callback argument is now
optional and a `.Future` will be returned if it is omitted.
"""
future = self._set_read_callback(callback)
assert isinstance(num_bytes, numbers.Integral)
self._read_bytes = num_bytes
self._read_partial = partial
self._streaming_callback = stack_context.wrap(streaming_callback)
try:
self._try_inline_read()
except:
if future is not None:
future.add_done_callback(lambda f: f.exception())
raise
return future
,它首先调用_set_read_callback方法:
def _set_read_callback(self, callback):
assert self._read_callback is None, "Already reading"
assert self._read_future is None, "Already reading"
if callback is not None:
self._read_callback = stack_context.wrap(callback)
else:
self._read_future = TracebackFuture()
return self._read_future
self._read_callback,self._read_future在__init__方法中初始化为None,这两个属性会在_run_read_callback中实现回调机制。如果指定了callback,那么就更新self._read_callback。如果没有指定callback,就更self._read_future。
然后更新读取属性,
read_bytes会更新self._read_bytes属性。
read_until_regex会更新self._read_regex属性。
read_until会更新_read_delimite属性。
这几个属性默认为None,在每次读取时会调用_read_from_buffer方法,重新初始化为None。
然后调用_try_inline_read()方法,
def _try_inline_read(self):
self._run_streaming_callback()
pos = self._find_read_pos()
if pos is not None:
self._read_from_buffer(pos)
return
self._check_closed()
try:
pos = self._read_to_buffer_loop()
except Exception:
self._maybe_run_close_callback()
raise
if pos is not None:
self._read_from_buffer(pos)
return
# We couldn't satisfy the read inline, so either close the stream
# or listen for new data.
if self.closed():
self._maybe_run_close_callback()
else:
self._add_io_state(ioloop.IOLoop.READ)
首先它调用了_run_sreaming_callback,它当指定了stream_callback会被调用。
它优先从_read_buffer中寻找,然后将socket的可读数据读取到_read_buffer中,再重复上一步。
然后调用了_find_read_pos方法,负责从_read_buffer中,寻找满足读条件的位置。如果找到,返回位置值。如果没有,则返回None。
_read_from_buffer方法是在self._read_buffer有足够的数据,才会调用。它会重置一些属性,并且会调用相应的回调函数。
def _read_from_buffer(self, pos):
self._read_bytes = self._read_delimiter = self._read_regex = None
self._read_partial = False
self._run_read_callback(pos, False)
初始化读取相关的属性,并且调用_run_read_callback。
def _run_read_callback(self, size, streaming):
if streaming:
callback = self._streaming_callback
else:
callback = self._read_callback
self._read_callback = self._streaming_callback = None
if self._read_future is not None:
assert callback is None
future = self._read_future
self._read_future = None
future.set_result(self._consume(size))
if callback is not None:
assert self._read_future is None
self._run_callback(callback, self._consume(size))
else:
# If we scheduled a callback, we will add the error listener
# afterwards. If we didn't, we have to do it now.
self._maybe_add_error_listener()
_run_read_callback会根据_read_future和_read_callback是否为None,决定怎么返回结果,并且会初始化为None。
如果_read_future不为None,就调用future.set_result()返回结果。
如果_read_callback不为None,就调用_run_callback。
_consume()方法就是从_read_buffer中读取数据。
def _run_callback(self, callback, *args):
def wrapper():
self._pending_callbacks -= 1
try:
return callback(*args)
except Exception:
app_log.error("Uncaught exception, closing connection.",
exc_info=True)
# Close the socket on an uncaught exception from a user callback
# (It would eventually get closed when the socket object is
# gc'd, but we don't want to rely on gc happening before we
# run out of file descriptors)
self.close(exc_info=True)
# Re-raise the exception so that IOLoop.handle_callback_exception
# can see it and log the error
raise
finally:
self._maybe_add_error_listener()
# We schedule callbacks to be run on the next IOLoop iteration
# rather than running them directly for several reasons:
# * Prevents unbounded stack growth when a callback calls an
# IOLoop operation that immediately runs another callback
# * Provides a predictable execution context for e.g.
# non-reentrant mutexes
# * Ensures that the try/except in wrapper() is run outside
# of the application's StackContexts
with stack_context.NullContext():
# stack_context was already captured in callback, we don't need to
# capture it again for IOStream's wrapper. This is especially
# important if the callback was pre-wrapped before entry to
# IOStream (as in HTTPConnection._header_callback), as we could
# capture and leak the wrong context here.
self._pending_callbacks += 1
self.io_loop.add_callback(wrapper)
wrapper是一个闭包函数,主要添加callback执行时,出现异常的处理(输出错误log,关闭连接)。
而且使用NullContext,上面的注释也很清楚,就是因为已经在wrapper中有异常的处理,所以不需要stack_context的异常处理。
上面一段是讲了_read_from_buffer的执行流程。回到_try_inline_read的里面,它如果从_read_buffer中没有读到满足条件的数据,就会调用_read_to_buffer_loop。它的作用,就是循环的从socket中读取数据到_read_buffer中,直到socket没有数据,或者已经读到满足条件的数据。
def _read_to_buffer_loop(self):
# This method is called from _handle_read and _try_inline_read.
try:
if self._read_bytes is not None:
target_bytes = self._read_bytes
elif self._read_max_bytes is not None:
target_bytes = self._read_max_bytes
elif self.reading():
# For read_until without max_bytes, or
# read_until_close, read as much as we can before
# scanning for the delimiter.
target_bytes = None
else:
target_bytes = 0
next_find_pos = 0
# Pretend to have a pending callback so that an EOF in
# _read_to_buffer doesn't trigger an immediate close
# callback. At the end of this method we'll either
# establish a real pending callback via
# _read_from_buffer or run the close callback.
#
# We need two try statements here so that
# pending_callbacks is decremented before the `except`
# clause below (which calls `close` and does need to
# trigger the callback)
self._pending_callbacks += 1
while not self.closed():
# Read from the socket until we get EWOULDBLOCK or equivalent.
# SSL sockets do some internal buffering, and if the data is
# sitting in the SSL object's buffer select() and friends
# can't see it; the only way to find out if it's there is to
# try to read it.
if self._read_to_buffer() == 0:
break
self._run_streaming_callback()
# If we've read all the bytes we can use, break out of
# this loop. We can't just call read_from_buffer here
# because of subtle interactions with the
# pending_callback and error_listener mechanisms.
#
# If we've reached target_bytes, we know we're done.
if (target_bytes is not None and
self._read_buffer_size >= target_bytes):
break
# Otherwise, we need to call the more expensive find_read_pos.
# It's inefficient to do this on every read, so instead
# do it on the first read and whenever the read buffer
# size has doubled.
if self._read_buffer_size >= next_find_pos:
pos = self._find_read_pos()
if pos is not None:
return pos
next_find_pos = self._read_buffer_size * 2
return self._find_read_pos()
finally:
self._pending_callbacks -= 1
self._pending_callbacks属性,就是为了延迟连接关闭。等到所有_pendding_callabcks回调完了,才会真正的关闭连接。
如果_read_to_buffer_loop仍然不能读取到满足条件的数据,就会在ioloop上登记此连接的READ事件,回调函数是_handle_event。_handle_event调用_handle_read处理可读事件。如果有足够的数据,就调用_read_from_buffer。如果没有,就继续监听可读事件。