Tornado iostream Analysis

Source: Internet
Author: User
Tags epoll socket error

Iostream in tornado encapsulates non-blocking Io read/write operations on the socket. I personally think it is interesting to use the read_util () interface: Set a flag string and callback function, the rest of the work can be omitted. When iostream reads the flag string, the callback function is automatically called. The entire interface is user-friendly and convenient.

Attribute:

Self. sockt: encapsulated socket, nonblocking mode;

Self. _ read_buffer: Read cache, collections. deque type, similar to self. _ write_buffer

Self. io_loop: event-driven model, because you need to add/modify the read/write event of F listener D.

Self. _ state: the event-driven model listens to the socket event (read/write/error)

Self. _ read_callback: the callback function to be executed when reading specified bytes of data or specifying a flag string

Self. _ write_callback: the callback function to be executed when _ write_buffer data is sent

Self. _ connect_callback: At this time, self. _ socket (nonblocking) is a client and is sending a request to the server. If the connection is established successfully, the callback function to be executed

Self. _ connecting: At this time, self. _ socket (nonblocking) is a client and is waiting for connection establishment.

External interface:

1. constructor: Initialize the attributes of the iostream instance, and add the socket error event to the ioloop (epoll) listener. The corresponding handler is self. _ handle_events.

def __init__(self, socket, io_loop=None, max_buffer_size=104857600, read_chunk_size=4096):    ....    self.io_loop.add_handler(                self.socket.fileno(), self._handle_events, self._state)

2. Connect function: iostream is used on the client. Note that the connection is established because the socket is nonblocking.

def connect(self, address, callback=None):        self._connecting = True        try:            self.socket.connect(address)        except socket.error, e:            if e.args[0] not in (errno.EINPROGRESS, errno.EWOULDBLOCK):                raise        self._connect_callback = stack_context.wrap(callback)        self._add_io_state(self.io_loop.WRITE)

3. read_util function: it mainly serves to set the flag string and the corresponding callback function. By the way, scan the current buffer for a flag string and try to read new data from the socket. _ Handle_events is analyzed in detail.

Def read_until (self, delimiter, callback): Assert not self. _ read_callback, "already reading" self. _ read_delimiter = delimiter self. _ read_callback = stack_context.wrap (callback) While true: # see if we 've already got the data from a previous read if self. _ read_from_buffer (): return self. _ check_closed () # HP: Continue to read data into _ read_buffer = 0, indicating eagain errno or closed if self. _ read_to_buffer () = 0: break # HP: callback when will it be executed? handle_events # HP: continue to listen to the socket read event and call ioloop. update_hander () # HP: Self. read_handler = self_handle_events self. _ add_io_state (self. io_loop.read)

4. The Write function is similar to read_util. It mainly serves to set the data to be sent and the callback function to be executed after the data is sent.

Def write (self, Data, callback = none): Self. _ check_closed () self. _ write_buffer.append (data) self. _ add_io_state (self. io_loop.write) # HP: starts listening for the socket write event self. _ write_callback = stack_context.wrap (callback)

5. Other Reading Writing closed operations are simple.

The focus of iostream analysis is: Self. socket's read/write processing function self. _ handle_events

Def _ handle_events (self, FD, events ):... try: if events & self. io_loop.read: Self. _ handle_read () if not self. socket: return if events & self. io_loop.write: If self. _ connecting: # HP: establish a connection with the server self. _ handle_connect () # HP: it calls the hook Self set by connect. _ connect_callback self. _ handle_write () # HP: Write the data in _ write_buffer to the socket. If all data is written, run the callback function if not self. socket: return if events & self. io_loop.error: # HP: epoll Error. Close connection self directly. close () return # HP: update the epoll listener state = self. io_loop.error if self. reading (): # self. _ read_callback is not none state | = self. io_loop.read if self. writing (): state | = self. io_loop.write if state! = Self. _ state: Self. _ state = State self. io_loop.update_handler (self. socket. fileno (), self. _ state) logging T: logging. error ("uncaught exception, closing connection. ", exc_info = true) self. close () raise

When the socket is readable, self. _ handle_read () to process read events, first call _ read_to_buffer to read the data prepared by the protocol stack into the _ read_buffer cache, and then call _ read_from_buffer to analyze the data in the cache, check whether the conditions set by read_util/read_bytes are met.

Def _ handle_read (Self): While true: Try: # 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. result = self. _ read_to_buffer () handle T exception: # HP: exception (ewouldblock/eagain is not counted) self. close () return if result = 0: # HP: closed or eagain break else: # HP: Check whether self exists. _ read_delimiter/self. _ read_callback if self. _ read_from_buffer (): Return

Because the socket is ready to read, _ read_from_socket directly calls Recv () to read data cyclically until the eagain/ewouldblock error occurs or the connection is closed.

Def _ read_to_buffer (Self): Try: Chunk = self. _ read_from_socket () # HP: The Recv () socket is called. error, E: # SSL. sslerror is a subclass of socket. error logging. warning ("read error on % d: % s", self. socket. fileno (), e) Self. close () raise if Chunk is none: # HP: eagain errno or peer has closed return 0 self. _ read_buffer.append (chunk) If self. _ read_buffer_size ()> = self. max_buffer_size: logging. error ("reached maximum read buffer size") self. close () Raise ioerror ("reached maximum read buffer size") return Len (chunk)

_ Read_from_buffer analyzes the cached data to see if the conditions set by read_util/read_bytes are met. If the conditions are met, the corresponding callback function is executed.

Def _ read_from_buffer (Self): If self. _ read_bytes :... elif self. _ read_delimiter: # HP: read_util setting # HP: _ read_buffer is too long. 2 ^ 32. During High concurrency, you are not afraid to squeeze out the memory !!! # HP: equivalent to storing all data on _ read_buffer [0] _ merge_prefix (self. _ read_buffer, sys. maxint) loc = self. _ read_buffer [0]. find (self. _ read_delimiter) If Loc! =-1: callback = self. _ read_callback delimiter_len = Len (self. _ read_delimiter) self. _ read_callback = none self. _ read_delimiter = none self. _ run_callback (callback, # HP: _ consume returns the data self that includes delimiter before delimiter. _ consume (loc + delimiter_len) return true return false

The _ merge_prefix (deque, size) function is interesting. It places the first size bytes of data in deque to the first position of deque. However, I personally think that this will frequently allocate and release memory, which affects performance. The advantage is that it is too convenient to manually write a cache processing structure.

In general, iostream uses the deque container and the _ merge_prefix (deque, size) function to complete the cache and reorganization of non-blocking Io read data, combined with the read_util function, it is good to encapsulate non-blocking Io shard data processing in the iostream class, and the upper layer can easily process socket read/write operations.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.