用MySQL模拟消息队列

  1. 队列(queue)是一种先进先出FIFO的线性表结构,只允许在尾部插入和在头部删除。最近一两年火爆的团购秒杀等应用,其中的大并发是会把数据库压垮的,一个解决方法就是把HTTP请求放入内存中的高速队列,然后对队列里的数据按一定的规则进行分流处理,这就是HTTP请求队列。比如,微博和SNS通常拥有上亿的受众数量,一个明星或公众人物可能有几千万的粉丝,如果一个公众人物发了条微博,那么就得推送到所有关注者那里(有推策略、拉策略、随机策略、优先策略等),这时就必须用到队列。面对大数据量和高并发的WEB应用,队列工具可大有所为。

  2. 下面以SNS中的动态分发的设计过程为例进行讲解队列在消息高度机制中的实现和使用。
    当某用户发布一条动态的时候,先把这条动态插入feed表,然后判断当前用户组,普通朋友则给所有好友发布广播,往feedBroadCast表写入冗余数据,但这种频繁不间断的读写数据库会给服务器造成很大压力,注意到好友动态不必具有十分高的实时要求,所以这里采用异步的推,把动态推给好友而不是好友主支拉取。(我理解为在冗余表里异步写入数据,不是很理解把动态推给好友 是什么意思。。)
    随着用户增加,在前端页面直接定稿大量数据会延长用户的等待时间,在多并发的情况下页面效率低压力集中,为解决上述问题,考虑使用数据的异步处理,而消息队列的背后实质就是一种“异步处理”的思想。
    “消息队列”是在消息的传输过程中保存消息的容器。消息队列管理器将消息从它的源中继到它的目标时充当中间人的角色。队列主要提供跌幅并保证消息的传递;如果发送消息时接收者不可用,消息队列会保留消息直到成功传递。利用消息队列可以很好地异步处理数据传送和存储,当频繁地向数据中插入数据时就可采用消息队列异步插入,另外可将较慢的处理逻辑、有并发数量限制的处理逻辑,通过消息队列放在后台处理如视频转换发送手机短信等。
    消息发送很简单,在动态产生之后,直接在DB中插入一条记录即可,这里,完全依赖数据库来模拟实现消息队列。接收者从已获取的消息列表中取出下一条消息。
    在队列中存储的是消息,而不是实际要分发和处理的数据。秒杀队列中存储的仅仅是一个HTTP请求,SNS队列中存储的仅仅是一条制造出来的动态,而不是所有要分发的动态。动态的颁发不是消息队列所负责的,其由另一个程序处理。

  1. 用数据库模拟消息队列。新建一个消息队列表,结构如下:DROP TABLE IF EXIISTS 'eventqueue'; CREATE TABLE IF NOT EXISTS 'eventqueue' ('qid' int(11) NOT NULL AUTO_INCREMENT COMMENT '消息队列','topic' tinyint(4) NOT NULL COMMENT '应用类别','status' tinyint(4) NOT NULL COMMENT '状态,标识是否进行分发,0为未发1已分发','data' varchar(1024) COLLATE gbk_bin DEFAULT NULL COMMENT '消息内容','uid' int(11) NOT NULL COMMENT '动态产生者的uid','create_date' timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '发送时间',PRIMARY KEY('qid')) ENGINE=MEMORY DEFAULT CHARSET=gbk COLLATE=gbk_bin COMMENT='消息队列表,用以记录消息的属性及内容'
    上述结构中存放消息的产生者的是uid而不是具体的动态内容,data字段是消息的内容并非动态的内容,动态的内容存储在feed表中。消息队列队列有了,还需要一个对消息队列进行轮询高度的程序。对此在数据库中建立3个存储过程和一个定时器来实现模拟:
    存储过程名----------------------作用------------------说明
    Proc_msg_receiver_friend---润滑处理好友的动态分----从消息队列中获取消息,业务处理:给好友发动态
    Proc_msg_receiver_vip------接收并处理公共主页的动态分发---从消息队列中获取消息,业务处理:给关注者发消息
    msg_scheduler--------------调度程序-------------根据服务器访问的高峰期不同,调度消息接收者从消息队列中获取消息
    Proc_msg_cleaner-----------消息删除程序------------定期删除消息队列中已经接收的数据
    下面来看其中一个消息分发过程,这是通过一个存储过程实现的:
    DROP PROCEDURE IF EXISTS 'proc_msg_receiver_friend';
    DELIMITER //
    CREATE DEFINDER ='TEST'@'%' PROCEDURE 'proc_msg_receiver_friend'()
    COMMENT '消息队列中好友分发存储过程'
    BEGIN
    DECLARE CONTINUE HANDLER FOR SQL EXCEPTION ROLLBACK;
    START TRANSACTION;
    --好友动态分发
    INSERT INTO feed_broadcast
    SELECT feed_id,ef.fri_uid,temp.create_date,app_id,src_type
    FROM (SELECT * FROM eventqueue AS eq WHERE eq.status=0 AND eq.topic=1
    ORDER BY create_date DESC LIMIT 100) AS temp, friend as ef
    WHERE temp.uid=ef.uid;
    --更改动态消息状态
    UPDATE eventqueue AS eq SET status=1
    WHERE eq.status=0 AND eq.topic=1
    ORDER BY create_date DESC LIMIT 100;
    COMMIT
    END //
    DELIMITER ;
    有了消息和消息的分发,接下来需要通过调度决定分发的策略、时间和频率,利用MYSQL的event实现调度。
    下面是消息清除器的event:
    DROP EVENT IF EXISTS 'event_msg_cleaner';
    DELIMITER //
    CREATE EVENT 'event_msg_cleaner' ON SCHEDULE EVERY 1 HOUR STATTS '2016-03-02 00:00:00'
    也许看到这里觉得和常规实现没多大区别,只不过把页面上的摘取操作变为服务器上的推操作还麻烦了许多。但是,通过进制的高度把原先一瞬间可能发生的成千上万条动态分发的insert操作延迟了,并且是定制分发量,把高峰期的负载分一部分到低俗时间段进行处理,这样能减少很多负担。使用MYSQL的表来存储和处理消息队列确实不够快且会造成数据库的压力,但我们可以把消息队列放到以快著称的内存缓存中如Memcached.如果再配合上作业管理系统,这个分发机制 的策略还能更复杂,甚至可进行优先级管理。
    这样,队列的“异步操作”特点就体现出来了,对于VIP用户或者活跃用户优先分发,对“垃圾”用户延迟分发。通过指定消息的优先级就能分清轻重缓急,合理分摊服务器负担。
    队列的异步操作的优点:部署简单,公使用MYSQL的内置存储过程和定时器即可完成大部分调度,服务器部署也很简单,易迁移。
    队列的异步操作的缺点:对于大数理量、上千万的消息队列,即使使用数据库模拟消息队列也力不从心,另外这种方法实时性不高,对于动态分发、邮件、短信等实时性相对力低的应用可以采用这种方案进行处理,但是不能用于对实时性要求很高的场所。当消息很大时,可把消息放入内在或者进行集群分发。
    消息队列的应用场景取决于消息的重要性,如果是重要的消息,就必须一条一条的分发不允许丢弃。关于HTTP队列,金山公司内部有一开源作品HTTPSQL,其基于Tokyo Cabinet的B+treeKEY/VALUE数据库做数据的持久化存储。这个作品正是把这里的队列从MYSQL移到内存数据库中,并且由一个专门的进程负责队列的操作,客户端则有通过Socket请求进行读写。HTTPSQL和消息队列的实现思路是一样的,不过结合了NSQL,能更快,并支撑更高的并发量。
  2. https://blog.csdn.net/denglitong/article/details/62089783

猜你喜欢

转载自blog.csdn.net/m0_37477061/article/details/85160927
今日推荐