关于本站
1、基于Django+Bootstrap开发
2、主要发表本人的技术原创博客
3、本站于 2015-12-01 开始建站
为了提高页面响应速度,使用Redis作为服务器缓存。但该缓存是被动刷新。
在没有缓存的情况下,第1次获取数据没有缓存,会先写入缓存。后面访客访问页面才有缓存可以直接使用。
于是又安装和学习Celery,想使用Celery定时更新服务器缓存。
相关的Celery和Redis安装使用可看Django+Celery+Redis专题。这里不在赘述,直接进入主题。
前面为了方便使用缓存代码开发,写了缓存装饰器加在获取数据的方法上。可获取数据时自动判断是否有缓存。若有缓存,返回缓存的数据;若没缓存,获取数据写入缓存。
但该缓存装饰器和准备加入的Celery定时任务装饰器逻辑有冲突。
若定时任务的装饰器加在缓存装饰器上,无法执行也无法主动更新缓存;若定时任务的装饰器加载获取数据方法上,只是执行获取数据方法无法写入缓存。so~需要改造代码。添加缓存处理类,代码如下:
#coding:utf-8 from django.core.cache import cache #获取和设置缓存的类 class RedisCache(): def __init__(self, key, timeout, get_data_method, args=None, kw=None): self.key = key self.timeout = timeout self.get_data_method = get_data_method self.args = [] if args is None else args self.kw = {} if kw is None else kw def get_cache(self): try: #判断缓存是否存在 if cache.has_key(self.key): data = cache.get(self.key) else: data = self.set_cache() except Exception as e: #使用缓存出错,可能是没开启redis data = self.get_data_method(*self.args, **self.kw) finally: return data def set_cache(self): data = self.get_data_method(*self.args, **self.kw) cache.set(self.key, data, self.timeout) return data
该类初始化时,需要传入参数key(缓存键名)、timeout(缓存超时时间)、get_data_method(获取数据的方法)。后面两个参数args和kw是执行get_data_method所需的参数。若该方法没参数则无需填写。
这么一来,就可以在实例化的对象中调用执行该获取数据的方法。
去掉原先加载获取数据方法上的装饰器,修改代码如下(该app下新建cache.py文件):
#目前有两个需要使用缓存的数据 #获取数据的方法简单结果如下,具体代码省略 def get_readed_data(item_num): data = [] # .... retrun data def get_chart_data(): data = [] # .... return data #获取一个缓存字典,可方便获取 def get_caches(): #缓存超时时间设置1小时1分钟 readed_list_cache = RedisCache('readed_list', 3660, get_readed_data, args=[14,]) chart_cache = RedisCache('chart', 3660, get_chart_data) caches = {} caches[readed_list_cache.key] = readed_list_cache caches[chart_cache.key] = chart_cache return caches #执行获取缓存管理器,以便被引用 caches = get_caches()
原先views.py文件返回数据可以引入caches,获取数据:
from .caches import caches #某响应方法 def index(request): data = {} data['readed_list'] = caches['readed_list'].get_cache() data['chart'] = caches['chart'].get_cache() #... 将data返回给模版页面即可
好了,上面处理了获取缓存的方法。接下来就可以愉快的刷新缓存。同样在该目录下创建tasks.py文件,写入代码:
#coding:utf-8 from __future__ import absolute_import from celery.task.schedules import crontab from celery.decorators import periodic_task, task #缓存 from .caches import caches #定时更新缓存 @periodic_task(run_every=crontab(minute=0)) def update_caches(): for cache in caches.values(): cache.set_cache()
引入缓存管理器,让celery在每小时0分时刻执行一次update_caches方法,刷新caches中的缓存。
本地测试正常,接着更新服务器。利用supervisor后台运行celery,但又出现问题了。开发就是这样,经常会碰到各种问题。遇到问题就尝试分析原因并找方法解决。
问题描述:可以正常执行supervisor打开celery worker 和 beat。但过一段时间之后服务器崩溃重启。
使用ps aux 命令发现运行了20个 celery worker。又输入top查了内存和CPU的使用情况(忘了截图,现在不想重现这个问题。会把服务器搞挂掉)发现内存不够用导致服务器崩溃。
重新打开django项目的settings.py文件。添加Celery如下设置:
#worker并发数,相当于--concurrency参数 CELERYD_CONCURRENCY = 4 #每次取任务的数量 CELERYD_PREFETCH_MULTIPLIER = 10 #每个worker执行多少次任务之后就销毁,防止内存泄漏。相当于--maxtasksperchild参数 CELERYD_MAX_TASKS_PER_CHILD = 64 #防止死锁 CELERYD_FORCE_EXECV = True #任务发出后,经过一段时间还未收到acknowledge , 就将任务重新交给其他worker执行 CELERY_DISABLE_RATE_LIMITS = True
重点是设置celery worker的并发数和执行多少次任务后销毁。这样服务器的内容就不再被爆掉。
再次运行supervisor。过了一段时间服务器还是正常运行,说明该解决方案可行。
打开celery日志,如下图:
分别执行了3次刷新缓存的任务。第1次是刚执行Celery时运行一次定时任务,后面两次都是在0分时刻运行。
更多相关的Django-Celery-Redis文章可参考专题:http://yshblog.com/subject/7
相关专题: Django+Celery+Redis