技术笔记——Django+Nginx+uwsgi搭建自己的博客(九)

这个系列好久没有更新了,主要是之前在实现了那么多功能后处于一个迷惘期,不知道下一步开发/学习的方向是什么。在参考了一些相关资料后,决定给这个博客加上基于redis的缓存机制,点亮Redis这个技能。在这篇博文中,我们将给博客的相关数据加上Redis缓存,以便用户在访问博客时可以更快加载。

Redis是一款基于内存的key-value型数据库,广泛用于缓存服务。由于它将数据存储在内存中,使得其加载数据的速度非常快。并且,Redis也支持持久化,可以将数据存储于硬盘中,当Redis再次启动时,Redis会从硬盘中读取之前存储的数据文件,将其再加载回内存。通常来讲,使用Redis的意义在于减少对硬盘数据库的访问,即当访问网站内容时,先判断在Redis缓存中是否存在,若存在的话可以直接从内存中读取数据,而无需访问硬盘上的数据库;若不存在的话再从硬盘数据库中读取,同时将新数据保存在缓存中,以便之后使用。

Redis的安装可以直接从Redis的官网(此官网最近处于抽风期)上下对应的安装包,下好后解压即可。当解压好后,我们可以进到其目录下的src目录,如我的目录为redis-4.0.8/src,输入

./redis-server

启动redis服务器,如果看到以下画面说明Redis启动成功:


这里出于习惯,我为启动redis服务器设置了别名runredis,大家也可以根据自己的习惯设置别名。

这样,我们的Redis环境就算初步准备好了。Redis的配置文件这里可以先不用特别设置,用默认的就好。

然后,我们要安装redis的python api,这个包已经包含在pypi里,所以我们直接输入命令:

pip3 install redis

就可以完成安装。

我们这次要对博客的标题、内容以及阅读数加入缓存机制,思路如下:

当访问某个博客页面时,首先检查当前博客的标题、内容、阅读数是否在redis缓存中;

若三者都在redis中,直接从redis中取得博客的标题、内容,并对redis中的阅读数+1;

若有某项不在redis中,则从数据库中取得该值,并将其加入redis,以便以后使用,且依然对redis中的阅读数+1;

当某篇博客处于编辑时,在完成编辑并保存时删除redis的标题和内容缓存,以防止缓存和数据库数据不一致;

当某篇博客被删除后,从redis中删除该博客的标题、内容和阅读数缓存;

当首页被访问时,将所有博客在redis中的阅读数回写到sqlite数据库中。

因此,我们需要改动blogs App的相关页面函数以及首页函数。首先来修改blogs/views.py中的content函数:

# blogs/views.py content
from redis import StrictRedis,ConnectionPool
# ...
def content(request,blogId):
    pool = ConnectionPool(host='localhost',port='6379',db=0)
    redis = StrictRedis(connection_pool=pool)
    blog = Blog.objects.get(id=blogId)
    title_key = blogId + '_title'
    content_key = blogId + '_content'
    readcount_key = blogId + '_readcount'
    if redis.exists(title_key):
        blog_title = redis.get(title_key)
    else:
        redis.set(title_key, blog.title)
        blog_title = redis.get(title_key)
    if redis.exists(content_key):
        blog_content = redis.get(content_key)
    else:
        redis.set(content_key, blog.content)
        blog_content = redis.get(content_key)
    if redis.exists(readcount_key):
        redis.incr(readcount_key)
    else:
        redis.set(readcount_key, blog.readcount)
        redis.incr(readcount_key)
    comment = Comment.objects.filter(attachedblog=blog)
    request.session['currblogId'] = blogId
    blogContent = {
                   'blog_title':blog_title,
                   'content':blog_content,
                   'comment_list':comment
                   }
    return render(request,'blogs/content.html',blogContent)
# ...

我们在这里使用ConnectionPool对象建立一个对默认redis数据库的连接,用于之后向redis里加入数据。由于需要保存三个数据在redis,因此我设计了三个key:用于存储标题的title_key,用于存储内容的content_key以及用于保存阅读数的readcount_key,这里使用博客的id+后缀的方式来确保key的唯一性(因为博客的id是唯一的)。在这里,使用redis.exists(key)函数来判断某个key是否存在于redis中,而使用redis.set(key,value)和redis.get(key)函数用于存取redis中的数据。至于redis.incr(key),则是对key中储存的数字值+1,这里用于对阅读数+1。

这里有个小问题没有解决:由于不清楚redis能否存储对象,所以我只能存储blog对象的属性值。然而,为了显示博客下的comment,我又必须通过

blog = Blog.objects.get(id=blogId)

取得对象,才能在后面拿到当前博客下的评论。这样一来,似乎在这里使用redis没有必要了?因为无论如何我都需要从硬盘数据库中拿到这个博客对象到内存中,然后再获取它的标题、内容以及阅读数。这样看来,在这里使用redis有些多此一举,并不能实现绕过硬盘直接拿内存数据的作用。不过,这里使用redis最大的好处是将阅读数的增加放置在了内存中,相比之前的每阅读一次就调用一次blog.save()相比还是节省了很多开销。

