简体   繁体   English

Asyncio 与另一个协程同时运行 Dash (Flask) 服务器

[英]Asyncio run Dash (Flask) server with another coroutine concurrently

I created a dash app to present information that another code is collecting, I want to run them both concurrently using the asyncio module in Python.我创建了一个破折号应用程序来显示另一个代码正在收集的信息,我想使用 Python 中的 asyncio 模块同时运行它们。

My code is using async functions and the Dash app (which is based on Flask) is blocking anything else from executing while serving.我的代码正在使用异步函数,而 Dash 应用程序(基于 Flask)在服务时阻止执行任何其他内容。

I'm not sure if this is something that has to involve opening up more threads.我不确定这是否需要打开更多线程。

Here's my current code which only runs the main coroutine.这是我当前的代码,它只运行主协程。

async def main():
    some code here...

    while True:
        try:
            await client.handle_message()
        except ConnectionClosedError as error:
            logger.error(error)
    
        for strategy in strategies:
            await asyncio.create_task(...)
            some code here...

async def run_dashboard():
    app = create_app()
    app.run_server('0.0.0.0', 5000, debug=False)


if __name__ == '__main__':
    some code here...

    # Currently just runs the main coroutine
    asyncio.run(main())

How can I run main and run_dashboard concurrently?如何同时运行 main 和 run_dashboard?

Here is some code running a dash app (collecting flight data - courtesy Jose Portilla - Udemy) + the threading to run the dash app and some tasks in async.这是一些运行 dash 应用程序的代码(收集飞行数据 - 由 Jose Portilla - Udemy 提供)+ 运行 dash 应用程序的线程和一些异步任务。


from flask import Flask, jsonify
import asyncio
from threading import Thread


# Dash
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import requests
import plotly.graph_objects as go 


# ** Async Part **

async def some_print_task():
    """Some async function"""
    while True:
        await asyncio.sleep(2)
        print("Some Task")


async def another_task():
    """Another async function"""
    while True:
        await asyncio.sleep(3)
        print("Another Task")


async def async_main():
    """Main async function"""
    await asyncio.gather(some_print_task(), another_task())


def async_main_wrapper():
    """Not async Wrapper around async_main to run it as target function of Thread"""
    asyncio.run(async_main())


# *** Dash Part ***:
app = dash.Dash()

app.layout = html.Div([

    # html.Div([
    #     html.Iframe(src="https://www.flightradar24.com",
    #                 height=500,width=200)
    # ]),
    html.Div([
        html.Pre(id='counter-text',children='Active Flights Worldwide'),
        dcc.Graph(id='live-update-graph',style={'width':1200}),
        dcc.Interval(   id='interval-component',
                        interval=6000,
                        n_intervals=0)
    ])
])
counter_list = []

@app.callback(  Output('counter-text','children'),
                [Input('interval-component','n_intervals')])
def update_layout(n):
    url = "https://data-live.flightradar24.com/zones/fcgi/feed.js?faa=1&mlat=1&flarm=1&adsb=1&gnd=1&air=1&vehicles=1&estimated=1&stats=1"
    res = requests.get(url, headers={'User-Agent' : 'Mozilla/5.0'})  # A fake header is necessary to access the site
    data = res.json()
    counter = 0
    for element in data["stats"]["total"]:
        counter += data["stats"]["total"][element]

    counter_list.append(counter)
    return "Active flights Worldwide: {}".format(counter)

@app.callback(  Output('live-update-graph','figure'),
                [Input('interval-component','n_intervals')])
def update_graph(n):
    fig = go.Figure(data=[
            go.Scatter(x=list(range(len(counter_list))),
                        y=counter_list,
                        mode='lines+markers')
    ])

    return fig

if __name__ == '__main__':
    # run all async stuff in another thread
    th = Thread(target=async_main_wrapper)
    th.start()
    # run Flask server
    # app.run(host="0.0.0.0", port=9999)
    app.run_server(debug=True)
    th.join()

Run run_dashboard in a background thread.在后台线程中运行run_dashboard Ref to the document .参考文档

async def run():
    await asyncio.gather(
        asyncio.to_thread(run_dashboard),
        main()
    )

asyncio.run(run())

Note that asyncio.to_thread is a new function in version 3.9.请注意asyncio.to_thread是 3.9 版中的新 function。 For python version older than 3.9, copy the following code threads.py .对于 python 3.9 之前的版本,复制以下代码threads.py

Frankly speaking it is not good design to combine Dash (Flask) with some async work in one process, consider to run Flask and async activities in different processes (ie apps).坦率地说,将 Dash (Flask) 与一些异步工作结合在一个进程中并不是一个好的设计,考虑在不同的进程(即应用程序)中运行 Flask 和异步活动。

Nevertheless, If you still want to run all in one process, I can give you the following working example , please follow comments and ask if you have any questions:尽管如此,如果您仍想在一个进程中运行所有内容,我可以为您提供以下工作示例,请关注评论并询问您是否有任何问题:

from flask import Flask, jsonify
import asyncio
from threading import Thread

# ** Async Part **


async def some_print_task():
    """Some async function"""
    while True:
        await asyncio.sleep(2)
        print("Some Task")


async def another_task():
    """Another async function"""
    while True:
        await asyncio.sleep(3)
        print("Another Task")


async def async_main():
    """Main async function"""
    await asyncio.gather(some_print_task(), another_task())


def async_main_wrapper():
    """Not async Wrapper around async_main to run it as target function of Thread"""
    asyncio.run(async_main())

# *** Flask Part ***:


app = Flask(__name__)


@app.route("/", methods=["GET"])
def index():
    """just some function"""
    return jsonify({"hello": "world"})


if __name__ == '__main__':
    # run all async stuff in another thread
    th = Thread(target=async_main_wrapper)
    th.start()
    # run Flask server
    app.run(host="0.0.0.0", port=9999)
    th.join()

If you really want it to run all in one process, the following worked for me:如果您真的希望它在一个进程中运行,以下对我有用:

from functools import partial
from threading import Thread    


partial_run = partial(app.run, host="0.0.0.0", port=5000, debug=True, use_reloader=False)
t = Thread(target=partial_run)
t.start()
asyncio.run(main())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM