php评论使用websocket,音讯队列的连带知识

诚如评论分这三种方式:1.盖楼格局(例如乐乎情报这种的,较为复杂)2.单条新增情势(比如laravel-china社区的评论系统,也是不盖楼格局)

1.新闻队列相关消息介绍:
队列介质有:
mysql:可信性高 易完结 速度慢
redis:速度快,单条大消息包时作用低
音讯系统:专业性强,可倚重,学习开销高
新闻处理触发机制:
澳门葡京备用网址,死循环形式读取:易已毕,故障时手足无措即时过来
定时职分:压力均分,有处理上限
护理进度:类似于PHP-FPM和PHP-CGI,须要shell基础

数据库使用的是关系型数据库
Mysql。本小说的数据库设计关心已毕,品质或不能够担保。

原文:

  1.单条新增形式比较简单(1.单条方式:不管新增评论或许过来评论都以一条新评论
 
 2.单条引用情势:新增评论是一条新评论,回复评论时上边会显示会展现回复的是哪条评论)

由于博客平台的数据库设计重点是环绕用户和博客之间举行,所以相对其余部分选取来说,博客平台的数据库设计是相比容易的。

 

    单条形式:(数据库设计)

订单系统(接受用户订单) 订单队列表(mysql) 配送系统及配送结果(crontab)

ER 图设计

澳门葡京备用网址 1

【参考】博客系统ERAV4图.png

率先是这几个宗旨的前篇链接

    id        (自增i主键,评论id)

接受用户订单 订单系统 队列表 定时脚本(goods.sh) 配送处理种类配送系统
(order.php) (order_id status) 每分钟开行 (Goods.php)
mobile address
created_at updated_at

mysql文件

mysql
文件查看/下载地址

前篇小说构思了一个用户广播的兑现,并且付诸了伪代码。未来 codecampo
已经落实了一个基于 Mongodb + redis
的图景广播,所以可以填补一下前篇没有描述清楚的地点。

    content      (评论内容)

3.订单队列表
order_queue
id order_id mobile address
created_at updated_at
status(0未处理 1已处理 2处理中)

表的安插

表的规划,最根本的是用户表(user)、博客表(blog)和评论表(comment)。

0 Timeline 用查询依旧缓存?

上篇说到由于广播规则的错综复杂,timeline 最好使用一个队列,新增 status
使用投递格局而不依靠数据库查询。

切切实实看例子,campo 当前的 status 数据会是那样的:

> db.status_bases.findOne({ _type : "Status::Topic" })
{
    "_id" : ObjectId("4df484bde7444a4597000002"),
    "_type" : "Status::Topic",
    "created_at" : ISODate("2011-02-19T12:14:53Z"),
    "tags" : [ ],
    "topic_id" : ObjectId("4d5fb43d9f328b666500000a"),
    "user_id" : ObjectId("4d5fb41b9f328b6665000006")
}

> db.status_bases.findOne({ _type : "Status::Reply" })
{
    "_id" : ObjectId("4df484c0e7444a45970003a7"),
    "_type" : "Status::Reply",
    "created_at" : ISODate("2011-05-21T15:31:30Z"),
    "reply_id" : ObjectId("4dd7dad29f328b74df000018"),
    "targeted" : true,
    "topic_id" : ObjectId("4d5fb94c9f328b666500001f"),
    "user_id" : ObjectId("4d5e8dfc9f328bd543000002")
}

眼前有两连串型的 status,一类跟宗旨创造连锁,叫做
Status::Topic,一类跟过来创造连锁,叫做
Status::Reply。那两类数据存在同一个 collection
中,数据有相同的地点,比如:userid,topicid;也有独家特色的数额,比如:reply_id,targeted(是不是以@开端的第一手过来),tags(缓存宗旨的tags)。

