Django+Celery+RabbitMQ
-
需要在项目目录下创建一个新的celery.py文件(与setting.py同级)
# celery.py from celery import Celery # Set the default Django settings module for the 'celery' program. os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'proj.settings') app = Celery('proj') # Using a string here means the worker doesn't have to serialize # the configuration object to child processes. # - namespace='CELERY' 所有和Celery相关的配置需要前缀为CELERY_,防止与Django的配置重叠 app.config_from_object('django.conf:settings', namespace='CELERY') # 将来自Django应用的任务自动注册到Celery中 app.autodiscover_tasks() """ 应用中有tasks.py就能自动发现 - app1/ - tasks.py - models.py - app2/ - tasks.py - models.py """ @app.task(bind=True) def debug_task(self): print(f'Request: {self.request!r}')# settings.py # Celery Configuration Options CELERY_TIMEZONE = "Australia/Tasmania" CELERY_TASK_TRACK_STARTED = True CELERY_TASK_TIME_LIMIT = 30 * 60 -
需要在项目目录下个的_init.py_.py中导入这个应用程序,一遍@shared_task装饰器使用
from .celery import app as celery_app __all__ = ('celery_app',) -
在各应用中的tasks.py中使用的装饰器是@shared_task,而不是@app.task
-
django-celery-results,使用Django ORM/Cache作为结果后端
-
pip install django-celery-results
-
# settings.py INSTALLED_APPS = ( ..., 'django_celery_results', # 注意此处是下划线 ) # CELERY_RESULT_BACKEND = 'django-db' CELERY_CACHE_BACKEND = 'default' CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', 'LOCATION': 'my_cache_table', } } -
通过执行数据库迁移来创建 Celery 数据库表
python manage.py migrate django_celery_results
-
-
django-celery-beats,管理定期任务
- pip install django-celery-beats
# setting.py INSTALLED_APPS = ( ..., 'django_celery_beat', )python manage.py migrate celery -A proj beat -l INFO --scheduler django_celery_beat.schedulers:DatabaseScheduler -
启动工作进程
python manage.py celery -A proj worker -l INFO -
示例
# blog/models.py from django.db import models from django.utils.translation import ugettext_lazy as _ class Comment(models.Model): name = models.CharField(_('name'), max_length=64) email_address = models.EmailField(_('email address')) homepage = models.URLField(_('home page'), blank=True, verify_exists=False) comment = models.TextField(_('comment')) pub_date = models.DateTimeField(_('Published date'), editable=False, auto_add_now=True) is_spam = models.BooleanField(_('spam?'), default=False, editable=False) class Meta: verbose_name = _('comment') verbose_name_plural = _('comments')# blog/views.py from django.http import HttpResponseRedirect from django.template.context import RequestContext from django.shortcuts import get_object_or_404, render_to_response from blog import tasks from blog.models import Comment class CommentForm(forms.ModelForm): class Meta: model = Comment def add_comment(request, slug, template_name='comments/create.html'): post = get_object_or_404(Entry, slug=slug) remote_addr = request.META.get('REMOTE_ADDR') if request.method == 'post': form = CommentForm(request.POST, request.FILES) if form.is_valid(): comment = form.save() # 异步检查垃圾邮件 tasks.spam_filter.delay(comment_id=comment.id, remote_addr=remote_addr) return HttpResponseRedirect(post.get_absolute_url()) else: form = CommentForm() context = RequestContext(request, {'form': form}) return render_to_response(template_name, context_instance=context)# blog/tasks.py from celery import Celery,shared_task from akismet import Akismet from django.core.exceptions import ImproperlyConfigured from django.contrib.sites.models import Site from blog.models import Comment app = Celery(broker='amqp://') @shared_task def spam_filter(comment_id, remote_addr=None): logger = spam_filter.get_logger() logger.info('Running spam filter for comment %s', comment_id) comment = Comment.objects.get(pk=comment_id) current_domain = Site.objects.get_current().domain akismet = Akismet(settings.AKISMET_KEY, 'http://{0}'.format(domain)) if not akismet.verify_key(): raise ImproperlyConfigured('Invalid AKISMET_KEY') is_spam = akismet.comment_check(user_ip=remote_addr, comment_content=comment.comment, comment_author=comment.name, comment_author_email=comment.email_address) if is_spam: comment.is_spam = True comment.save() return is_spam

浙公网安备 33010602011771号