高并发处理框架 Tornado

鳄鱼君

发表文章数:643

Vieu四代商业主题

高扩展、安全、稳定、响应式布局多功能模板。

¥69 现在购买
首页 » Python » 高并发处理框架 Tornado

Tornado是一个可扩展的非阻塞式Web服务器及其相关工具的开源版本。Tornado每秒可以处理上千的连接,对于实时Web服务器来说,Tornado是一个理想的框架。

Tornado介绍

Tornado使用Python编写的一个强大的可扩展Web服务器,在处理高网络流量时表现的足够强健。相比于其它Python网络框架,Tornado的特点很多:

  1. 完备的Web框架:与Django、Falsk一样,Tornado也提供URL路由映射、Request上下文、基于模板的页面渲染技术。
  2. 高效的网络库:提供异步I/O支持、超时事件处理。
  3. 高效的HTTPClient:不仅仅是服务器端框架,Tornado还提供基于异步框架的HTTP客户端。
  4. 高效的内部HTTP服务器:Tornado的HTTP服务器与Tornado异步调用紧密结合,可直接用于生产环境。
  5. 完备的WebSocket支持:WebSocket是HTML5的一种新标准,实现了浏览器与服务器之间的双向实时通信。

Tornado安装

Tronado已经被配置到PyPI网站中,使用pip命令:pip install tornado即可,如果下载慢,使用国内源即可!基于Python3、Tornado6.0.4

在当今的计算机应用开发中,要做的是减少程序在I/O相关操作中的等待,提高并发程度。同步I/O操作导致请求进程阻塞,直到I/O操作完成;异步I/O操作不导致请求进程阻塞。在Python中,同步I/O可以简单理解为,一个被调用的I/O函数会阻塞调用函数的执行,而异步I/O不会阻塞调用函数的执行。

同步I/O

# Tornado的HTTP客户端
from tornado.httpclient import HTTPClient


def synchronization_wait():
    """
    同步I/O操作访问www.e1yu.com,执行速度取决于网络速度和对方服务器响应速度,
    只有对www.e1yu.com访问完成并获取到结果,函数才执行完毕
    :return: response.body
    """
    http_client = HTTPClient()
    # 阻塞,直到www.e1yu.com访问完成
    response = http_client.fetch("https://www.e1yu.com")
    return response.body


print(synchronization_wait())

异步I/O

# Tornado的HTTP客户端
from tornado.httpclient import AsyncHTTPClient


def handle_response(response):
    print(response.body)


def asynchronous_wait():
    http_client = AsyncHTTPClient()
    # 异步访问,http_client.fetch()在调用后立刻返回无需等待实际访问的完成
    # 访问完成,AsyncHTTPClient()会调用callable参数指定的函数
    http_client.fetch("https://www.e1yu.com", callable(handle_response))
    

print(asynchronous_wait())

可迭代与迭代器

协程是Tornado中进行异步I/O开发的方法,它使用了Python的关键字yield,yield将调用者挂起和恢复执行。在学习之前,需要了解一下这些概念。

迭代器Iterator

迭代器是访问集合内元素的一种方式。迭代器对象从集合的第一个元素开始访问,直到所有的元素都被访问一遍后结束。迭代器不能回退,只能向前迭代!
Python中所有的Sequence(序列)类型簇都是可迭代的。使用iter()方法可以将列表、集合转换为迭代器:

num = [1, 2, 3, 4, 5, 6, 7]
iter_tor = iter(num)
# <list_iterator object at 0x000002050858F9C8>
print(iter_tor)

迭代器与普通Python对象的区别:迭代器可以调用next()函数获取一个元素,不断调用next()就能逐个访问集合中所有的元素

num = [1, 2, 3, 4, 5, 6, 7]
iter_tor = iter(num)
# 1
print(iter_tor.__next__()) 
# 2
print(next(iter_tor))

可以一直调用next()函数,直到返会StopIteration异常表示迭代已经完成。其中__next__()和next()函数两者实现效果一样。

定义生成器

迭代器在Python中使用范围很广,一般有两种方式定义迭代器:

  1. 实现一个(可迭代对象)Iterable,使用iter()函数获取迭代器。
  2. yield关键字直接将一个函数转变为迭代器,用这种方式定义的迭代器被称为生成器

任何一个可迭代对象Iterable都可以通过iter()函数生成一个迭代器。定义一个Iterable类一般可以通过为其实现__iter__()和__next()俩个成员方法实现:

class MyIterable():
    def __init__(self):
        self.data = [1, 2, 3, 4]
        self.step = 0

    # 返回一个实现了__next__()的对象
    def __iter__(self):
        return self

    # 返回一个元素,没有元素抛出异常
    def __next__(self):
        if self.step >= len(self.data):
            raise StopIteration
        data = self.data[self.step]
        print('index:{0} call of next()'.format(self.step))
        self.step += 1
        return data


for i in MyIterable():
    print(i)

使用生成器可以简化代码,调用任何定义中包含yield关键字的函数都会不执行该函数,而会获得一个对应该函数的生成器。

def MyIterator():
    # 定义一个迭代器函数
    for k, v in enumerate([1, 2, 3, 4]):
        print('index:{0} call of next()'.format(k))
        # 用yield返回一个元素
        yield v


