• nginx php-fpm swoole


    最近仔细翻阅了网上的相关文章,了解nginx、php-fpm的工作模式,cpu 进程及线程的切换(抢占模式、固定模式)。本文为自己学习理解
    先上大图
    在这里插入图片描述
    完整的请求模式如上图所示,用户从DNS服务器上通过域名获得服务器IP地址(中间可能产生的过程有DNS轮询和SLB负载均衡)。

    nginx

    并请求指定端口(http:80 https:443)

    此时就由master-nginx获取到这个请求

    然后将此请求下发给子进程work -nginx,只要有一个work-nginx获得到了这个请求的锁,那么其他的work-nginx将不能处理此请求,只有获得到这个请求的锁的work-nginx才能处理这个请求,获得锁的work-nginx会解析这个请求

    如果是访问静态资源(css、html、js、媒体文件)就直接返回相应的内容给用户,如果解析出此请求是一个php请求,就将此请求转发给master-php-fpm处理。(注意,当请求转发后,获得锁的work-nginx接着又可以去获得下一个请求的锁,而不用等待当前请求处理完成后才去处理下一个,这是因为nginx采用I/O复用模型

    当php-fpm处理完请求,响应报文的时候会触发事件,这个事件会通知获得该请求锁的work-nginx来处理,基于这样的原理,nginx可支撑的迸发量是很高的)。

    php-fpm

    当master-php-fpm 接收到来自nginx的转发请求时,会将此请求下发给子进程work-php-fpm,具体的程序运行也是在子进程 work-php-fpm上。

    • 每个work-php-fpm 都受master-php-fpm管理(管理内容有:创建、销毁、平滑重启等)。
    • 每个work-php-fpm 在处理一定数量后(由配置文件配置),都会重启,这是为了避免内存泄露
    • 同一时刻一个work-php-fpm只能处理一个请求,只有当当前的请求处理完毕后才可以去处理下一个请求
    • 当所有的work-php-fpm都处于工作状态时,如果此时还有新的请求加入,那么这些请求将会阻塞在master-php-fpm上,等待work-php-fpm进程处理完当前请求后,再将请求给此子进程。如果阻塞队列超出配置值,则会在master-php-fpm进程直接向nginx抛出异常。
    • 所有的work-php-fpm 均是使用抢占模式来处理请求,抢占模式:每个进程都可以得到一定CPU时间片的时间处理,时间片结束或是进程发生阻塞(数据库连接、网络请求I/O(如发送短信、邮件第三方接口)、读写文件、sleep等待)就会上下文切换进程去处理下一个进程的内容。等待阻塞结束再将进程由阻塞态加入就绪态队列(此时的进程处于就绪队列高等级),等待cpu调度执行
    • php-fpm 配置建议采用动态配置,闲时多少个工作进程,忙时最多多少个工作进程,能达到更高的处理性能,但也不能盲目的增加工作进程,需要结合CPU核数配置合适值,因为CPU在进程上下文切换时也是需要消耗资源的,当进程过多,CPU就一直在上下文切换上消耗资源,且前面已执行完时间片的php-fpm也不能快速的得到CPU调度(因为进程过多CPU还在后面调度其它进程的执行呢,哪有时间管你)

    我们平时在开发上需要注意的点

    • 如果一个接口的运行如果比较耗时,那么说明这个接口做的工作越多,越复杂,吞吐量不高。那么此时需要评估这个接口一般由谁调用,如果是由用户使用,那么就需要考虑是否可以拆分这个接口所做的工作。解决方案:
      1、队列执行:将这个接口的任务加入队列,等待队列执行成功后再通知用户。队列执行串行化,避免多个队列同时执行导致cpu性能开销大。
      2、协程:任务分段开多个线程执行。系统创建线程的开销比创建一个进程的开销小,因为同一个进程中线程间共享内存

    • 数据库死锁问题:我们在开发中应尽量避免在一个事务中锁住两行记录,举例,账户间的转账,需要同时锁住两个记录再进行资金转账。类似于这种的情况下我们要做好重试机制,当两个请求发生数据库事务死锁时,重试机制是一个补救的办法,如A锁住了记录a1,即将要去锁住记录b1,但B锁住了b1,即将要去锁住记录a1。此时A、B互斥了,所以产生了所等待,当A一旦事务超时,那么A就会发生回滚(回滚会释放a1的锁),此时B就拿到a1的锁继续执行后面的逻辑。当A回滚成功后,因重试机制又去重复执行,此时因B拿到了a1、b1的锁,只能等待B处理完成提交后(提交成功也会释放锁)才可以拿到锁跑逻辑

    • 进程阻塞:由前,我们知道php-fpm同一时刻只能处理一个请求,那么一旦这个请求发生了进程阻塞,CPU将会执行上下文切换去处理其他的进程。如果在高迸发时期,这个进程阻塞是要命的。原因:当所有的工作进程都处于阻塞态,那么新的请求进来只能在master-php-fpm的请求队列中等待,如果此时连master-php-fpm的队列都已经满了那么就只能给用户抛异常了。接口的吞吐量要高的话,一定要
      1、接口工作内容简单
      2、不易发生阻塞(一般最常见的就是I/O阻塞)

    为什么说swoole比php-fpm快

    针对这个问题我曽学习了swoole,并参考文献及做了测试。我使用的是异步风格的swoole http服务器,并在前端采用nginx代理,所有的php请求均由nginx代理转发至swoole来处理相应的php请求

    • 首先我们要了解swoole是常驻内存的。因为常驻所以就没有一个请求创建一个进程的这种开销所以快。且一旦进程创建后再次修改代码将不会生效,因为程序代码已经载入到了内存中,执行的直接跑就是,减少了编译次数。

    • swoole与php-fpm也有相似的地方,相似的地方如下:
      1、当某个进程处理了一定请求数后都会重启,目的是为了放置内存泄漏
      2、都是采用 master-work模式:即是由master-swoole监听某个端口,监听到请求来了以后,将此请求下发给work-swoole处理。
      3、每个work-swoole进程同一时刻只能处理一个请求

    • swoole和php-fpm不同之处:swoole在启动时,一旦定义了一个变量,那么这个变量在整个进程的生存周期中都存在,使用时直接从内存沿用这个对象。因为这个特性,所以每次请求来的时候,都不用再去重新定义框架的一些内容,如框架的启动组件这些统统不用,在work-swoole启动的时候这些工作已经做好了。

    • task任务进程,异步程序的实现,将一个复杂的任务拆分成多个小任务,并交给task任务去跑,极大提高了cpu的利用率。

    基于以上几点,所以swoole比php-fpm快。但是在使用的时候也有要注意的点。

    • 如果没有一定的把握,一定不要去操作全局静态变量(新增、删除、修改),这样可能会导致内存溢出
    • 数据库连接的问题,数据库连接是有超时时间的,当连接超时后就没有办法再操作数据库了,所以使用要考虑是否做一个数据库连接池实现,redis也同理。一般第三方服务如阿里云RDS、polarDB mysql 都有连接池的实现方案,我们php直接用就行了,如果不喜欢,我们直接用swoole做一个mysql连接池也可以,网上目前已有成熟的解决方案了。我们直接git下来直接跑就行。

    抢占模式、固定模式

    为什么说抢占模式的性能比固定模式的效率要高
    先举一个假设:以单核CPU为例,我们设计的某个restful api 接口所做的工作比较复杂,一个请求需要耗时3s才能完成,那么此时采用固定模式或是抢占模式让CPU来调度处理的话,会发生什么情况呢?(采用php-fpm)

    固定模式
    CPU的时间片会一直在这个进程上,会等待这个请求处理完成后才去处理下一个请求耗时3s,如果单位时间内,并发起来了,那么只有第一个请求会受到cpu的处理,其它的请求只能等待前面的请求处理完成后才能轮到我处理。从设计逻辑层面看上去没什么问题,但是对并发的性能不好。

    抢占模式
    CPU会经过一定算法(队列优先级)为每个进程分配一定的运行时间片,当一个进程的运行时间片结束,不管这个进程是否执行完成,都会切换上下文至下一个进程处理,当下一个进程处理一定时间片后,又继续切换上下文至下一个。一直到所有进程的工作任务结束。(当进程产生阻塞时,也会切换CPU的上下文,如I/O(数据库连接,读写,http请求,RPC等),sleep)这种模式下一个CPU可以同时处理多个任务。并发性能一下子就起来了。

    其实我个人挺喜欢用swoole的,运行速度快,提高机器性能的利用率,期待swoole可以给我们更好的程序开发体验。

  • 相关阅读:
    神经网络做预测的原理,神经网络预测空气质量
    D. Yet Another Problem
    前端中有序标签的使用
    【Java成王之路】EE初阶第十篇:(网络编程) 4
    每天一个设计模式之桥接模式
    BadNets: Identifying Vulnerabilities in the Machine Learning Model Supply Chain
    AI 场景存储优化:云知声超算平台基于 JuiceFS 的存储实践
    跟羽夏学 Ghidra ——工具
    c# 扩展类,扩展方法
    mysql之两阶段提交
  • 原文地址:https://blog.csdn.net/u014559227/article/details/125907653