【问题标题】:Gunicorn + ThreadPoolExecutor: How to handle multiple simultaneous user requests?Gunicorn + ThreadPoolExecutor:如何同时处理多个用户请求?
【发布时间】:2021-12-31 10:39:53
【问题描述】:

我想提供一个可以同时处理多个用户请求的 falcon API。 每个请求都会触发一个长处理任务,因此我使用了来自concurrent.futuresThreadPoolExecutor,如下所示:

import falcon
from concurrent.futures import ThreadPoolExecutor

executor = ThreadPoolExecutor(max_workers=10)

class Resource:

    def on_post(self, req, resp):
        
        def some_long_task():
            # here is the code for the long task
            
        executor.submit(some_long_task)
        
        resp.body = 'OK'
        resp.status = falcon.HTTP_201
        
app = falcon.App()

resource = Resource()

app.add_route('/', resource)

# Serve...

我正在使用gunicorn 提供API,并带有以下参数:gunicorn main:app --timeout 10000

当我连续对 API 执行 2 个请求时,两个长任务都在后台连续触发。 但是,一旦启动的第一个长任务完成,它就会停止执行第二个任务。我怎样才能避免这种情况?

【问题讨论】:

    标签: python-3.x gunicorn concurrent.futures falconframework


    【解决方案1】:

    通过用time.sleep(10) 替换您长期运行的任务,我无法重现您的问题:

    import logging
    import time
    import uuid
    
    import falcon
    from concurrent.futures import ThreadPoolExecutor
    
    logging.basicConfig(
        format='%(asctime)s [%(levelname)s] %(message)s', level=logging.INFO)
    executor = ThreadPoolExecutor(max_workers=10)
    
    
    class Resource:
    
        def on_post(self, req, resp):
    
            def some_long_task():
                # here is the code for the long task
    
                time.sleep(10)
                logging.info(f'[task {taskid}] complete')
    
            taskid = str(uuid.uuid4())
            executor.submit(some_long_task)
            logging.info(f'[task {taskid}] submitted')
    
            resp.media = {'taskid': taskid}
            resp.status = falcon.HTTP_ACCEPTED
    
    
    app = falcon.App()
    resource = Resource()
    app.add_route('/', resource)
    

    正如预期的那样,所有任务都正确运行到完成:

    [2021-11-26 21:45:25 +0100] [8242] [INFO] Starting gunicorn 20.1.0
    [2021-11-26 21:45:25 +0100] [8242] [INFO] Listening at: http://127.0.0.1:8000 (8242)
    [2021-11-26 21:45:25 +0100] [8242] [INFO] Using worker: sync
    [2021-11-26 21:45:25 +0100] [8244] [INFO] Booting worker with pid: 8244
    2021-11-26 21:45:29,565 [INFO] [task 5b45b1f5-15ac-4628-94d8-3e1fd0710d21] submitted
    2021-11-26 21:45:31,133 [INFO] [task 4553e018-cfc6-4809-baa4-f873579a9522] submitted
    2021-11-26 21:45:33,724 [INFO] [task c734d89e-5f75-474c-ad78-59f178eef823] submitted
    2021-11-26 21:45:39,575 [INFO] [task 5b45b1f5-15ac-4628-94d8-3e1fd0710d21] complete
    2021-11-26 21:45:41,142 [INFO] [task 4553e018-cfc6-4809-baa4-f873579a9522] complete
    2021-11-26 21:45:43,735 [INFO] [task c734d89e-5f75-474c-ad78-59f178eef823] complete
    

    问题可能出在你的长任务代码上吗?

    需要注意一些重要的陷阱:

    • Gunicorn 使用预分叉服务器设计的变体。如果您碰巧执行了高级设置,例如启动线程或打开文件句柄,那么当 Gunicorn fork 一个 worker 时,事情可能会中断。另请参阅:Gunicorn: multiple background worker threads。理想情况下,您应该仅在分叉后初始化您的 executor
    • 以这种方式将工作提交给执行程序,而不检查实际结果,可能会掩盖任务中的异常,这可能看起来像是停止执行。也许完成第一个任务会在第二个任务中引发异常?尝试用try... except 包围您的任务,并记录异常。
    • 我自己从来没有遇到过这种情况,但似乎从并行线程导入可能会导致死锁,例如,请参阅ThreadPoolExecutor + Requests == deadlock? 这通常不是问题,但可能是由于并行任务尝试在运行时导入插件,就像请求的情况一样。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-11-17
      • 1970-01-01
      • 2012-06-03
      • 1970-01-01
      • 1970-01-01
      • 2015-03-12
      • 2018-01-19
      • 2021-12-21
      相关资源
      最近更新 更多