我们现在有以下项目结构:
- 处理来自客户端的传入请求的 Web 服务器。
- 分析模块,为用户提供一些建议。
我们决定让这些模块完全独立,并将它们移动到不同的 docker 容器中。当用户的查询到达 Web 服务器时,它会向分析模块发送另一个查询以获取建议。
为了使建议保持一致,我们需要定期进行一些后台计算,例如,当新用户在我们的系统中注册时。此外,一些后台任务纯粹与 Web 服务器逻辑连接。为此,我们决定使用分布式任务队列,例如 Celery。
任务创建和执行有以下可能的情况:
在Web服务器上排队的任务,- 在Web服务器上执行的任务(例如,处理上传的图像(
- 在Web服务器上排队的任务,在分析模块上执行的任务(例如,为新用户计算建议(
- 任务在分析模块上排队并在那里执行(例如,定期更新(
到目前为止,我在这里看到了 3 种相当奇怪的可能性:
I.芹菜放在单独的容器中,做所有事情
- 将芹菜移动到单独的 docker 容器。
- 提供来自Web服务器和分析的所有必要软件包来执行任务。
- 与其他容器共享任务代码(或在 Web 服务器和分析中声明虚拟任务(
这样,我们就可以松开隔离,因为功能由 Celery 容器和其他容器共享。
II. 芹菜放在单独的容器中,做得少得多
和我一样,但任务现在只是对 Web 服务器和分析模块的请求,它们在那里异步处理,结果在任务内轮询,直到它准备好。
这样,我们从拥有经纪人中受益,但所有繁重的计算都从 Celery 工人那里转移。
三、每个容器中分别装芹菜
- 在网络服务器和分析模块中运行芹菜。
- 将虚拟任务声明(分析任务(添加到 Web 服务器。
- 添加 2 个任务队列,一个用于 Web 服务器,一个用于分析。
这样,在Web服务器上安排的任务可以在分析模块中执行。但是,仍然必须在容器之间共享任务代码或使用虚拟任务,此外,还需要在每个容器中运行芹菜工作器。
最好的方法是什么,或者应该完全改变逻辑,例如,将所有东西都移到一个容器内?
首先,让我们澄清芹菜库(pip install
或setup.py
获得(和芹菜工作者之间的区别 - 这是从代理中取消任务排队并处理它们的实际过程。当然,您可能希望有多个工作线程/进程(例如,将不同的任务分离到不同的工作线程(。
假设您有两个任务:calculate_recommendations_task
和periodic_update_task
,并且您想在单独的工作线程上运行它们,即recommendation_worker
和periodic_worker
。 将celery beat
另一个进程,它每 x 小时将periodic_update_task
排队进入代理。
此外,假设您有使用瓶子实现的简单Web服务器。
我假设你也想在docker中使用芹菜代理和后端,我会选择芹菜的推荐用法 - RabbitMQ作为代理,Redis作为后端。
所以现在我们有 6 个容器,我将将它们写成docker-compose.yml
:
version: '2'
services:
rabbit:
image: rabbitmq:3-management
ports:
- "15672:15672"
- "5672:5672"
environment:
- RABBITMQ_DEFAULT_VHOST=vhost
- RABBITMQ_DEFAULT_USER=guest
- RABBITMQ_DEFAULT_PASS=guest
redis:
image: library/redis
command: redis-server /usr/local/etc/redis/redis.conf
expose:
- "6379"
ports:
- "6379:6379"
recommendation_worker:
image: recommendation_image
command: celery worker -A recommendation.celeryapp:app -l info -Q recommendation_worker -c 1 -n recommendation_worker@%h -Ofair
periodic_worker:
image: recommendation_image
command: celery worker -A recommendation.celeryapp:app -l info -Q periodic_worker -c 1 -n periodic_worker@%h -Ofair
beat:
image: recommendation_image
command: <not sure>
web:
image: web_image
command: python web_server.py
构建recommendation_image
和web_image
的 dockerFiles 都应该安装 Celery库。只有recommendation_image
应该有任务代码,因为工作人员将处理这些任务:
推荐Dockerfile:
FROM python:2.7-wheezy
RUN pip install celery
COPY tasks_src_code..
WebDockerfile:
FROM python:2.7-wheezy
RUN pip install celery
RUN pip install bottle
COPY web_src_code..
其他映像(rabbitmq:3-management
&library/redis
可从docker hub获得,当您运行docker-compose up
时,它们将自动拉取(。
现在事情是这样的:在Web服务器中,您可以通过字符串名称触发芹菜任务,并通过任务ID提取结果(不共享代码(web_server.py:
import bottle
from celery import Celery
rabbit_path = 'amqp://guest:guest@rabbit:5672/vhost'
celeryapp = Celery('recommendation', broker=rabbit_path)
celeryapp.config_from_object('config.celeryconfig')
@app.route('/trigger_task', method='POST')
def trigger_task():
r = celeryapp.send_task('calculate_recommendations_task', args=(1, 2, 3))
return r.id
@app.route('/trigger_task_res', method='GET')
def trigger_task_res():
task_id = request.query['task_id']
result = celery.result.AsyncResult(task_id, app=celeryapp)
if result.ready():
return result.get()
return result.state
最后一个文件config.celeryconfig.py:
CELERY_ROUTES = {
'calculate_recommendations_task': {
'exchange': 'recommendation_worker',
'exchange_type': 'direct',
'routing_key': 'recommendation_worker'
}
}
CELERY_ACCEPT_CONTENT = ['pickle', 'json', 'msgpack', 'yaml']