我正在使用python3 + falcon组合编写一个API。
方法中有很多地方我可以向客户端发送回复,但由于一些执行数据库、I/O 操作等的繁重代码,它必须等到繁重的部分结束。
例如:
class APIHandler:
def on_get(self, req, resp):
response = "Hello"
#Some heavy code
resp.body(response)
我可以在代码的第一行发送"Hello"。我想要的是在后台运行繁重的代码并发送响应,而不管繁重的部分何时完成。
Falcon没有任何内置的异步功能,但他们提到它可以与gevent之类的东西一起使用。我还没有找到任何关于如何将这两者结合起来的文档。
客户端库对异步操作的支持各不相同,因此决定通常归结为特定后端客户端最支持哪种异步方法,以及要使用的 WSGI 服务器。另请参阅下面的一些更常见的选项...
对于不支持异步交互模型的库,无论是本机还是通过某种子类化机制,任务都可以委托给线程池。对于特别长时间运行的任务(即几秒钟或几分钟(,Celery 不是一个糟糕的选择。
对WSGI(和Falcon(应用程序的一些更常见的异步选项的简要调查:
- 扭曲的。支持显式异步样式,可能是最成熟的选项。为了与像Falcon这样的WSGI框架集成,有twisted.web.wsgi和crochet。
- asyncio.借用了 Twisted 的许多想法,但利用 Python 3 语言功能来提供更简洁的界面。从长远来看,这可能是最干净的选择,但需要WSGI接口的演变(另见脉冲星对PEP-3333的扩展作为一种可能的方法(。在撰写本文时,异步生态系统还相对年轻;社区仍在尝试围绕接口、模式和工具的各种方法。
- 事件。支持寻求使异步代码看起来同步的隐式样式。eventlet 执行此操作的一种方法是在标准库中对 I/O 模块进行猴子修补。有些人不喜欢这种方法,因为它掩盖了异步机制,使边缘情况更难调试。
- 哎呀。与事件类似,尽管更现代一些。uWSGI和Gunicorn都支持geventworker类型,这些类型对标准库进行猴子修补。
最后,有可能将Falcon扩展到原生支持twisted.web或asyncio(ala aiohttp(,但我认为还没有人尝试过。
我使用芹菜进行异步相关工作。我不知道 gevent .看看这个 http://celery.readthedocs.org/en/latest/getting-started/introduction.html
我认为这里有两种不同的方法:
- 任务管理器(如芹菜(
- 异步实现(如 gevent(
你用它们中的每一个实现的是不同的。使用 Celery,您可以做的是运行同步计算响应所需的所有代码,然后在后台运行任何其他操作(如保存到日志(。这样,响应应该更快。
使用 gevent,您实现的是在处理程序的不同实例中并行运行。因此,如果您只有一个请求,则不会看到响应时间有任何差异,但是如果您有数千个并发请求,则性能会好得多。这样做的原因是,如果没有 gevent,当您的代码执行 IO 操作时,它会阻止该进程的执行,而使用 gevent,CPU 可以在 IO 操作等待时继续执行其他请求。
设置 gevent 比设置芹菜容易得多。如果您使用的是 gunicorn,则只需安装 gevent 并将工作线程类型更改为 gevent。另一个优点是可以并行化响应中所需的任何操作(例如从数据库中提取响应(。在 Celery 中,不能在响应中使用 Celery 任务的输出。
我的建议是,从使用 gevent 开始,如果出现以下情况,请考虑稍后添加芹菜(并同时拥有它们(:
- 响应中不需要将使用 Celery 处理的任务的输出
- 你有一台不同的机器来执行你的芹菜任务,或者你的服务器的使用有一些高峰和一些空闲时间(如果你的服务器一直处于 100%,你不会从使用 Celery 中获得任何好处(
- 您的芹菜任务将完成的工作量值得使用芹菜的开销
您可以将multiprocessing.Process
与deamon=True
一起使用来运行守护进程并立即向调用方返回响应:
from multiprocessing import Process
class APIHandler:
def on_get(self, req, resp):
heavy_process = Process( # Create a daemonic process
target=my_func,
daemon=True
)
heavy_process.start()
resp.body = "Quick response"
# Define some heavy function
def my_func():
time.sleep(10)
print("Process finished")
您可以通过发送GET
请求来测试它。您将立即收到响应,10 秒后,您将在控制台中看到一条打印的消息。