为什么 Celery 异步任务比同步任务慢?
Why is Celery Async Task working slower than Synchronous task?
我正在开发一个 Django 应用程序,它使用 Celery 运行 一些异步任务。我尝试使用 Apache Bench 执行负载测试并检查响应时间。从结果中我可以看出,没有 celery 异步任务,响应时间更快。
我正在使用:
Django: 2.1.0 celery: 4.2.1 Redis (经纪人): 2.10.5 django-redis: 4.9.0
Django 中的芹菜配置 settings.py:
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)
和tasks.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:
的结果
没有芹菜异步任务
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)
使用celery异步任务
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)
celery 异步任务不是应该使任务比同步任务运行得更快吗?我可能在这里错过了什么?
如有任何帮助,我们将不胜感激。谢谢。
运行 代码同步是在主线程上直接阻塞代码,另一方面 celery 的工作方式类似于 生产者消费者 机制。
Celery 将任务转发到代理消息队列,如 RabbitMQ 或 Redis 这会增加额外的处理时间。根据你的 celery 运行 所在的位置,如果不是 运行 本地,你可以考虑增加网络延迟。如果您正在调用 get
或 delay
那么 returns 一个可用于监视状态并在准备就绪时获取结果的承诺。
所以架构基本上变成了
网络
经纪人
- 工人
- 结果后端
考虑到如此多的处理 celery 任务比主线程上的 运行 慢
我认为你的问题中有很多误解需要回答。
Isn't celery async task supposed to make tasks work faster than synchronous tasks?
正如@Yugandhar 在他的回答中指出的那样,通过使用 Celery 之类的东西,您正在为您的处理增加额外的开销。您实际上不是在执行代码,而是在执行以下操作:
- 客户端向代理发送消息。
- Worker 获取消息并执行。
- 工人 return 对经纪人的回应。
- 客户端获取响应并处理它。
如您所见,与同步执行 Celery 相比,使用 Celery 显然会产生额外的开销。因此,说 "async task is faster than synchronous tasks".
不一定是真的
那么问题来了,为什么要使用异步任务呢?如果它增加了额外的开销并可能减慢执行速度,那么它有什么好处呢?好处是你不需要等待响应!
我们以您的 ApiCall()
为例。假设调用本身需要 10 秒来执行。通过同步执行它意味着您将阻止其他任何事情在调用完成之前完成。例如,如果您有一个触发此事件的表单提交,则意味着用户必须等待浏览器加载 10 秒才能收到响应。这是一个非常糟糕的用户体验。
通过在后台异步执行,调用本身可能需要 10.01 秒才能执行(由于开销较慢),但不必等待这些秒过去,您可以(如果您选择)立即return 将响应返回给用户并使用户体验更好。
等待结果与回调
您的代码示例的问题在于同步和 "asynchronous" 代码基本上做同样的事情。它们都以阻塞方式等待结果,您并没有真正获得异步执行它的好处。
通过使用 .get()
方法,您告诉 AsyncResult
对象等待结果。这意味着它会阻塞(就像你同步执行它一样)直到 Celery worker return 响应为止。
task.delay() # Async, don't await any response.
task.delay().get() # Blocks execution until response is returned.
有时这是您想要的,但在其他情况下您不需要等待响应,您可以完成 HTTP 请求的执行,而是使用回调来处理您触发的任务的响应。
我正在开发一个 Django 应用程序,它使用 Celery 运行 一些异步任务。我尝试使用 Apache Bench 执行负载测试并检查响应时间。从结果中我可以看出,没有 celery 异步任务,响应时间更快。
我正在使用:
Django 中的芹菜配置 settings.py:
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)
和tasks.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:
的结果
没有芹菜异步任务
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)
使用celery异步任务
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)
celery 异步任务不是应该使任务比同步任务运行得更快吗?我可能在这里错过了什么?
如有任何帮助,我们将不胜感激。谢谢。
运行 代码同步是在主线程上直接阻塞代码,另一方面 celery 的工作方式类似于 生产者消费者 机制。
Celery 将任务转发到代理消息队列,如 RabbitMQ 或 Redis 这会增加额外的处理时间。根据你的 celery 运行 所在的位置,如果不是 运行 本地,你可以考虑增加网络延迟。如果您正在调用 get
或 delay
那么 returns 一个可用于监视状态并在准备就绪时获取结果的承诺。
所以架构基本上变成了
网络
经纪人
- 工人
- 结果后端
考虑到如此多的处理 celery 任务比主线程上的 运行 慢
我认为你的问题中有很多误解需要回答。
Isn't celery async task supposed to make tasks work faster than synchronous tasks?
正如@Yugandhar 在他的回答中指出的那样,通过使用 Celery 之类的东西,您正在为您的处理增加额外的开销。您实际上不是在执行代码,而是在执行以下操作:
- 客户端向代理发送消息。
- Worker 获取消息并执行。
- 工人 return 对经纪人的回应。
- 客户端获取响应并处理它。
如您所见,与同步执行 Celery 相比,使用 Celery 显然会产生额外的开销。因此,说 "async task is faster than synchronous tasks".
不一定是真的那么问题来了,为什么要使用异步任务呢?如果它增加了额外的开销并可能减慢执行速度,那么它有什么好处呢?好处是你不需要等待响应!
我们以您的 ApiCall()
为例。假设调用本身需要 10 秒来执行。通过同步执行它意味着您将阻止其他任何事情在调用完成之前完成。例如,如果您有一个触发此事件的表单提交,则意味着用户必须等待浏览器加载 10 秒才能收到响应。这是一个非常糟糕的用户体验。
通过在后台异步执行,调用本身可能需要 10.01 秒才能执行(由于开销较慢),但不必等待这些秒过去,您可以(如果您选择)立即return 将响应返回给用户并使用户体验更好。
等待结果与回调
您的代码示例的问题在于同步和 "asynchronous" 代码基本上做同样的事情。它们都以阻塞方式等待结果,您并没有真正获得异步执行它的好处。
通过使用 .get()
方法,您告诉 AsyncResult
对象等待结果。这意味着它会阻塞(就像你同步执行它一样)直到 Celery worker return 响应为止。
task.delay() # Async, don't await any response.
task.delay().get() # Blocks execution until response is returned.
有时这是您想要的,但在其他情况下您不需要等待响应,您可以完成 HTTP 请求的执行,而是使用回调来处理您触发的任务的响应。