tornao.iostream-io流

首先看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。如果没有,就继续监听可读事件。

转载于:https://my.oschina.net/u/569730/blog/383858

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值