PHP项目中并发处理

总结一下PHP项目中并发处理:

高并发业务处理的重点在于原子操作,常见操作:

  1. redis->incr
  2. 如果redis是单机,可以使用watch方案
    WATCH mykey
     val = GET mykey
     val = val + 1
     MULTI
     SET mykey $val
     EXEC
    
  3. 如果redis是集群,使用redis->setNX
    死锁问题:

    1. 上锁的 key 保存的是 unix 时间戳,假如 key 值的时间戳小于当前的时间戳,表示锁已经不再有效。
    2. C4 向 lock.foo 发送 SETNX 命令。因为崩溃掉的 C3 还锁着 lock.foo ,所以 Redis 向 C4 返回 0 。
    3. C4 向 lock.foo 发送 GET 命令,查看 lock.foo 的锁是否过期。如果不,则休眠(sleep)一段时间,并在之后重试。另一方面,如果 lock.foo 内的 unix 时间戳比当前时间戳老,C4 执行以下命令:
      GETSET lock.foo
      因为 GETSET 的作用,C4 可以检查看 GETSET 的返回值,确定 lock.foo 之前储存的旧值仍是那个过期时间戳,如果是的话,那么 C4 获得锁。
    4. 如果其他客户端,比如 C5,比 C4 更快地执行了 GETSET 操作并获得锁,那么 C4 的 GETSET 操作返回的就是一个未过期的时间戳(C5 设置的时间戳)。C4 只好从第一步开始重试。
    5. 即便 C4 的 GETSET 操作对 key 进行了修改,这对未来也没什么影响。
  1. 队列方案:每次pop一个出来,但是数量多的时候会造成hotkey ,时间复杂度与数量相关

项目中整理处理:

  1. 降级:根据业务判断降级内容及手段。
    降级的最终目的是保证核心服务可用,即使是有损的。而且有些服务是无法降级的(如加入购物车、结算)。
    梳理业务,可以参考日志级别设置预案:
    一般:比如有些服务偶尔因为网络抖动或者服务正在上线而超时,可以自动降级;
    警告:有些服务在一段时间内成功率有波动(如在95~100%之间),可以自动降级或人工降级,并发送告警;
    错误:比如可用率低于90%,或者数据库连接池被打爆了,或者访问量突然猛增到系统能承受的最大阀值,此时可以根据情况自动降级或者人工降级;
    严重错误:比如因为特殊原因数据错误了,此时需要紧急人工降级。

    降级按照是否自动化可分为:自动开关降级和人工开关降级。
    降级按照功能可分为:读服务降级、写服务降级。
    降级按照处于的系统层次可分为:多级降级。

    降级的功能点主要从服务端链路考虑,即根据用户访问的服务调用链路来梳理哪里需要降级:
    页面降级:在大促或者某些特殊情况下,某些页面占用了一些稀缺服务资源,在紧急情况下可以对其整个降级,以达到丢卒保帅;
    页面片段降级:比如商品详情页中的商家部分因为数据错误了,此时需要对其进行降级;
    页面异步请求降级:比如商品详情页上有推荐信息/配送至等异步加载的请求,如果这些信息响应慢或者后端服务有问题,可以进行降级;
    服务功能降级:比如渲染商品详情页时需要调用一些不太重要的服务:相关分类、热销榜等,而这些服务在异常情况下直接不获取,即降级即可;
    读降级:比如多级缓存模式,如果后端服务有问题,可以降级为只读缓存,这种方式适用于对读一致性要求不高的场景;
    写降级:比如秒杀抢购,我们可以只进行Cache的更新,然后异步同步扣减库存到DB,保证最终一致性即可,此时可以将DB降级为Cache。
    爬虫降级:在大促活动时,可以将爬虫流量导向静态页或者返回空数据,从而保护后端稀缺资源。
    参考《亿级流量网站架构核心设计》

  2. 熔断:关键在统一的api调用层,阈值设计。比如利用apcu,计算某个底层接口超时/返回错误的情况,假如在一个30s的周期内,失败次数超过n次,则不调用这个接口,直接返回某个约定值。

  3. 限流:基于请求、基于资源(cpu/..)
    限流的目的是通过对并发访问/请求进行限速或者一个时间窗口内的的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务(定向到错误页或告知资源没有了)、排队或等待(比如秒杀、评论、下单)、降级(返回兜底数据或默认数据,如商品详情页库存默认有货)。

    一般开发高并发系统常见的限流有:限制总并发数(比如数据库连接池、线程池)、限制瞬时并发数(如nginx的limit_conn模块,用来限制瞬时并发连接数)、限制时间窗口内的平均速率(如Guava的RateLimiter、nginx的limit_req模块,限制每秒的平均速率);其他还有如限制远程接口调用速率、限制MQ的消费速率。另外还可以根据网络连接数、网络流量、CPU或内存负载等来限流。

    先有缓存这个银弹,后有限流来应对618、双十一高并发流量,在处理高并发问题上可以说是如虎添翼,不用担心瞬间流量导致系统挂掉或雪崩,最终做到有损服务而不是不服务;限流需要评估好,不可乱用,否则会正常流量出现一些奇怪的问题而导致用户抱怨。

    限流算法
    常见的限流算法有:令牌桶、漏桶。计数器也可以进行粗暴限流实现。
    参考《亿级流量网站架构核心设计》

  4. 排队:排队模块、调度模块、服务模块