Py学习  »  tornado

在Jupyter笔记本中运行Tornado服务器

Daniel • 4 年前 • 1535 次点击  

采用标准的Tornado演示并将IOLoop推入后台线程,可以在单个脚本中查询服务器。当Tornado服务器是一个交互式对象时(请参阅DASK或类似对象),这很有用。

import asyncio
import requests
import tornado.ioloop
import tornado.web

from concurrent.futures import ThreadPoolExecutor

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])

pool = ThreadPoolExecutor(max_workers=2)
loop = tornado.ioloop.IOLoop()

app = make_app()
app.listen(8888)
fut = pool.submit(loop.start)

print(requests.get("https://localhost:8888"))

在标准的python脚本中,上面的代码工作得很好(尽管它缺少安全关闭)。Jupyter笔记本是这些交互式Tornado服务器环境的最佳环境。然而,当涉及到Jupyter时,这个想法就崩溃了,因为已经有一个活动的运行循环:

>>> import asyncio
>>> asyncio.get_event_loop()
<_UnixSelectorEventLoop running=True closed=False debug=False>

当在jupyter笔记本中运行上述脚本时,服务器和请求客户机都试图在同一线程中打开连接,代码挂起。构建一个新的Asyncio循环和/或Tornado IOLoop似乎没有帮助,我怀疑我在Jupyter本身中缺少了一些东西。

问题是:是否可以在Jupyter笔记本的后台运行一个活动的Tornado服务器,以便标准的python requests 或者类似的可以从主线程连接到它?如果可能的话,我希望避免在向用户提供的代码中使用Asyncio,因为它对于新手用户来说比较复杂。

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/38205
 
1535 次点击  
文章 [ 3 ]  |  最新文章 4 年前
mdurant
Reply   •   1 楼
mdurant    5 年前

基于我的 recent PR to streamz ,这里是一些有用的东西,类似于您的想法:

class InNotebookServer(object):
    def __init__(self, port):
        self.port = port
        self.loop = get_ioloop()
        self.start()

    def _start_server(self):
        from tornado.web import Application, RequestHandler
        from tornado.httpserver import HTTPServer
        from tornado import gen

        class Handler(RequestHandler):
            source = self

            @gen.coroutine
            def get(self):
                self.write('Hello World')

        application = Application([
            ('/', Handler),
        ])
        self.server = HTTPServer(application)
        self.server.listen(self.port)

    def start(self):
        """Start HTTP server and listen"""
        self.loop.add_callback(self._start_server)


_io_loops = []

def get_ioloop():
    from tornado.ioloop import IOLoop
    import threading
    if not _io_loops:
        loop = IOLoop()
        thread = threading.Thread(target=loop.start)
        thread.daemon = True
        thread.start()
        _io_loops.append(loop)
    return _io_loops[0]

在笔记本里打电话

In [2]: server = InNotebookServer(9005)
In [3]: import requests
        requests.get('http://localhost:9005')
Out[3]: <Response [200]>
xyres
Reply   •   2 楼
xyres    5 年前

您可以使用 %%script --bg 魔法命令。选择权 --bg 告诉Jupyter在后台运行当前单元格的代码。

只需在一个单元中使用magic命令创建一个Tornado服务器并运行该单元。

例子:

%%script python --bg

import tornado.ioloop
import tornado.web

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")

def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
    ])

loop = tornado.ioloop.IOLoop.current()

app = make_app()
app.listen(8000) # 8888 was being used by jupyter in my case

loop.start()

然后你可以用 requests 在连接到服务器的单独单元中:

import requests

print(requests.get("http://localhost:8000"))

# prints <Response [200]>

这里需要注意的一点是,如果您停止/中断任何单元格上的内核,后台脚本也将停止。所以您必须再次运行这个单元才能启动服务器。

Matt
Reply   •   3 楼
Matt    5 年前

第1部分:让我们得到嵌套龙卷风

要找到所需的信息,您必须遵循以下crumbtrails,首先查看的发行说明中描述的内容 IPython 7 它将特别指出有关异步和等待部分的更多信息。 documentation ,到 this discussion , 建议使用 nest_asyncio .

关键在于:

  • a)要么欺骗python运行两个嵌套的事件循环。(Nest-Asyncio做什么)
  • b)您在已经存在的EventLoop上调度协程。(我不知道如何处理龙卷风)

我很确定你知道这些,但我相信其他读者会感激的。

不幸的是,除非您像在jupyterhub上那样控制部署,并且可以将这些行添加到自动加载的ipython初创公司脚本中,否则无法使其对用户完全透明。但我认为以下内容足够简单。

import nest_asyncio
nest_asyncio.apply()


# rest of your tornado setup and start code.

第2部分:Gotcha同步代码块EventLoop。

上一节只关注能够运行Tornado应用程序。但请注意 任何同步代码都将阻塞事件循环 ;因此在运行时 print(requests.get("http://localhost:8000")) 当您阻止EventLoop时,服务器似乎不工作,只有当等待EventLoop重新启动的代码完成执行时,它才会重新启动…(理解这是留给读者的练习)。你要么发行 print(requests.get(“http://localhost:8000”)) 从另一个内核 或者,使用aiohtp。

下面介绍如何以与请求类似的方式使用aiohtp。

import aiohttp
session =  aiohttp.ClientSession()
await session.get('http://localhost:8889')

在这种情况下,由于aiohtp是非阻塞的,所以事情看起来会正常工作。在这里,您可以看到一些额外的ipython魔力,我们可以自动检测异步代码并在当前的eventlocop上运行它。

一个很酷的练习可以是 request.get 在另一个内核的循环中,并运行 sleep(5) 在运行Tornado的内核中,看到我们停止处理请求…

第3部分:免责声明和其他途径:

这是 相当棘手 我建议不要在生产中使用,并警告您的用户这不是推荐的操作方法。

这并不能完全解决您的问题,您需要运行一些不在主线程中的东西,我不确定这是否可能。

你也可以试着和其他的循环跑者一起玩 trio curio ;它们可能允许您做默认情况下不能用Asyncio做的事情,比如嵌套,但是 here be dragoons . 我强烈推荐三重奏和 multiple blog posts around its creation 尤其是当你在教异步的时候。

享受,希望这有帮助,并请报告错误,以及确实有效的事情。