社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  tornado

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

Daniel • 5 年前 • 2328 次点击  

采用标准的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
 
2328 次点击  
文章 [ 3 ]  |  最新文章 5 年前
mdurant
Reply   •   1 楼
mdurant    6 年前

基于我的 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    6 年前

您可以使用 %%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    6 年前

第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 尤其是当你在教异步的时候。

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