我的网站搭建(第53天) 定时刷新服务器缓存

  • 发布时间:2017年5月11日 12:11
  • 作者:杨仕航
* 该文是基于Python2.7开发的,最新Python3.x和Django2.x视频教程可以前往 >> Django2.0视频教程

为了提高页面响应速度,使用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日志,如下图:

20170511/20170511120948762.png


分别执行了3次刷新缓存的任务。第1次是刚执行Celery时运行一次定时任务,后面两次都是在0分时刻运行。

更多相关的Django-Celery-Redis文章可参考专题:http://yshblog.com/subject/7

上一篇:Python正则使用函数替换

下一篇:服务器使用Supervisor后台运行Celery

相关专题: Django+Celery+Redis   

评论列表

智慧如你,不想发表一下意见吗?

新的评论

清空