主要工作:
服务器启动的时候做的事:
1、把包含了各种配置信息的 application 对象封装到了 HttpServer 对象的 request_callback 字段中,等待被调用
2、TCPServer 通过 listen 方法启动端口监听, 封装_handle_connection回调函数,并注册到 IOLoop 中
 
服务器运行时做的事:
3、当有请求到来时,注册在 IOLoop 中的 _handle_connection 将会被调用, _handle_connection 方法将会调用handle_stream 方法。
4、handle_stream 方法是由 HTTPServer 重写TCPServer的方法,它将会创建 HTTP1ServerConnection对象和_ServerRequestAdapter 对象(这里只是创建好,并没有执行),并调用 HTTP1ServerConnection 对象的start_serving 方法
5、start_serving 方法创建创建HTTP1Connection 对象,并在方法 _server_request_loop 中异步yield conn.read_response(request_delegate) 接受请求发过来的数据, 这里传入的delegate就是在HTTPServer 中创建的_ServerRequestAdapter对象。
4、在异步接收的时候,_ServerRequestAdapter 负责将数据封装成 HTTPRequest 对象, 接收完毕之后,调用_ServerRequestAdapter的 finish 方法
5、在调用_ServerRequestAdapter 的 finish 方法时,数据就会调用 application 对象的 __call__ 方法, 这时就回到了 Application 类了(这里不解释,直接看Application 的 __call__ 方法)
 
 
1、TCPServer 作为工厂类,自身只做统一的端口绑定、监听、回调函数绑定的操作
2、HTTPServer 作为子类,实现数据接收类的创建,接受数据,最后封装成 HTTPRequest 对象,交给Application 对象
3、在整个接收数据的过程中,并不能分辨出 url 是什么,该匹配哪个 handler, 这件事是由 Application 对象来处理的
 
源码分析,省略部分源码,只粘贴出主要部分
 class HTTPServer(TCPServer, httputil.HTTPServerConnectionDelegate):
#继承自TCPServer 和 httputil.HTTPServerConnectionDelegate def __init__(self, request_callback, no_keep_alive=False, io_loop=None,
xheaders=False, ssl_options=None, protocol=None,
decompress_request=False,
chunk_size=None, max_header_size=None,
idle_connection_timeout=None, body_timeout=None,
max_body_size=None, max_buffer_size=None):
self.request_callback = request_callback #获取 application对象
self.no_keep_alive = no_keep_alive
self.xheaders = xheaders
self.protocol = protocol
self.conn_params = HTTP1ConnectionParameters( #获取基本的http 参数
decompress=decompress_request,
chunk_size=chunk_size,
max_header_size=max_header_size,
header_timeout=idle_connection_timeout or 3600,
max_body_size=max_body_size,
body_timeout=body_timeout)
TCPServer.__init__(self, io_loop=io_loop, ssl_options=ssl_options, 传递HTTPServer 给TCPServer对象,初始化
max_buffer_size=max_buffer_size,
read_chunk_size=chunk_size)
self._connections = set() def handle_stream(self, stream, address):
#请求到来时,真正的数据处理方法
context = _HTTPRequestContext(stream, address,
self.protocol)
conn = HTTP1ServerConnection( #创建HTTP1ServerConnection对象,用来获取链接
stream, self.conn_params, context)
self._connections.add(conn)
conn.start_serving(self) #启动获取数据服务, 传递自己为代理 def start_request(self, server_conn, request_conn): #将会在handle_stream添加进去的代理中执行
return _ServerRequestAdapter(self, request_conn) #创建_ServerRequestAdapter对象, 开始接受封装数据

HTTPServer

 class _ServerRequestAdapter(httputil.HTTPMessageDelegate):
"""负责数据流整理,将数据最后封装成 HTTPRequest对象
"""
def __init__(self, server, connection):
self.server = server
self.connection = connection
self.request = None
if isinstance(server.request_callback, httputil.HTTPServerConnectionDelegate):
self.delegate = server.request_callback.start_request(connection)
self._chunks = None
else:
self.delegate = None
self._chunks = [] def headers_received(self, start_line, headers): #接受请求头部数据
if self.server.xheaders:
self.connection.context._apply_xheaders(headers)
if self.delegate is None:
self.request = httputil.HTTPServerRequest( #创建HTTPServerRequest对象
connection=self.connection, start_line=start_line,
headers=headers)
else:
return self.delegate.headers_received(start_line, headers) def data_received(self, chunk): #不停的接受数据,放到_chunks里面
if self.delegate is None:
self._chunks.append(chunk)
else:
return self.delegate.data_received(chunk) def finish(self): #request_callback本身就是一个application对象,在加上括号,那就是执行这个对象,也是就是调用application 的 __call__方法,并传入了封装好的request对象
if self.delegate is None:
self.request.body = b''.join(self._chunks)
self.request._parse_body()
self.server.request_callback(self.request)
else:
self.delegate.finish()
self._cleanup()

