我有一个 Flask 服务器(带有端点和套接字事件)和一个 Discord 机器人,它们都是独立工作的,我想并行运行它们,这样我就可以从 Flask 端点触发机器人的功能。
对于上下文,这是端点的示例:
@app.route("/submit", methods=["POST"])
async def submit():
data = request.json
userid = int(os.getenv("USER_ID"))
message = f"```Title: {data['title']}\nMessage: {data['message']}```"
await send_dm(userid, message)
return data
在自己的包装中
send_dm
看起来像这样
# notice that this is not a method of a function
# nor a decorated function with properties from the discord library
# it just uses an intance of the commands.Bot class
async def send_dm(userid: int, message: str):
user = await bot.fetch_user(userid)
channel = await user.create_dm()
await channel.send(message)
所以要并行运行它们并且能够相互通信我试过:
def main():
executor = ProcessPoolExecutor(2)
loop = asyncio.new_event_loop()
loop.run_in_executor(executor, start_bot)
loop.run_in_executor(executor, start_server)
loop.run_forever()
if __name__ == "__main__":
run()
当提到的端点上的函数执行时,我收到以下错误
AttributeError: '_MissingSentinel' object has no attribute 'is_set' on concurrent tasks
# make them aware of each other
bot.flask_app = app
app.bot = bot
async def main():
task = threading.Thread(target=start_bot)
task.start()
start_server()
if __name__ == "__main__":
asyncio.run(main())
这种方法带来两个问题:
start_bot
我使用 .start()
方法而不是 .run()
因为根据 this example .run()
创建了自己的事件池,这将使其无法被其他进程访问,并且 .start()
是一个异步函数,所以当运行它时我得到错误:RuntimeError: You cannot use AsyncToSync in the same thread as an async event loop - just await the async function directly.
run.()
函数,在执行上述端点时也会出现同样的问题。def main():
executor = ProcessPoolExecutor(2)
loop = asyncio.new_event_loop()
boo = loop.run_in_executor(executor, start_bot)
baa = loop.run_in_executor(executor, start_server)
loop.run_forever()
if __name__ == "__main__":
main()
这次我实际上执行了两个进程,但仍然无法从烧瓶端点调用我想要的函数。
我也试过了
await asyncio.gather([start_server(), start_bot()])
但是和尝试 2 一样的问题,我已经升级了 flask[async] 模块,所以这不再是问题了。
要重现我现在拥有的内容,您可以查看完整的回购协议here只有4个文件或这个样本应该足以重现。
from server import socketio, app
from bot import bot
from dotenv import load_dotenv
import os
import asyncio
import threading
env_path = os.path.dirname(__file__) + "/.env"
load_dotenv(env_path)
def start_server():
socketio.run(app)
def start_bot():
token = os.getenv("BOT_TOKEN")
bot.run(token)
async def main():
# this doesn't achieve what I want and is the main part of the problem
start_server()
start_bot()
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
print("Program exited")
我错过了什么吗?请在评论中指出,我会在编辑中添加它。
这就是
IPC
的发明目的。您可以使用一个 dpy 帮助程序库:https://github.com/MiroslavRosenov/better-ipc
这两个进程彼此无关,因此您应该单独启动它们,而不是将它们合并在一个主进程中。