django项目之SNS社交平台开发(模拟新浪微博)

本节内容

  1. 项目介绍 
  2. 架构设计 
  3. 表结构设计 
  4. 图片本地预览功能
  5. 项目详细功能实现

1. 项目介绍

新浪weibo是一个用来练习开发能力的非常好的项目,本节我们一起来开发一个简单版的weibo

1.1功能需求

  1. 一个人可以follow很多人
  2. 一个用户如果发了新weibo会自动推送所有关注他的人
  3. 可以搜索、关注其它用户
  4. 可以分类关注
  5. 用户可以发weibo, 转发、收藏、@其它人
  6. 发微博时可选择公开、隐私、只能好友看等
  7. 可私信聊天
  8. 热门微博无需关注 
  9. 我的微博列表
  10. 可以评论、点赞微博
  11. 发微博时可以上传图片、视频、可以发表情

 

2. 架构设计

2.1 设计架构前需要考虑的问题

  • 1.图片的本地预览、异步上传技术,上文已有叙述(详见下文);
  • 2.当用户发布微博后,需要立即告诉用户已经发布成功,并在前端展示,可是新浪微博同一时间可能有上千用户发微博,也就是说短时间内需要做大量的数据库操作,这就需要用户等很长时间,这样就没有完美的用户体验,那么这个问题该如何解决呢?

      解决方案:当用户发布微博,上传图片至临时目录,微博内容放入rabbitmq,前端获取微博内容和图片地址,立即生成新微博,并告诉用户微博发布成功。事实上,此时微博只是放入了rabbitmq,并没有存入数据库,也就是说只要用户的微博放入rabbitmq,就算用户发新微博成功。然后后台其他模块从rabbitmq取消息并存入数据库。

  • 3.用户发送微博后,需要将该微博推送给他的所有关注者,而用户同时可上传多张图片和视频,目前一张高清图片大约10M左右,假设用户每次上传3张图片,共30M,需要将30M推送给所有粉丝,大家都知道新浪微博中一个明星可能有几千万粉丝,以姚晨为例,姚晨有7000多万粉丝,那么问题来了,难道我们要将这30M内容存入每一个粉丝的数据库表里吗?这样下去,新浪微博短短几分钟就需要大量的存贮空间,如何解决这个问题呢?

  解决方案:推送给粉丝微博时,仍然推送姚晨对应表的数据即可,粉丝那里存微博id即可,这样就解决了存贮问题。

  • 4.如问题3所示,姚晨有7000多万粉丝,我们真的需要推送给那么多人吗?那又需要做多少数据库操作,服务器压力也会很大,那么如何解决这个问题呢?

  解决方案:新浪微博是怎么做的呢?这里我们可以筛选有效用户进行推送,那么什么是有效用户呢?如果一个用户很长时间没有登录,那么我们推送给他也没有意义,这里我们定义有效用户即活跃用户,即用户24小时内有登录过。经过筛选,姚晨的7000多万粉丝可能只需要推送其中的一千万即可,这样大大减小了服务器的压力。

  • 5.如何判断用户是否是活跃用户呢?

     解决方案:当用户登陆的时候,我们以用户id作为key值,Ture作为value值存入缓存radis,并设置超时时间为24小时,每次推送前会判断radis里是否含有当前用户id,如果有,则推送。

  • 6.如何实现接口安全?

    详细点击右侧链接:如何实现接口安全?

 

2.2 架构实现

所需服务组件:

  • django: 用户页面呈现
  • nginx: 前端高并发必备
  • cdn:异地高并发、静态内容速度快必备
  • redis: 热点weibo\话题数据高效存储
  • rabbitMQ: 用户weibo推送
  • SOA:服务解耦,服务接口化
  • zabbix,nagios: 系统监控
  • google analysis: 用户访问质量、行为分析监控

 

用户发微博流程图

 

 

3.表结构设计

from django.db import models
from django.contrib.auth.models import User
# Create your models here.


