tornado一:一个基本的tornado服务器

一、一个基本的tornado服务器

#coding=utf-8
import tornado.web
"""tornado的基础web框架模块"""
import tornado.ioloop
"""tornado的核心io循环模块,封装了linux的epoll和bsd的kqueue"""

#类比django中的视图,一个业务处理类
class IndexHandler(tornado.web.RequestHandler):
     # 这里重写了get方法,处理get请求的类;不能处理其它请求,如post
    def get(self, *args, **kwargs):
        # get对应http请求的方法
        # self.write给浏览器响应信息
        self.write("this is return text")


if __name__ == '__main__':
    # Application:是tornado web框架的核心应用类,是与服务器对应的接口(里面保存了路由映射表)
    # 用Applications类实例化一个app,然后由app实例调用Application的方法
    app = tornado.web.Application([
        (r'/', IndexHandler)
    ])
    #1. app实例的listen方法:用来创建一个http服务器,并绑定监听端口
    # 注意:此时服务器并没有开启监听,app.listen相当于tcp服务器的bind
    app.listen(8000)
    """IOLoop.current():返回当前线程的IOLoop实例
    IOLoop.start():启动IOLoop实例的I/O循环(这里是网络连接I/O),同时开启监听。
    监听:start创建linux epoll服务器监听套接字,由linux epoll服务器负责此套接字的监听    
    linux服务器:如果此服务器套接字有连接,linux epoll服务器为每个客户端创建一个响应的socket;
    IOLoop实例循环:循环询问linux epoll服务器的监听套接字,是否有此套接字的连接请求;
    app路由:如果IOLoop实例循环得到从linux服务器得到请求信号,交给Application实例app处理,app根据请求地址,由不同的路由调用不同的Handler。
    Handler:Handler异步返回数据给linux为客户端创建的响应套接字,一个客户端请求对应一个响应套接字"""
    tornado.ioloop.IOLoop.current().start()

 

 app.listen自动创建了http服务器,如果要手动创建httpserver呢?
二、手动创建http服务器
#coding=utf-8
import tornado.web
import tornado.ioloop

class IndexHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.write("this is return text")


if __name__ == '__main__':
    app = tornado.web.Application([
        (r'/', IndexHandler)
    ])
    import tornado.httpserver
    # 参数app用于告诉http服务器的路由
    # 创建http服务器,即实例化HTTPServer对象
    httpServer = tornado.httpserver.HTTPServer(app)
    # 绑定端口
    httpServer.listen(8000)
    tornado.ioloop.IOLoop.current().start()

 

 以上,无论是app.listen(port)还是httpServer.listen(port),都是单进程http服务器。
因此,tornado使用listen绑定端口时,默认是启动单进程服务器。

如果开启多进程http服务器呢?不使用listen绑定端口,使用bind绑定端口则可以使用start方法定义多个进程

三、创建简单的多进程http服务器
#coding=utf-8
import tornado.web
import tornado.ioloop

class IndexHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.write("this is return text")


if __name__ == '__main__':
    app = tornado.web.Application([
        (r'/', IndexHandler)
    ])
    import tornado.httpserver
    # 参数app用于告诉http服务器的路由
    # 创建http服务器,即实例化HTTPServer对象
    httpServer = tornado.httpserver.HTTPServer(app)
    # 绑定端口
   httpServer.bind(8000)
  # 指明多进程http服务器的进程数量
    httpServer.start(num_processes=32)

    # num_process没有值时:默认开启1个进程;num_process的值大于1时,为多个子进程;
    # num_process的值为None或小于等于0时,开启服务器硬件cpu个数的子进程
  tornado.ioloop.IOLoop.current().start()
 

四、 创建高级多进程httpserver

方式:绑定socket

add_sockets 接口更加复杂, 但是,当fork发生的时候,它可以与 tornado.process.fork_processes 一起使用来提供更好的灵活性。 如果你想使用其他的方法,而不是 tornado.netutil.bind_sockets , 来创建监听socket, add_sockets 也可以被用在单进程server中。

在 4.0 版更改: 增加了 decompress_request, chunk_size, max_header_size, idle_connection_timeout, body_timeout, max_body_size 参数。支持 HTTPServerConnectionDelegate 实例化为 request_callback 。

在 4.1 版更改: HTTPServerConnectionDelegate.start_request 现在需要传入两个参数来调用 (server_conn, request_conn) (根据文档内容)而不是一个 (request_conn).

在 4.2 版更改: HTTPServer 现在是 tornado.util.Configurable 的一个子类。

#coding=utf-8
import tornado.web
import tornado.ioloop

class IndexHandler(tornado.web.RequestHandler):
    def get(self, *args, **kwargs):
        self.write("this is return text")


if __name__ == '__main__':
    app = tornado.web.Application([
        (r'/', IndexHandler)
    ])
    import tornado.httpserver
  # 1.首先是调用tornado.netutil.bind_sockets来创建一个socket(或一个socket列表)
sockets = tornado.netutil.bind_sockets( port, address=BIND_IP, family=socket.AF_INET)
  # 2.创建多个子进程
   tornado.process.fork_processes(100)
  # 3.创建http_server
http_server
= tornado.httpserver.HTTPServer(app)
  # 4.监听socket
http_server.add_sockets(sockets)  tornado.ioloop.IOLoop.current().start()

上面的tornado多进程的处理流程是先创建 socket, 然后再 fork 子进程, 这样所有的子进程实际都监听 一个(或多个)文件描述符, 也就是都在监听同样的 socket.

当连接过来所有的子进程都会收到可读事件, 这时候所有的子进程都会跳到 accept_handler 回调函数, 尝试建立连接.

一旦其中一个子进程成功的建立了连接, 当其他子进程再尝试建立这个连接的时候就会触发EWOULDBLOCK(或 EAGAIN) 错误. 这时候回调函数判断是这个错误则返回函数不做处理.

当成功建立连接的子进程还在处理这个连接的时候又过来一个连接, 这时候就会有另外一个 子进程接手这个连接.

Tornado 就是通过这样一种机制, 利用多进程提升效率, 由于连接只能由一个子进程成功创建, 同一个请求也就不会被多个子进程处理.

 

 

五、总结:
1).app.listen,只能在单进程模式中使用
2).httpServer.listern,只能在单进程中使用
3).httpServer.bind()+httpServer.start(),多进程模式
4).创建sockets + fork子进程 + httpServer.add_sockets(刚创建的sockets),多进程模式
但是tornado不建议使用第3种和第4种方式一次性启动多进程;而是手动启动多个tornado实例,并且还能绑定不同的端口
因为1.第3种方式创建的多进程,都是子进程,每个子进程都会从父进程中复制一份IOLoop的实例,
如果在创建之进程前修改了IOLoop,会影响所有的子进程;
2.所有的进程都是由一个命令启动的,无法做到在不停止服务的情况下修改代码。如果只想修改一个进程里的代码,只能停止父进程,
则停止了所有子进程,所有子进程都会受到影响。
所有进程共享一个端口,想要分别监控很困难。
因此实际开发中,都是创建单进程服务器;然后手动启动多个服务器进程或者使用superviser手动管理多个http服务进程。

 

posted on 2018-07-22 22:58  myworldworld  阅读(842)  评论(0)    收藏  举报

导航