_ServerRequestAdapter

 TCPServer 启动一个 Socket 服务端口监听有三中方式
1. `listen`: 单进程启动一个监听
server = TCPServer()
server.listen(8888)
IOLoop.instance().start() 2. `bind`/`start`: 多进程启动一个监听,取决于start(1)的进程数
server = TCPServer()
server.bind(8888)
server.start(0) # Forks multiple sub-processes
IOLoop.instance().start() 3. `add_sockets`: 另外一种方式启动多进程
sockets = bind_sockets(8888)
tornado.process.fork_processes(0)
server = TCPServer()
server.add_sockets(sockets)
IOLoop.instance().start() class TCPServer(object): def listen(self, port, address=""): #单进程启动
sockets = bind_sockets(port, address=address) #绑定 socket 监听端口
self.add_sockets(sockets) #添加监听 def add_sockets(self, sockets):
if self.io_loop is None:
self.io_loop = IOLoop.current() #获取 IOLoop 单例
for sock in sockets:
self._sockets[sock.fileno()] = sock
#将封装好的 self._handle_connection 回调函数与sock对象一起绑定到IOLoop 中, 这一步很重要,_handle_connection 是封装之后的回调函数,在 IOLoop 中会回调它
add_accept_handler(sock, self._handle_connection, io_loop=self.io_loop) #add_accept_handler方法封装在netutil 文件中 def _handle_connection(self, connection, address):
#创建IOStream对象,并封装回调函数, 这个方法并不会在这里执行,只是在这里封装好之后,被当做参数传递与IOLoop 绑定 .......
try:
if self.ssl_options is not None: #https 相关
stream = SSLIOStream(connection, io_loop=self.io_loop,
max_buffer_size=self.max_buffer_size,
read_chunk_size=self.read_chunk_size)
else:
stream = IOStream(connection, io_loop=self.io_loop, #创建一个 IOStream, 主要用来读取HTTP 数据流
max_buffer_size=self.max_buffer_size,
read_chunk_size=self.read_chunk_size)
#调用继承至 TCPServer的类的handle_stream方法(也就是HTTPServer 的handle_stream 方法),传入IOStream和address,
self.handle_stream(stream, address)
except Exception:
app_log.error("Error in connection callback", exc_info=True) #*******下面两个方法是用于多进程的方式启动 TCPServer 可以不看********
def bind(self, port, address=None, family=socket.AF_UNSPEC, backlog=128):
#绑定socket
sockets = bind_sockets(port, address=address, family=family,
backlog=backlog)
if self._started:
self.add_sockets(sockets)
else:
self._pending_sockets.extend(sockets) def start(self, num_processes=1):
#多进程方式启动TCPServer,也就是一个 tornado 运行时,启动多个进程,同时监听一个端口,一般不推荐这么使用,而是采用 Supervisor 启动多个tornado进程,分别监听不同的端口
assert not self._started
self._started = True
if num_processes != 1:
process.fork_processes(num_processes) #fork 子进程
sockets = self._pending_sockets
self._pending_sockets = []
self.add_sockets(sockets)

TCPServer

 