Timeline 的规则是:1、不突显本身的 status 2、不显得 targeted 为 true
的直接过来 3、突显 following 用户的 status 4、突显出现本身喜爱标签的
status 5、突显自身关心宗旨和协调创设的宗旨的恢复生机 status 6、按时间排列

一经用数据库查询怎么落到实处 Timeline?用 mongoid 查询看起来会是这么的:

mark_topic_ids = Topic.where(:marker_ids => @user.id).only(:_id).map(&:_id) 
self_topic_ids = @user.topics.only(:_id).map(&:_id)
topic_ids = (mark_topic_ids + self_topic_ids).uniq
status_ids = Status::Base.where(:targeted.ne => true, :user_id.ne => @user.id).any_of({:user_id.in => @user.following_ids.to_a}, {:tags.in => @user.favorite_tags.to_a}, {:topic_id.in => topic_ids}).asc(:created_at).limit(Stream.status_limit)

转移的 Mongo Query 或者令人吓一跳,因为用来 $in 查询的 followingids 和
favorite
tags 还有 topic_ids
会不短。纵然过早考虑性能不是一个好习惯,但本人以为每一趟都用查询来拿到一个不变的列表格外不“自然”。

所以可以设想建造一个 Timeline 队列缓存,当前有一个格外适合存放 Timeline
的内存型数据库:redis。

    user_source   (用户来源,微信,新浪,app等)

4.linux部署 crontab命令

用户表 user

率先设计一个用户表 user,它就像下属性(依照必要增减):

  1. user_id(主键,自增长)
  2. name
  3. password
  4. quote
  5. email
  6. sex
  7. perm(权限,那里暗中认可是0,假若是1代表的是协会者)

1 用 redis 储存 Timeline

php评论使用websocket,音讯队列的连带知识。先介绍一下 redis:

Redis is an open source, advanced key-value store. It is often
referred to as a data structure server since keys can contain strings,
hashes, lists, sets and sorted sets.

redis 对 lists 数据的支撑很好,优于 mongodb。例如小编没找到让 mongodb
不难插入一条数据到 List 尾部并且限定长度、屏弃老多少的好措施。

自身对 Timeline list 操作的需要如下:

  1. 能够插入一个 status id 到列表头部
  2. 借使 list 长度当先设定值(比如800),就删除底部的数量
  3. 能够接近翻页式的获取某区间的 ids

campo 达成的 timeline 操作封装在 app/model/stream.rb
文件中,完整代码可以在这里看到。

上面分析一下降到实处

    user_code    (用户标识,app端传来识别用户的)

分 时 日 月 周 命令

博客表 blog

  1. blog_id
  2. title
  3. content
  4. create_date(小说创制时间)
  5. modified_date(文章修改时间,如果不只怕修改就不须要)

push status

def push_status(status)
  $redis.lpush store_key, status.id
  $redis.ltrim store_key, 0, Stream.status_limit - 1
end

push_status 操作先用 lpush 操作将 id 从列表左侧 push 进去,然后用 ltrim
甩掉列表左边当先指定数量的 id。

    user_pic     (用户头像)

/1 * * * *
/home/www/queue/goods.sh
>> /home/www/queue/queue.log 2>&1

评论表 comment

评论表是连接用户表和博客表的关系表。其次评论还要考虑到是对小说的褒贬恐怕对另一条评论的评头品足。

  1. comment_id
  2. owner_user_id(发表评论的用户id)
  3. create_date(评论创设事件)
  4. target_user_id(目的用户id,假设是对小说评论,设置为暗许值0)
  5. content

关于 target_user_id
这些表字段,假使这么设计的话,评论针对的靶子就将会是“用户”,而不是某个指定的“评论”。那代表你大概不可以落到实处在某个评论下盖楼。假诺你要完结盖楼格局的褒贬,提议改为使用
target_comment_id
表字段,用它来针对一个一定的“评论”,而不是一个“用户”。

乘势评论的充实,我们大概还须求对 comment
表举办横向分表,分成多少个有一致表字段的表,来下滑单表数据量,优化查询品质。