下面我们来修改blogs/views.py下的addBlog函数和deleteblog函数。

def clearRedis(keys):
    pool = ConnectionPool(host='localhost', port='6379', db=0)
    redis = StrictRedis(connection_pool=pool)
    for key in keys:
        if redis.exists(key):
            redis.delete(key)
    pool.disconnect()

def addBlog(request):
    if request.method == 'POST':
        if request.session.has_key('currentblogId'):
            blogId = request.session['currentblogId']
            tmpBlog = Blog.objects.get(id=blogId)

            if tmpBlog:
                form = BlogForm(request.POST,instance=tmpBlog)
                tmpBlog = form.save(commit=False)
                tmpBlog.draft = False
                tmpBlog.save()
                result_info = 'Success'
            else:
                form = BlogForm(request.POST)
                if form.is_valid():
                    newBlog = form.save(commit=False)
                    newBlog.auther = Users.objects.get(username=request.session['username'])
                    newBlog.draft = False
                    category = Category.objects.get(categoryname=newBlog.category)
                    category.blogcount = category.blogcount+1
                    category.save()
                    newBlog.save()
                    result_info = 'Success'
                    # 当编辑已有博客时,删除redis中内容
            title_key = blogId + '_title'
            content_key = blogId + '_content'
            clearRedis([title_key, content_key])
            del request.session['currentblogId']
        else:
            form = BlogForm(request.POST)
            if form.is_valid():
                newBlog = form.save(commit=False)
                newBlog.auther = Users.objects.get(username=request.session['username'])
                newBlog.draft = False
                category = Category.objects.get(categoryname=newBlog.category)
                category.blogcount = category.blogcount + 1
                category.save()
                newBlog.save()
                result_info = 'Success'
            del request.session['currentblogId']
        return HttpResponseRedirect(reverse('blogs:addblogResult', kwargs={'info': result_info}))
    else:
        if request.session['username'] != 'anony':
            form = BlogForm()
        else:
            return render(request, 'blogs/failedoperation.html')
    return render(request, 'blogs/addblog.html', {'form':form})

这里我们写一个函数clearRedis,它接受一个key的list,并将这个list中的key都从redis中清空。在addBlog函数中,加粗的地方就是我们新增的代码,使用clearRedis将标题和内容从redis中清除出去。如果没有这一段的话,我们会碰到这样的问题:当一篇博客编辑后发表,缓存中却保存的是旧的内容,因此更新后的博客直至缓存过期都无法被看到。因此我们需要在编辑博客后删除博客的缓存。

def deleteblog(request,blogId):
    blog = Blog.objects.get(id=blogId)
    if blog.auther.username == request.session['username']:
        blog.delete()
        title_key = blogId + '_title'
        content_key = blogId + '_content'
        readcount_key = blogId + '_readcount'
        clearRedis([title_key, content_key, readcount_key])
        blogList = Blog.objects.filter(auther=request.session['username'])
    else:
        return render(request, 'blogs/failedoperation.html')
    return HttpResponseRedirect(reverse('blogs:blogmanage'))

deleteblog函数中添加的代码与addBlog类似,只不过把阅读数也从缓存中清空,从而将这篇博客从所有数据库中“斩尽杀绝”。

最后一步,修改myblog/views.py中的index函数,将redis中保存的博客阅读数回写到sqlite中,以便在前端显示出来:

def index(request):
    try:
        username = request.session['username']
        user = Users.objects.get(username=username)
    except KeyError:
        user = Users.objects.get(username='anony')
        request.session['username'] = 'anony'
    except Users.DoesNotExist:
        user = Users.objects.get(username='anony')
        request.session['username'] = 'anony'
    blogList = Blog.objects.filter(draft=False).order_by('title')
    # 从redis中将每篇博客的阅读数回写到数据库中
    pool = ConnectionPool(host='localhost', port='6379', db=0)
    redis = StrictRedis(connection_pool=pool)
    for blog in blogList:
        readcount_key = str(blog.id)+'_readcount'
        if redis.exists(readcount_key):
            blog.readcount = redis.get(readcount_key)
            blog.save()
    pool.disconnect()

    searchform = searchForm()
    # get all unread message count
    unreadmeg =  InfoMessage.objects.filter(attachUser=user).filter(isRead=False)
    # username = request.session.get('username')
    content = { 'blog_list':blogList,
                'curruser':user,
                'searchform':searchform,
                'msgcount':len(unreadmeg)
               }
    return render(request, 'myblog/index.html', content)

加粗部分是我们这次新增的代码(如果细心读的话会发现代码与第八篇相比不仅多了redis的东西,还多了用户消息以及实现的一个简单搜索的功能,但这部分的实现个人感觉不是太正规,因此暂时不讲)。在主页上,对于每篇博客我们都要从redis中拿到其阅读数,并将其回写到sqlite中,这样每当有人访问主页时,都会看到正确的阅读数。

在这篇博客中,给我们的博客加入了简单的redis缓存系统,将博客的标题、内容以及阅读数均存在了缓存中。预计最近一段时间都会围绕着redis缓存做文章,希望大家继续关注,也希望大家可以帮我思考一下文中提到的那个小问题~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值