Tornado 高并发源码分析之四--- HTTPServer 与 TCPServer 对象的更多相关文章

  1. Tornado 高并发源码分析之三--- Application 对象

    Application 对象主要工作: 服务器启动时: 1.在新建一个app的时候,根据设置好的 URL 和回调函数 Handler 封装成URLSpec 对象   服务器运行时: 2.在请求到来,将 ...

  2. Tornado 高并发源码分析之五--- IOLoop 对象

    IOLoop主要工作 1.将TCPServer 注册到 IOLoop 的事件记到 _handlers 字段,同时注册 READ 和 ERROR 事件到 epoll 2.IOLoop 启动一个大循环,负 ...

  3. Tornado 高并发源码分析之二---Tornado启动和请求处理流程

    Tornado 服务器启动流程 因为Tornado 里使用了很多传类的方式,也就是delegate,之所以要这么做,其实和 iOS 开发那样,也很多的 delegate, 如此来实现高度解耦,但是比较 ...

  4. Tornado 高并发源码分析之一---启动一个web服务

    前言: 启动一个tornado 服务器基本代码 class HomeHandler(tornado.web.RequestHandler): #创建 RequesHandler 对象,处理接收到的 h ...

  5. Tornado 高并发源码分析之六---异步编程的几种实现方式

    方式一:通过线程池或者进程池 导入库futures是python3自带的库,如果是python2,需要pip安装future这个库 备注:进程池和线程池写法相同 from concurrent.fut ...

  6. ViewGroup事件分发源码分析

    1.AndroidStudio源码调试方式 AndroidStudio默认是支持一部分源码调试的,但是build.gradle(app) 中的sdk版本要保持一致, 最好是编译版本.运行版本以及手机的 ...

  7. libevent源码分析:http-server例子

    http-server例子是libevent提供的一个简单web服务器,实现了对静态网页的处理功能. /* * gcc -g -o http-server http-server.c -levent ...

  8. zookeeper源码分析之四服务端(单机)处理请求流程

    上文: zookeeper源码分析之一服务端启动过程 中,我们介绍了zookeeper服务器的启动过程,其中单机是ZookeeperServer启动,集群使用QuorumPeer启动,那么这次我们分析 ...

  9. 深入理解Spring系列之十:DispatcherServlet请求分发源码分析

    转载 https://mp.weixin.qq.com/s/-kEjAeQFBYIGb0zRpST4UQ DispatcherServlet是SpringMVC的核心分发器,它实现了请求分发,是处理请 ...

随机推荐

  1. SqlServer表死锁的解决方法

    SqlServer表死锁的解决方法   前些天写一个存储过程,存储过程中使用了事务,后来我把一些代码注释掉来进行调试找错,突然发现一张表被锁住了,原来是创建事务的代码忘记注释掉.本文表锁住了的解决方法 ...

  2. About toupper()

    // toupper.c #include <stdio.h> #include <string.h> #include <ctype.h> int main() ...

  3. HAWQ取代传统数仓实践(八)——维度表技术之角色扮演维度

    单个物理维度可以被事实表多次引用,每个引用连接逻辑上存在差异的角色维度.例如,事实表可以有多个日期,每个日期通过外键引用不同的日期维度,原则上每个外键表示不同的日期维度视图,这样引用具有不同的含义.这 ...

  4. HTML5编写规范

    HTML和CSS编码规范内容 一.HTML规范 二.CSS规范 三.注意事项: 四.常用的命名规则 五.CSS样式表文件命名 六.文件命名规则 一.HTML规范: 1.代码规范 页面的第一行添加标准模 ...

  5. MS SQL Server2000转换成MySQL

    按计划今天着手进行将后台数据库从MS SQL Server2000转换成MySQL5.1.3.目的是便于发布软件的测试版本. 1. 驱动: mysql-connector-odbc-5.1.11-wi ...

  6. bzoj 2734 集合选数

    Written with StackEdit. Description <集合论与图论>这门课程有一道作业题,要求同学们求出\(\{1, 2, 3, 4, 5\}\)的所有满足以 下条件的 ...

  7. vc++ windows获取计算机信息

    在软件开发中,我们经常要获当前系统的版本号,判断当前是什么系统,获取获取物理内存和可用内存大小,获取CPU名称.内核数目.主频,获取MAC地址,获取屏幕分辨率,下面的这个c++类将包含所有这些信息. ...

  8. iOS当前屏幕截屏

    需求描述: 有两个ViewController 我们记做 A.B ,其中B controller只是显示下半部分: 如下图效果: 实现这种的方案很多,可以用添加View方法,  也可以用UIWindo ...

  9. loj #6136. 「2017 山东三轮集训 Day4」Left

    题目: 题解: 我们可以发现所有的交换器都是一个位置连接着下一层左侧的排序网络,另一个位置连着另一侧的排序网络. 而下一层是由两个更低阶的排序网络构成的. 两个网络互不干扰.所以我们可以通过第一行和最 ...

  10. 阿里云服务器tomcat启动慢解决方案

    yum -y install rng-tools( 熵服务)     cp /usr/lib/systemd/system/rngd.service /etc/systemd/system     v ...