获取某区间 ids

def status_ids(start = 0, stop = -1)
  $redis.lrange store_key, start, stop
end

redis 的 lrange 操作能够分段读取 list 数据。实际读取 Timeline 时,先得到ids,然后再到 mongodb 获取文档数据。具体贯彻看 Stream#fetch_statuses。

    add_time      (添加时间)

touch /home/www/queue/queue.log

关注表 follow

用户与用户之间的涉嫌是 多对多 的涉及,有多少个表字段:

  1. follow_id
  2. user_id
  3. target_user_id

2 重建 Timeline

有二种状态需求重建 提姆eline:1、服务崩溃导致队列丢失 2、用户新增订阅。

这时可以用前边提到的 Mongodb 查询重建
Timeline。重建可以看做后台职务举办,那样无论规则多么复杂都不会阻塞用户的新增订阅的操作。

详见可以看 Stream#rebuild_later 和 Stream#rebuild 的实现。

    level        (是还是不是置顶)

tail -f queue.log

会话表 session

以此不是必须用关系型数据库完毕,推荐应用 redis 数据库来完毕session。因为 redis 将数据保存在内存中,访问较快。该表是由 npm
的一个模块包 express-mysql-session 创建的。

  1. session_id
  2. expires
  3. data

3 关于数据完整性?

接触 NoSQL 应用之后,常常听到的一个难题是数据完整性。campo
当前的兑现有完整性问题么?有的,比如删除一个 status 的时候 提姆eline
里面会遗留无效的 id。但据悉气象的两样,web
应用普通可以忽略那一个完整性:读写须要远大于删除要求、用户本身不在乎数据完整性。

campo 的 Timeline 里面碰到无效 id 的时候,会造成某页的 status
数量不足分页数量,但那不是什么样大题材。能够在用户下次触发 Timeline
重建的时候屏弃,可能趁着年华的延期被新 status 推后直至放任。

自然通过 redis 缓存 + mongodb 也足以查询一个尚未遗憾的 Timeline

# slow than fetch_statuses, but complete than fetch_statuses
def statuses
  Status::Base.where(:_id.in => status_ids).desc(:created_at)
end

而是用一个 800 ids 的 $in
查询自个儿觉得不太优雅,所以实际中并不曾调用那个格局。

    其余字段……   

5.秒杀业务程序
redis 入库程序
数据库

收藏表 collection

用户收藏博客是 多对多 的关系。

  1. collection_id
  2. user_id
  3. blog_id
  4. type(收藏夹名称)

4 小结

今天已经落实了上篇主题中涉嫌的第二品级
Timeline,而第三阶段的“忽略不活跃用户”,近来 campo
还未曾直达这么些用户量,就可是分设计了。

对从前几日的音讯过载的互连网,订阅和播音方式是很好的新闻过滤方式。用户应该允许只关心自身感兴趣的内容,并且屏蔽不感兴趣的故事情节。campo
接下去还会兑现用户 block 和大旨 mute 成效。

订阅格局在互连网上曾经出现很久了,然则现实贯彻的篇章不多,希望本篇给寻找此类消息的人某些助手。

 

统筹思路:
秒杀程序把请求写入Redis(uid ,time_stamp)
自小编批评Redis已存放数据的长度,超出上限直接丢掉
死循环处理存入Redis的多寡并入数据库

标签表 tag

(从那些表起头只做了布署,没用 mysql 完成出来)

  1. tag_id
  2. tag_name
  3. description

    单条引用方式(数据库设计):

6.redis_queue 表 从redis入数据库的表
id uid time_stamp

stick

tag 表和 blog 表是 多对多 关系。使用 stick 关系表进行互换:

  1. stick_id
  2. blog_id
  3. tag_id

    id       (自增主键,评论的id) 