for i in MyIterator():
    print(i)

异步和协程

异步上面简单介绍过,接下来要说的是协程。Tornado协程可以开发出类似同步代码的异步行为。编写一个简单的协程函数:

# 引入协程库gen
from tornado import gen
from tornado.httpclient import AsyncHTTPClient


# 使用gen.coroutine装饰器,声明一个协程函数
@gen.coroutine
def coroutine_wait():
    http_client = AsyncHTTPClient()
    response = yield http_client.fetch("https://www.e1yu.com")
    print(response.body)

Tornado协程基于Python的yield关键字,所以你不能直接像调用函数呢样调用协程。协程函数可以通过以下三种方式调用:

  1. 在本身是协程的函数内通过yield关键字调用
  2. 在IOLoop没有启动时,通过IOLoop的run_sync()函数调用
  3. 在IOLoop已经启动时,通过IOLoop的spawn_callback()函数调用

1.通过协程函数调用协程函数

# 使用gen.coroutine装饰器,声明一个协程函数
@gen.coroutine
def outer_coroutine():
    print('start to call  a coroutine')
    yield coroutine_wait()
    print('end of outer_coroutine')


outer_coroutine()

2.在IOLoop没有启动时,通过IOLoop的run_sync()函数调用
IOLoop是Tornado的主事件循环对象,Tornado程序通过监听外部客户端的访问请求,并执行相应的操作。当程序没有进入IOLoop的running状态时,可以通过run_sync()函数调用协程函数:

from tornado.ioloop import IOLoop


def func_sync():
    print('start to call  a coroutine')
    IOLoop.current().run_sync(lambda: coroutine_wait())
    print('end of outer_coroutine')


func_sync()

在普通函数中使用run_sync()函数调用,经过lambda封装的协程函数。run_sync()函数将阻塞当前函数的执行,直到被调用的协程执行完成。

Tornado要求协程函数在IOLoop的running状态中才能被调用,只不过run_sync()函数自动完成了启动、停止IOLoop的步骤,它的实现逻辑为:启动IOLoop——调用lambda封装的协程函数——停止IOLoop。

3.在IOLoop已经启动时,通过IOLoop的spawn_callback()函数调用
当Tornado程序处于running状态时的协程函数调用:

from tornado.ioloop import IOLoop


def func_sync():
    print('start to call  a coroutine')
    IOLoop.current().spawn_callback(lambda: coroutine_wait)
    print('end of outer_coroutine')


func_sync()

spawn_callback()函数将不会等待被调用协程执行完成,所以该普通函数运行只会打印两个print的结果,coroutine_wait()本身会由IOLoop在合适的时机调用。

IOLoop的spawn_callback()函数没有提供获取协程函数调用的返回值的方法,所以只能使用spawn_callback()调用没有返回值的协程函数。

协程中调用阻塞函数

协程中调用阻塞函数,会影响协程本身的性能。Tornado提供了在协程中利用线程池调度阻塞函数,不影响协程本身继续执行。

from tornado import gen
from concurrent.futures import ThreadPoolExecutor


# 实例化两个线程的线程池
thread_pool = ThreadPoolExecutor(2)


def my_sleep(count):
    import time
    for i in range(count):
        time.sleep(1)


@gen.coroutine
def call_block():
    print('start to call_block')
    # 调用阻塞函数
    yield thread_pool.submit(my_sleep, 10)
    print('end of call_block')


# 调用没有返回值的协程函数
call_block()

在协程中等待多个异步调用

Tornado允许在协程中使用一个yield关键字等待多个异步调用,只需要把这些调用列表list或字典dictionary的方式传递给yield即可

from tornado.httpclient import AsyncHTTPClient
from tornado import gen

# 列表方式
@gen.coroutine
def coroutine_list():
    http_client = AsyncHTTPClient()
    # 只有在列表中的所有调用都执行完成,yield才会返回并继续执行
    list_response = yield [http_client.fetch('https://www.e1yu.com'),
                           http_client.fetch('https://www.baidu.com'),
                           http_client.fetch('https://www.qq.com')
                           ]
    for response in list_response:
        print(response)
    
        
# 字典方式  
@gen.coroutine
def coroutine_dict():
    http_client = AsyncHTTPClient()
    # 只有在列表中的所有调用都执行完成,yield才会返回并继续执行
    dict_response = yield {"e1yu": http_client.fetch('https://www.e1yu.com'),
                           "baidu": http_client.fetch('https://www.baidu.com'),
                           "qq": http_client.fetch('https://www.qq.com')
                           }
    print(dict_response['e1yu'].body)

未经允许不得转载:作者:鳄鱼君, 转载或复制请以 超链接形式 并注明出处 鳄鱼君
原文地址:《高并发处理框架 Tornado》 发布于2020-07-12

分享到:
赞(0) 赏杯咖啡

评论 抢沙发

7 + 3 =


文章对你有帮助可赏作者一杯咖啡

支付宝扫一扫打赏

微信扫一扫打赏

Vieu4.6主题
专业打造轻量级个人企业风格博客主题!专注于前端开发,全站响应式布局自适应模板。
切换注册

登录

忘记密码 ?

您也可以使用第三方帐号快捷登录

Q Q 登 录
微 博 登 录
切换登录

注册