• PHP消息队列学习


    在我们平常网站设计时,会遇到“给用户群发短信”,“商城订单系统大批量订单处理”,“商城秒杀活动”等需求,这些功能,都有一个共同的特点:就是在面对高迸发的同时,必须要保证系统处理数据的有效性。那么如何处理这些数据,“消息队列”就是很好的选择。

    接下来我们主要了解以下知识:

      1.队列是什么东西?它能做哪些事情?

      2.队列的应用场景有哪些?

      3.如何使用队列对业务进行解耦?

      4.如何使用redis队列来缓解系统压力?

    一、认识消息队列

    1.1 消息队列概念

      从本质上来说消息队列就是一个队列结构的中间件,就是说消息放入(即入队)这个中间件之后可以不马上处理,而等待另外一个程序进行处理读取(出队)这些消息数据,并且按照顺序逐次处理。也就是说你的遇到一个并发特别大而且耗时特别长,还不需要马上进程处理返回结果的功能时,可以使用消息队列解决此类问题。

    1.2 核心结构

    由一个业务系统进行入队,把消息逐次插入到消息队列中,插入成功之后直接返回成功结果,然后另一个消息处理系统,这个系统会把消息系统中的记录逐次读取出来并进行处理,完成出队流程。

     

    1.3 应用场景

      数据冗余:比如商城的订单系统,后续需要严格可靠的进行数据转换和记录,消息队列可以把这些订单数据持久化存储在队列中,如果队列中有订单数据,则后续处理程序进行获取,后续处理完之后将队列中与该条订单对应的元素删除,这样就能保证每条订单都能被有效处理,以下是一些消息队列常见的使用场景。

      系统解耦:软件开发尽量做到“高内聚,低耦合”,使用消息队列之后,可以做到入队系统和出队系统相互分开,互不影响,假如入队系统出现故障崩溃,出队系统不受影响,依然能够正常运作。

      流量削峰:例如商城秒杀和抢购,可以配合缓存来使用消息队列,能够有效顶住瞬间的高访问量,防止服务器因承受不住压力崩溃。

      异步通信:消息发送之后,如果消息入队成功之后可以直接返回发送成功。

      扩展性:商城订单队列,不仅可以处理订单,还可以给其他业务使用,比如下单成功之后,必然为其生成一条快递派送订单。

      排序保证:有些场景需要按照产品的顺序进行处理,比如单进单出从而保证数据按照一定的顺序处理,可以使用消息队列。

    1.4 常见队列的实现优缺点

      队列介质:

        1、数据库,比如mysql(可靠性高,容易实现,数据量大时速度慢)

        2、缓存,例如redis(速度快,单个消息报包过大时效率低)

        3、消息系统,例如rabbitMq(专业性强,可靠,学习成本高)

      消息处理触发机制:

        1、死循环方式读取:易实现,故障时无法及时恢复。(比较适合做秒杀,比较集中,运维集中维护)

        2、定时任务:压力均分,有处理上限;可选择用户访问量低时段减小服务器压力;目前比较流行的处理触发机制。(唯一缺点是间隔和数据需要注意,不要等上一个任务还没有完成下一个任务又开始了)

        3.守护进程:类似于php-fpm和php-cg,需要shell基础

    二、解耦案例:队列处理“订单系统”和“配送系统”

      对于订单流程,我们可以设计两个系统,即“订单系统”和“配送系统”。在网购中我们都知道,当我们下单之后,在我的订单中可以看到我的货物正在配送中,这时就要参与进来一个“配送系统”。如果在架构时把“订单系统”和“配送系统”设计在一起,就会出现一些问题。对于订单系统来说,因为系统压力较大,但是“配送系统”却没有必要即时做出反应;第二个,我们不希望在订单系统出现故障时,配送系统也无法运行,这个时候两个系统就会相互影响正常运转。所以我们要把两个系统的耦合度降到最低。这个可以用“对列表”来实现两者之间的沟通。

    2.1 架构设计

      1、首先订单系统接收到用户的订单,然后进行订单的处理。

      2、然后会把这些订单信息写到对列表中,这个对列表是沟通订单系统和订单系统的关键。

      3、由配送系统定时执行的一个程序来读取队列进行处理。

      4、配送系统处理之后,会把已处理的记录进行标记处理

    2.2 程序流程

    三、流量消峰案例:Redis 的 list 类型实现秒杀/抢购活动

      redis 基于内存,它的速度非常快,redis对数据库有一个非常好的补充作用,因为它是可持久化的,redis会周期性把数据写到硬盘中,因此它不需要担心断电问题,redis提供了5种不同的数据类型(字符串,双向表链,哈希,集合,有序集合)。

      一般情况下,做秒杀案例,抢购,瞬间高并发,需要排队的案例中,redis是一个很好的选择。

    3.1 redis数据类型中的list类型

      redis 的list 是一个双向链表,可以从头部或者尾部追加数据。

      * LPUSH/LPUSHX :将值插入到(/存在的)列表头部

      * RPUSH/RPUSHX: 将值插入到(/存在的)列表尾部

      * LPOP : 移除并获取列表的第一个元素

      * RPOP: 移除并获取列表的最后一个元素

      * LTRIM: 保留指定区间内的元素

      * LLEN: 获取列表长度

      * LSET: 通过索引设置列表元素的值

      * LINDEX: 通过索引获取列表中的元素

      * LRANGE: 获取列表指定范围内的元素

    3.2 架构设计

      

      1、首先记录哪一个用户参与了秒杀同时记录他的时间。

      2、将用户的id存到redis列表中,让它排队。如果规定只有前10个用户可以参与活动,如果列表中个数已经够了就不会让它继续插入数据。这样redis的列表长度就为10个。

      3、最后慢慢的将redis中的数据写入到数据库中,以减小数据的压力。

    3.3 代码初级设计

      1、当用户开始参与秒杀时,将秒杀程序的请求写入Redis(uid, time)中。

      2、假如只有10个人可以秒杀成功,检查Redis已经存放数据的长度,达到上限后不再插入,说明秒杀已完成。

      3、最后循环处理存入Redis中的10条数据,然后慢慢取数据存入到数据库中。

    在秒杀这一块对数据库压力特别大,如果我们直接在用户发起秒杀请求时,每次都查询数据库是否已经达到秒杀人数上限的话,会造成数据库压力巨大。现在通过Redis的一个队列list,然后把秒杀请求放入到Redis里面,最后将秒杀成功的数据通过入库程序写入到数据库,这样的话会极大缓解mysql的压力。

    本文参考来自:https://www.cnblogs.com/dump/p/8243868.html

  • 相关阅读:
    idea设置编译版本
    java.lang.NoClassDefFoundError: Could not initialize class com.github.pagehelper.util.MetaObjectUtil
    Incorrect string value: 'xF0x9Fx92x98xF0x9F...'
    mybatis%_查询
    Executor.query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.sessi
    git 查看当前仓库地址以及设置新的仓库地址
    C#-Linq-使用Linq实现SQL的全连接
    C#-Linq-使用Linq实现SQL的全连接
    JavaScript中匿名函数this指向问题
    Vue设置导航栏为公共模块并在登录页不显示
  • 原文地址:https://www.cnblogs.com/be-thebest/p/9988827.html
Copyright © 2020-2023  润新知