为什么Celery异步任务比同步任务工作慢?

问题描述 投票:2回答:2

我正在研究一个使用Celery异步运行某些任务的Django应用程序。我尝试使用Apache Bench执行负载测试并检查响应时间。从我可以从结果中得出的结论是,没有芹菜异步任务,响应时间会更快。 我正在使用:

  • Django:2.1.0
  • 芹菜:4.2.1
  • Redis(经纪人):2.10.5
  • django-redis:4.9.0
  • Django settings.py中的Celery配置:

    BROKER_URL = 'redis://127.0.0.1:6379/1'
    CELERY_RESULT_BACKEND = 'django-db' # Using django_celery_results
    CELERY_ACCEPT_CONTENT = ['application/json']
    CELERY_TASK_SERIALIZER = 'json'
    CELERY_RESULT_SERIALIZER = 'json'
    CELERY_TIMEZONE = 'Asia/Kolkata'
    

    以下是我的代码(我的系统暴露的API):

    class CustomerSearch(APIView):
    
        def post(self, request):
            request_dict = {# Request parameters}
            # Async Block
            response = celery_search_customer_task.delay(request_dict)
            response = response.get()
            # Synchronous Block (uncomment following to make synchronous call)
            # api_obj = ApiCall(request=request_dict)
            # response = api_obj.search_customer() # this makes an API call to 
            return Response(response)
    

    task.py中的芹菜任务:

    @app.task(bind=True)
    def celery_search_customer_task(self, req_data={}):
        api_obj = ApiCall(request=req_data)
        response = api_obj.search_customer() # this makes an API call to another system
        return response
    

    Apache Bench命令:

    ab -p req_data.data -T application/x-www-form-urlencoded -l -r -n 10 -c 10 -k -H "Authorization: Token <my_token>" http://<my_host_name>/<api_end_point>/
    

    以下是ab的结果: 没有芹菜Async任务

    Concurrency Level:      10
    Time taken for tests:   1.264 seconds
    Complete requests:      10
    Failed requests:        0
    Keep-Alive requests:    0
    Total transferred:      3960 bytes
    Total body sent:        3200
    HTML transferred:       1760 bytes
    Requests per second:    7.91 [#/sec] (mean)
    Time per request:       1264.011 [ms] (mean)
    Time per request:       126.401 [ms] (mean, across all concurrent requests)
    Transfer rate:          3.06 [Kbytes/sec] received
                            2.47 kb/s sent
                            5.53 kb/s total
    
    Connection Times (ms)
                  min  mean[+/-sd] median   max
    Connect:      259  270  10.7    266     298
    Processing:   875  928  36.9    955     967
    Waiting:      875  926  35.3    950     962
    Total:       1141 1198  43.4   1224    1263
    
    Percentage of the requests served within a certain time (ms)
      50%   1224
      66%   1225
      75%   1231
      80%   1233
      90%   1263
      95%   1263
      98%   1263
      99%   1263
     100%   1263 (longest request)
    

    用芹菜Async Task

    Concurrency Level:      10
    Time taken for tests:   10.776 seconds
    Complete requests:      10
    Failed requests:        0
    Keep-Alive requests:    0
    Total transferred:      3960 bytes
    Total body sent:        3200
    HTML transferred:       1760 bytes
    Requests per second:    0.93 [#/sec] (mean)
    Time per request:       10775.688 [ms] (mean)
    Time per request:       1077.569 [ms] (mean, across all concurrent requests)
    Transfer rate:          0.36 [Kbytes/sec] received
                            0.29 kb/s sent
                            0.65 kb/s total
    
    Connection Times (ms)
                  min  mean[+/-sd] median   max
    Connect:      259  271   9.2    268     284
    Processing:  1132 6128 4091.9   8976   10492
    Waiting:     1132 6127 4091.3   8975   10491
    Total:       1397 6399 4099.3   9244   10775
    
    Percentage of the requests served within a certain time (ms)
      50%   9244
      66%   9252
      75%  10188
      80%  10196
      90%  10775
      95%  10775
      98%  10775
      99%  10775
     100%  10775 (longest request)
    

    是不是芹菜异步任务应该使任务比同步任务更快地工作?我可能在这里失踪的是什么?

    任何帮助,将不胜感激。谢谢。

    django redis django-celery
    2个回答
    2
    投票

    同步运行代码是直接阻塞主线程上的代码,另一方面芹菜就像生产者消费者机制一样工作。 Celery将任务转发到像RabbitMQ或Redis这样的代理消息队列,这会在此处增加额外的处理时间。根据芹菜的运行位置,如果不在本地运行,可以考虑添加网络延迟。如果您正在调用getdelay,则返回一个可用于监视状态的promise,并在准备好后获取结果。所以建筑基本上变成了

    • 卷筒纸
    • 经纪人
    • 工人
    • 结果后端

    考虑到这么多处理芹菜任务比在主线程上运行要慢


    1
    投票

    我认为你的问题中存在多种应该回答的误解。

    是不是芹菜异步任务应该使任务比同步任务更快地工作?

    正如@Yugandhar在他的回答中指出的那样,通过使用像Celery这样的东西,你会为你的处理增加额外的开销。您实际执行以下操作,而不是执行代码的相同进程:

    • 客户端向代理发送消息。
    • 工人拿起消息并执行它。
    • 工人返回对经纪人的回应。
    • 客户端获取响应并进行处理。

    正如您所看到的,显然使用Celery相对于同步执行它会产生额外的开销。因此,“异步任务比同步任务更快”并不一定正确。

    问题是,为什么要使用异步任务?如果它增加了额外的开销并可能减慢执行速度,那么它的好处是什么?好处是您无需等待响应!

    我们以你的ApiCall()为例。假设调用本身需要10秒才能执行。通过同步执行它意味着在阻止呼叫完成之前阻止其他任何操作。例如,如果您有表单提交触发此操作,则表示用户必须等待其浏览器加载10秒才能获得响应。这是一个非常糟糕的用户体验。

    通过在后台异步执行它,调用本身可能需要10.01秒才能执行(由于开销较慢),而不是必须等待那些秒传递,你可以(如果你选择)立即将响应返回给用户并使用户体验更好。

    Awaiting Results vs Callbacks

    您的代码示例的问题是同步和“异步”代码基本上做同样的事情。它们都以阻塞的方式等待结果,你并没有真正获得以异步方式执行它的好处。

    通过使用.get()方法,您可以告诉AsyncResult对象等待结果。这意味着它会阻塞(就像你同步执行它一样),直到Celery工作者返回一个响应。

    task.delay()        # Async, don't await any response.
    task.delay().get()  # Blocks execution until response is returned.
    

    有时这是你想要的,但在其他情况下你不需要等待响应,你可以完成执行HTTP请求,而是使用回调来处理你触发的任务的响应。

    © www.soinside.com 2019 - 2024. All rights reserved.