作为服务端,创建一个用于处理连接的socket, 然后把这个socket交给linux操作系统的epoll去帮我们监听,一下子有10个用户过来连接了(三次握手建立连接,此时还没有发送请求数据), accept一次就会生成一个相应的conn, 这时把这个coon也交给epoll去帮我们监听,然后接着accept 9次, "生产"9个conn交给epoll去帮我们监听。这10个连接其中如果有3个连接发送了请求数据到服务端,那么假设服务端采用单进程单线程模型,这3个请求来了,服务端使用循环拿到请求数据并一个个去做相应的处理。那么这里问题来了,如果在处理一个请求的过程中发生阻塞(比如第一个请求的处理函数中对数据库做查询操作且查询很慢或者在处理函数中往别地发了一个网络请求), 势必会影响到另外的2个请求的处理。这时候就需要我们在处理函数中编写这类阻塞代码使用正确的姿势了
处理单个请求的过程应该是什么样的呢?拿到http请求报文, 并根据http协议进行解析封装成一个HTTPRequest对象, 然后我们根据请求去匹配路由,找到对应的handler, 根据请求的method去调用handler不同的方法做数据相应处理, 并通过conn发送数据。有了epoll之后我们可以把数据的发送和conn进行绑定,等conn可以发送的时候再去发送数据。我们发现,有了epoll之后我们就可以把socket对象和要进行的处理回调全部一股脑交给epoll去监听,然后我们在一个事件循环中去获取就绪的描述符和相应的回调函数。
HTTPServer:处理套接字的 listen/bind/accept。IOStream: 处理套接字的 read/write。HTTPConnection: 处理与 HTTP client 建立的连接,解析 HTTP Request 的 header 和 body, 调用用户自定义的处理方法,以及把响应数据写给客户端socket。HTTPRequest: http 请求对象, 包含了请求头、请求体等数据IOLoop: I/O loop,循环取出可用的 fd,并调用对应的事件处理函数。RequestHandler: 处理请求,支持 GET/POST 等操作。
1,applicationApplication.__init__ 最重要工作的是完成了 URL 路由的设置,通过调用 add_handlers 将我们设置的 "(r"/", TestHandler)"
2,ioLoop,IOstream前者是一个事件循环, 通过epoll对不同的socket对象进行监听和调度. IOStream类则是socket对象的封装, 它依靠着IOLoop的事件循环, 实现了对socket读写功能的非阻塞+异步回调.
IOStream本质是一个socket对象, 只不过通过事件循环变为异步的了. 我们调用它的read_until或者write方法时, IOStream并不会马上尝试去读取或写入数据, 而是设置一个回调函数, 然后调用_add_io_state方法在事件循环中添加对可读或可写事件的监控. 然后, 事件循环在监听到事件时, 调用IOStream的_handle_events方法, 该方法根据事件的类型再调用_handle_read和_handle_write去读取或写入数据, 并调用之前设定好的回调, 这样一次读取&写入才算结束.
3,httpserver , httpconnection, httprequestweb服务器:
bind(self, port, address="")listen(self, port, address="")start(self, num_processes=1)stop(self):
io_loop.add_handler(sock, accept_handler, IOLoop.READ)
其实他的作用就是把这个socket加入ioloop的循环队列,当ioloop监听到这个socket有新的连接请求的时候(READ事件)就调用回调函数self._handle_connection去处理.
httpserver模块中有三个类: HTTPServer, HTTPConnection和HTTPRequest, HTTPServer相当于服务端socket的封装, 负责接收客户端的连接. 该连接会交由HTTPConnection去处理, HTTPConnection利用iostream模块读取客户端的请求数据, 然后将请求数据封装成一个HTTPRequest对象, 将这个对象交由最上层的web应用去处理.
HTTPServer是web服务器端的入口, 首先, 我们通过实例化这个对象来指定web服务器所配套的web应用. 然后, 调用它的listen方法, 就会通过ioloop监听指定端口的可读事件, 也就是客户端连接. 当有客户端连接时, HTTPServer会首先实例化一个IOStream对象, 这个对象相当于对客户端socket对象的封装, 然后新建一个HTTPConnection对象去处理这个新连接.
这样, 一个http服务器就完成了, 它的流程像是下面这样:
4,requestHandlerclass RequestHandler(object):
"""Subclass this class and define get() or post() to make a handler.
If you want to support more methods than the standard GET/HEAD/POST, you
should override the class variable SUPPORTED_METHODS in your
RequestHandler class.
"""
SUPPORTED_METHODS = ("GET", "HEAD", "POST", "DELETE", "PUT")
RequestHandler对响应进行了封装. Application调用它的_execute方法, 就会根据请求类型反射到我们所重写的方法, 比如get方法. 在执行完我们定义的方法之后, 调用自己的finish方法来生成响应消息, 并通过request将响应消息返回.
该类对get/post/等请求做最后的封装,开发人员可继承实现自己的业务逻辑。
_execute 是 RequestHandler 被调用的入口主要代码逻辑:
result = self.prepare() if result is not None: result = yield result if self._prepared_future is not None: # Tell the Application we've finished with prepare() # and are ready for the body to arrive. future_set_result_unless_cancelled(self._prepared_future, None) if self._finished: return if _has_stream_request_body(self.__class__): # In streaming mode request.body is a Future that signals # the body has been completely received. The Future has no # result; the data has been passed to self.data_received # instead. try: yield self.request.body except iostream.StreamClosedError: return method = getattr(self, self.request.method.lower()) result = method(*self.path_args, **self.path_kwargs) if result is not None: result = yield result if self._auto_finish and not self._finished: self.finish()
所谓的 Non-blocking 和 Asynchronous Request 目前看来还是形式大于实际。它并不会真的使用类似 ThreadPool 之类的机制去异步执行 get / post,
而是需要我们的代码本身就支持异步调用。也就是说在 single-threading 模式下,如果 get 里面的代码不支持异步调用,
参考:
1,Tornado: 1. 流程分析_小蚂蚁的专栏-CSDN博客
2,tornado流程第二次分析 - 简书
3,tornado服务器工作原理 - 终末之冬 - 博客园
4,Tornado HTTP - 简书