class Weibo(models.Model):
    '''所有微博'''
    wb_type_choices = (
        (0,'new'),
        (1,'forward'),
        (2,'collect'),
    )
    wb_type = models.IntegerField(choices=wb_type_choices,default=0)
    forward_or_collect_from = models.ForeignKey('self',related_name="forward_or_collects",blank=True,null=True)
    user = models.ForeignKey('UserProfile')
    text = models.CharField(max_length=140)
    pictures_link_id = models.CharField(max_length=128,blank=True,null=True)
    video_link_id = models.CharField(max_length=128,blank=True,null=True)
    perm_choice = ((0,'public'),
                   (1,'private'),
                   (2,'friends'))
    perm = models.IntegerField(choices=perm_choice,default=0)
    date = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.text

class Topic(models.Model):
    '''话题'''
    name = models.CharField(max_length=140)
    date = models.DateTimeField()
    def __str__(self):
        return self.name
class Category(models.Model):
    '''微博分类'''

    name = models.CharField(max_length=32)
    def __str__(self):
        return self.name

class Comment(models.Model):
    '''评论'''
    to_weibo = models.ForeignKey(Weibo)
    p_comment = models.ForeignKey('self',related_name="child_comments")
    user = models.ForeignKey('UserProfile')
    comment_type_choices = ((0,'comment'),(1,'thumb_up'))
    comment_type = models.IntegerField(choices=comment_type_choices,default=0)
    comment = models.CharField(max_length=140)
    date  = models.DateTimeField(auto_created=True)

    def __str__(self):
        return self.comment

class Tags(models.Model):
    '''标签'''
    name = models.CharField(max_length=64)
    def __str__(self):
        return self.name

class UserProfile(models.Model):
    '''用户信息'''

    user = models.OneToOneField(User)
    name = models.CharField(max_length=64)
    brief = models.CharField(max_length=140,blank=True,null=True)
    sex_type = ((1,'Male'),(0,'Female'))
    sex = models.IntegerField(choices=sex_type,default=1)
    age = models.PositiveSmallIntegerField(blank=True,null=True)
    email = models.EmailField()
    tags = models.ManyToManyField(Tags)
    head_img = models.ImageField()


    follow_list = models.ManyToManyField('self',blank=True,related_name="my_followers",symmetrical=False)

    #registration_date = models.DateTimeField(auto_created=True)
    def __str__(self):
        return self.name
简单版weibo表结构

 

4.上传图片本地预览功能

如下图,发微博时,可以同时发图片,图片我们肯定是通过ajax异步上传到后台,但是我只是从电脑里选择了图片后,我还想实现一个在本地小图预览的功能, 这个需求如何实现呢?

 

有的同学会立刻想到,把图片先传到后台服务器,然后返回一个img url, 在当前浏览器上就可以调用这个用img url预览了,这样是没问题的。 但是这是最好的办法么?如果我上传的图片非常大,比如说5mb,网速又慢,那我可能2分钟后才能看到预览, 用户体验必然不好, 有没有办法可以实现在图片还没上传完就可以预览呢?

 

本地预览实现

function handleFileSelect(evt) {
    var files = evt.target.files;

    // Loop through the FileList and render image files as thumbnails.
    for (var i = 0, f; f = files[i]; i++) {

      // Only process image files.
      if (!f.type.match('image.*')) {
        continue;
      }

      var reader = new FileReader();

      // Closure to capture the file information.
      reader.onload = (function(theFile) {
        return function(e) {
          // Render thumbnail.
          var span = document.createElement('span');
          span.innerHTML = 
          [
            '<img style="height: 75px; border: 1px solid #000; margin: 5px" src="', 
            e.target.result,
            '" title="', escape(theFile.name), 
            '"/>'
          ].join('');
          
          document.getElementById('list').insertBefore(span, null);
        };
      })(f);

      // Read in the image file as a data URL.
      reader.readAsDataURL(f);
    }
  }

  document.getElementById('files').addEventListener('change', handleFileSelect, false);
js code
<input type="file" id="files" multiple />
<output id="list"></output>

参考 http://stackoverflow.com/questions/14069421/show-an-image-preview-before-upload 

  

4. 协作开发 

git 基础教程http://www.runoob.com/git/git-branch.html 

 

5.项目详细功能实现

目录结构:

 

 

 

 

 

 

 

 

 

 
posted @ 2016-10-08 23:58  wangheng1409  阅读(990)  评论(1)    收藏  举报