7.真实的景观如若其它一个顺序一贯在循环取redis队列中的数据,在高并发的情景下必将会冒出超卖景况,那么可以在redis队列中数据达到秒杀指定人数后,关闭前台页面中的秒杀按钮,点击购买指示秒杀停止不再插入redis中,然后再通进程序从redis列中取数据->插入mysql->下订单等等逻辑操作。

浏览表 browser

比方要精通小说被有些人浏览过,可以加上这么一个表。

  1. browser_id
  2. user_id
  3. blog_id
  4. create_date(要求的话能够加上)

行吗笔者认可这么设计数据库显得繁琐,质量也不佳(要出口表中某个 blog_id
才能领略总浏览量),可是这么些表能清晰的明亮有何样用户浏览了某篇小说,可以将一篇文章的浏览者全体列出来(比如
QQ 空间可以知道什么人访问了什么怎么相册
那种效果),其它仍是可以组成浏览时间,还是可以完毕查看历史记录的功力。

最后化解方案:如若只是想驾驭浏览量,可以直接将
浏览量(blog_num)表字段 放到 blog
表中,每一趟浏览都对该表字段进展三次自增。那意味用户每一趟浏览文章所在的路由是,都要进行判断。怎么着判断用户真正是
“浏览” 了,还只是看了一眼(只可以归咎到“点击量”)就跑了,必要考虑考虑。

    content    (评论内容)

喜欢 like

喜好某篇文章。同样可以像下边 browser 表那样落成。

    add_time   (评论时间)

结尾

仅供参考,因为本人的小项目只兑现了一部分的职能,其余表设计中设有的补足只怕不可以发现。需要的改观也会促成数据库设计的布局暴发变化。

    r_id      (被回复者的id)

    r_content   (被回复者评论内容)

    r_time    (回复时间)

    level     (回复是不是置顶)

    其余字段……..(那里作者喜爱把苏醒的评说和被苏醒评论当作一条评论,在控制器里依照r_id(被復苏的评论id)去redis里取数据,若是没有就去查数据库并存入redis设置过期时间,把收获的数目插入数据库,也可以去掉r_起来的字段换为parent_id,通过那几个遍历查询去取得)

 

    2.盖楼那种情势如今作者会的是透过parent_id来递归查询,若是是率先条被评价的parent_id为0,其余parent_id是被评价的id,但那种情势尤其消耗内存

 

 

  在此在此以前端拿到了评价的多寡,大家用websocket举办发送的时候,最好压缩一下讲评字段,那样可以省去服务器资源,比如有10000个人在线,其中一人发了一条评论,其他9999个人都得看见,那本来非常长的字段压缩后总数据量大小就裁减了,

  用websocket发送时得以通过post格局和redis订阅格局发送,小编更倾向于redis订阅方式的出殡,你可以对评论添加举行日志的治本

   post方式发送要有websocket的服务器地址和要发送的数码,发送的数码中要有一个key值是在websocket中登记过的

    redis订阅格局的公布要先连上redis服务器,也要在websocket上登记过的key,还有要发送的数额

  评论发送websocket时也分为先审后发和先发后审,先审后发相比较不难,有数量先进redis队列,然后通过Linux的crontab程序刷进数据库,后台要操作哪条就发哪条的websocket

  头阵后审的话有数量也是先进redis队列,评论并发多的话就要有二种队列,发websocket的系列和不发websocket的连串,然后经过Linux的crontab程序刷进数据库

  crontab是最低每分钟实施三遍,你可以友善写一个php文件每秒执行五遍,php文件里就写个for循环执行,但要在文件最前部加上如此一段代码

      

  $cc = exec("ps -ef |grep '" . __FILE__ . "' | grep -v 'grep' | wc -l");
  if ($cc > 10) {
    exit("program is running……");
  }
  这是防止某个程序执行太多,让服务器崩溃,本人菜鸟级别,如果以上有什么不对的欢迎指出来

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

*
*
Website