出现大量TIMEWAIT连接的排查与解决

Last-Modified: 2019年7月10日21:58:43 项目生产环境出现大量TIME_WAIT(数千个), 需要一一排查 先上总结: nginx 未开启 keep-alive 导致大量主动断开的tcp连接nginx 与 fastcgi(php-fpm) 的连接默认是短连接, 此时必然出现 TIME_WAIT状态确认统计TIME_WAIT 连接的本地地址 netstat -an | grep TIME_WAIT | awk '{print $4}' | sort | uniq -c | sort -n -k1# ... 前面很少的略过# 2 127.0.0.1:56420# 442 192.168.1.213:8080# 453 127.0.0.1:9000分析: 8080端口是nginx对外端口9000端口是php-fpm的端口8080 对外web端口经过确认, nginx 的配置文件中存在一行 # 不启用 keep-alivekeepalive_timeout 0;尝试抓取 tcp 包 tcpdump tcp -i any -nn port 8080 | grep "我的ip"# 其中某一次连接的输出如下# 20:52:54.647907 IP 客户端.6470 > 服务端.8080: Flags [S], seq 2369523978, win 64240, options [mss 1460,nop,wscale 8,nop,nop,sackOK], length 0# 20:52:54.647912 IP 服务端.8080 > 客户端.6470: Flags [S.], seq 1109598671, ack 2369523979, win 14600, options [mss 1460,nop,nop,sackOK,nop,wscale 7], length 0# 20:52:54.670302 IP 客户端.6470 > 服务端.8080: Flags [.], ack 1, win 256, length 0# 20:52:54.680784 IP 客户端.6470 > 服务端.8080: Flags [P.], seq 1:301, ack 1, win 256, length 300# 20:52:54.680789 IP 服务端.8080 > 客户端.6470: Flags [.], ack 301, win 123, length 0# 20:52:54.702935 IP 服务端.8080 > 客户端.6470: Flags [P.], seq 1:544, ack 301, win 123, length 543# 20:52:54.702941 IP 服务端.8080 > 客户端.6470: Flags [F.], seq 544, ack 301, win 123, length 0# 20:52:54.726494 IP 客户端.6470 > 服务端.8080: Flags [.], ack 545, win 254, length 0# 20:52:54.726499 IP 客户端.6470 > 服务端.8080: Flags [F.], seq 301, ack 545, win 254, length 0# 20:52:54.726501 IP 服务端.8080 > 客户端.6470: Flags [.], ack 302, win 123, length 0上述具体的ip已经被我批量替换了, 不方便暴露服务器ip分析: ...

July 10, 2019 · 3 min · jiezi

PHPFPM系列一次请求的过程

一、概要PHP-FPM启动后,master进程会陷入event_loop(0)中来管理维持worker进程,而fork出的worker进程会回到主函数开始循环接收、处理请求。一次请求可以总结为 请求接收、请求处理、请求结束 三个阶段,下面就详细来讲一下。 运行环境:Mac 10.14.2 + PHP 7.3.7 二、请求接收阶段对listen_socket加锁:因为accept()会有惊群问题,在调用accept()之前会对listen_socket加锁。惊群问题在Linux2.6版本中得到解决,内核在收到一个客户端连接时只会唤醒等待队列上的第一个进程。获取client_socket:worker进程会调用 accept(listen_socket, (struct sockaddr *)&sa, &len) 从全连接队列中接受一个连接,如果队列中暂时没有则会一直阻塞着,这里的listen_socket是在fcgi_listen()中创建监听的。判断client_socket是否被允许:满足如下请求之一即可 client_socket为unix_socket,表明客户端为本机客户端地址在allowed_clients列表里,allowed_clients是通过listen.allowed_clients参数配置等待client_socket上的可读事件发生:在do-while循环中调用poll()来监听client_socket上的可读事件,这里的while条件是while (ret < 0 && errno == EINTR); EINTR错误是当阻塞中的poll()被捕获到的信号中断所产生的错误,所以可以重新执行poll系统调用。读取client_socket中的数据:这里是对FastCGI协议的一个实现,Nginx会按照FastCGI协议的消息格式发送数据,worker进程再按照协议多次read()数据并解析,消息传递大致如下。关于PHP如何实现FastCGI协议可以看下这篇文章。 三、请求处理阶段初始化在上一阶段读取到请求数据后,worker进程接着会初始化输出相关的堆栈、初始化编译阶段用到的compiler_globals(CG宏)、执行阶段用到的executor_globals(EG宏)、执行每个扩展的PHP_RINIT_FUNCTION函数 等等。 ZendVM讲到请求处理阶段就不得不提ZendVM,大家都知道PHP是解释型语言,ZendVM就是PHP的解释器,负责PHP的解析、执行。计算机理解不了PHP代码,但是ZendVM可以,对PHP而言,ZendVM就像是真正的“计算机“,这台“计算机“可以识别的指令就是自己事先定义好的opcode。在运行时,PHP会被编译为一系列opcode指令,ZendVM会逐个调用opcode对应的机器指令,最终完成PHP代码的运行。 ZendVM运行过程词法语法分析,生成AST:这一步的目的是生成抽象语法树AST,AST是PHP7引入的概念,PHP7之前是在语法分析后就直接生成opcode了。在这过程中语法分析器yacc不断调用词法分析器re2c将PHP代码切割为token,然后yacc根据token组合匹配语法规则,最终生成AST。解析AST,生成zend_op_array:这一步的目的是生成zend_op_array,zend_op_array是编译后所有opline指令的集合,也包括编译期间生成的关键数据。对于ZendVM而言,zend_op_array就是可执行数据。ZendVM执行zend_op_array:zend_op_array作为ZendVM编译器的输出,也是ZendVM执行器的输入。执行时,ZendVM执行器会调用opcode相应的handler完成指令的处理,其中handler是每条opcode对应的C语言编写的处理逻辑。四、请求结束阶段执行用户通过register_shutdown_function()注册的关闭函数释放资源,清理符号表,销毁超全局变量,重置max_execution_time 等等冲刷掉所有缓冲区执行每个扩展的PHP_RSHUTDOWN_FUNCTION函数…...经过以上的清理操作,worker进程就准备好接收处理下一个请求了。

July 9, 2019 · 1 min · jiezi

Carbon —— PHP 中日期 / 时间处理,你只需要这个扩展包就够了

文章转自:https://learnku.com/php/t/26998在 PHP 中使用日期和时间并不是容易或清晰的任务。我们必须处理 strtotime ,格式化问题,大量计算等等。这个漂亮的包叫做 Carbon 可以帮助在 PHP 开发中处理日期/时间变得更加简单、更语义化,从而使得我们的代码更容易阅读和维护。CarbonCarbon 是由 Brian Nesbit 开发的一个包,它扩展了 PHP 自己的 DateTime 类。它提供了一些很好的功能来处理 PHP 中的日期,特别是诸如:处理时区轻松获取当前时间将 datetime 转换成可读的内容将英语短语解析成 datetime (first day of January 2016)日期的加减 (+ 2 weeks, -6 months)处理日期的语义方法所有的这些都带来了一个非常有用的包,使得这些在 PHP 中处理时间非常容易。设置为了使用 Carbon ,你需要从 Carbon 命名空间中导入 Carbon 。幸运的是,在 Laravel 中已经包括了 Carbon ,所以不需要和 Composer 一起添加。当我们需要使用 Carbon 的时候,我们可以这样导入它:<?phpuse Carbon\Carbon;在导入之后,让我们看看我们可以用这个很棒的包做一些很酷的事情。获取特定的日期/时间// 获取当前时间 - 2015-12-19 10:10:54$current = Carbon::now();$current = new Carbon();// 获取今天 - 2015-12-19 00:00:00$today = Carbon::today();// 获取昨天 - 2015-12-18 00:00:00$yesterday = Carbon::yesterday();// 获取明天 - 2015-12-20 00:00:00$tomorrow = Carbon::tomorrow();// 解析特定字符串 - 2016-01-01 00:00:00$newYear = new Carbon(‘first day of January 2016’);// 设定一个特定的时区 - 2016-01-01 00:00:00$newYearPST = new Carbon(‘first day of January 2016’, ‘America\Pacific’);创造具有更细粒度控制的日期除了快速定义日期/时间方法之外,Carbon 也可以让我们从特定数量的参数中创建时间。Carbon::createFromDate($year, $month, $day, $tz);Carbon::createFromTime($hour, $minute, $second, $tz);Carbon::create($year, $month, $day, $hour, $minute, $second, $tz);当你以一种通常不被 Carbon 识别的格式获得某种日期或时间时,这些是非常有用的。如果你为任何一个参数传递 null 值,则它默认会使用当前日期/时间传递 。操作日期/时间抓取日期/时间并不是你在处理日期时唯一要做的事情。你经常需要操作日期或时间。例如,当为一个用户创建一个试用期时,你将希望试用期在一定时间后过期。假设我们有 30 天的试用期。我们可以用 add 和 subtract 很容易的计算出时间。在这段试用期内,我们会:// 获取当前时间$current = Carbon::now();// 添加 30 天到当前时间$trialExpires = $current->addDays(30);从 Carbon 文档 中,我们可以找到一些其他的 add() 和 sub() 方法:$dt = Carbon::create(2012, 1, 31, 0);echo $dt->toDateTimeString(); // 2012-01-31 00:00:00echo $dt->addYears(5); // 2017-01-31 00:00:00echo $dt->addYear(); // 2018-01-31 00:00:00echo $dt->subYear(); // 2017-01-31 00:00:00echo $dt->subYears(5); // 2012-01-31 00:00:00echo $dt->addMonths(60); // 2017-01-31 00:00:00echo $dt->addMonth(); // 2017-03-03 00:00:00 equivalent of $dt->month($dt->month + 1); so it wrapsecho $dt->subMonth(); // 2017-02-03 00:00:00echo $dt->subMonths(60); // 2012-02-03 00:00:00echo $dt->addDays(29); // 2012-03-03 00:00:00echo $dt->addDay(); // 2012-03-04 00:00:00echo $dt->subDay(); // 2012-03-03 00:00:00echo $dt->subDays(29); // 2012-02-03 00:00:00echo $dt->addWeekdays(4); // 2012-02-09 00:00:00echo $dt->addWeekday(); // 2012-02-10 00:00:00echo $dt->subWeekday(); // 2012-02-09 00:00:00echo $dt->subWeekdays(4); // 2012-02-03 00:00:00echo $dt->addWeeks(3); // 2012-02-24 00:00:00echo $dt->addWeek(); // 2012-03-02 00:00:00echo $dt->subWeek(); // 2012-02-24 00:00:00echo $dt->subWeeks(3); // 2012-02-03 00:00:00echo $dt->addHours(24); // 2012-02-04 00:00:00echo $dt->addHour(); // 2012-02-04 01:00:00echo $dt->subHour(); // 2012-02-04 00:00:00echo $dt->subHours(24); // 2012-02-03 00:00:00echo $dt->addMinutes(61); // 2012-02-03 01:01:00echo $dt->addMinute(); // 2012-02-03 01:02:00echo $dt->subMinute(); // 2012-02-03 01:01:00echo $dt->subMinutes(61); // 2012-02-03 00:00:00echo $dt->addSeconds(61); // 2012-02-03 00:01:01echo $dt->addSecond(); // 2012-02-03 00:01:02echo $dt->subSecond(); // 2012-02-03 00:01:01echo $dt->subSeconds(61); // 2012-02-03 00:00:00 Getters and Setters另外一种快速操作或读取时间的方法是使用可用的 getters 和 serrers 。$dt = Carbon::now();// 设置一些参数$dt->year = 2015;$dt->month = 04;$dt->day = 21;$dt->hour = 22;$dt->minute = 32;$dt->second = 5;// 获取一些参数var_dump($dt->year);var_dump($dt->month);var_dump($dt->day);var_dump($dt->hour);var_dump($dt->second);var_dump($dt->dayOfWeek);var_dump($dt->dayOfYear);var_dump($dt->weekOfMonth);var_dump($dt->daysInMonth);我们甚至还可以把一些 setter 串在一起。$dt = Carbon::now();$dt->year(1975)->month(5)->day(21)->hour(22)->minute(32)->second(5)->toDateTimeString();$dt->setDate(1975, 5, 21)->setTime(22, 32, 5)->toDateTimeString();$dt->setDateTime(1975, 5, 21, 22, 32, 5)->toDateTimeString();格式化在上面的示例中,你可能注意到了 ->toDateTimeString() 方法。我们可以方便的为达到我们的目的去进行格式化。在这种情况下,我们得到了一个日期时间字符串。$dt = Carbon::now();echo $dt->toDateString(); // 2015-12-19echo $dt->toFormattedDateString(); // Dec 19, 2015echo $dt->toTimeString(); // 10:10:16echo $dt->toDateTimeString(); // 2015-12-19 10:10:16echo $dt->toDayDateTimeString(); // Sat, Dec 19, 2015 10:10 AM// ……当然 format() 也可以这样用echo $dt->format(’l jS \of F Y h:i:s A’); // Saturday 19th of December 2015 10:10:16 AM相对时间通过 diff() 方法可以很容易的显示相对时间。例如,我们有一篇博客,并且我们想显示它是在 三小时 前发布的。可以利用这些方法。求时间差这些方法用于求两个时间的时间差。$current = Carbon::now();$dt = Carbon::now();$dt = $dt->subHours(6);echo $dt->diffInHours($current); // -6echo $current->diffInHours($dt); // 6$future = $current->addMonth();$past = $current->subMonths(2);echo $current->diffInDays($future); // 31echo $current->diffInDays($past); // -62显示人类容易阅读的时间差在过去的几年,显示相对时间变得越来越流行。在 Twitter 和 Facebook 等社交网络中经常可以看到。例如,将时间显示为 3 小时前 比显示 上午 8:12,更适合人类阅读。这些方法被用于计算时间差,并转换为人类可阅读的格式。这里有四种表达时间差的方式:将一个过去的时间和现在做比较:1 小时前5 个月前将一个未来的时间和现在做比较:1 小时后5 个月后将一个过去的时间和另一个时间做比较:1 小时前5 小时前将一个未来的时间和另一个做比较:1 小时后5 小时后$dt = Carbon::now();$past = $dt->subMonth();$future = $dt->addMonth();echo $dt->subDays(10)->diffForHumans(); // 10 天前echo $dt->diffForHumans($past); // 1 个月前echo $dt->diffForHumans($future); // 1 个月前总结Carbon 能做的远远不止这些。请务必查看 Carbon 官方文档。希望这能帮助你在 PHP 中更容易的使用日期 / 时间并加快开发效率!文章转自:https://learnku.com/php/t/26998 更多文章:https://learnku.com/laravel/c… ...

April 12, 2019 · 3 min · jiezi

PHP-FPM 的管理和配置

PHP-FPM 是什么?PHP-FPM 是 FastCGI 的进程管理器。PHP-FPM 的特点支持平滑停止、启动的高级进程管理功能动、静态子进程产生慢日志,记录脚本运行所导致的异常缓慢可以监听不同的端口和使用不同的 php.iniPHP-FPM 的管理和配置PHP-FPM 管理master 主进程理解以下信号:INT,TERM 立刻终止QUIT 平滑终止USR1 重新打开日志文件USR2 平滑重载所有 worker 进程并重新载入配置和二进制模块# 查看 php-fpm 用户ps aux | grep php-fpm# 查看 php-fpm 进程 pidps -ef | grep php-fpm# 启动 php-fpmphp-fpm -D# 关闭 php-fpmkill -INT [pid]# 重启 php-fpmkill -USR2 [pid]ps 是展示当前进程的快照。PHP-FPM 配置/etc/php-fpm.d 文件夹中包含了所有进程池的配置,在主配置文件 /etc/php-fpm.conf 中有下面一行:include=/etc/php-fpm.d/*.confphp-fpm.conf 的主配置文件包含全局配置,子配置是不同进程池的配置,例如 www。全局配置# s 秒,m 分,h 小时,d 天# 子进程在 emergency_restart_interval 设定的时间内收到该参数设定次数的 SIGSEGV 或者 SIGBUS退出信息号,则FPM会重新启动emergency_restart_threshold 10# 平滑重启的间隔时间:emergency_restart_interval 1m# 子进程接受主进程复用信号的超时时间process_control_timeout 10s进程池配置# 接收 FastCGI 请求的地址listen: ‘ip:port’, ‘/path/to/unix/socket’# 设置进程管理器如何管理子进程pm: static 固定数量, ondemand 进程在有需求时才产生, dynamic 动态设置static 和 dynamic 配置内存比较少,并发量不是很大的应用,可以考虑采用 dynamic 的方式,这样可以控制 php-fpm 所消耗的总内存数。在并发高或者流量波动大的情况下,使用 static 可以在高并发下获得比 dynamic 更快的响应速度。可配置进程数量 = php-fpm 可配置内存 / (php-fpm 子进程的内存占用 * 1.2) ...

February 17, 2019 · 1 min · jiezi

深入了解SAPI

一、SAPI比较1. SAPI服务器应用程序编程接口,就是服务器与编程语言之间交互的接口。比如Linux命令行执行一段PHP代码,其实是Linux shell通过PHP SAPI传入一组参数,zend引擎执行后返回给shell。在PHP生命周期的各个阶段,一些与服务相关的操作都是通过SAPI接口实现。php_sapi_name()可以查看当前SAPI接口的类型。 如 cli(php -r “echo php_sapi_name();")、fpm-fcgi等2. PHP运行和加载的4个阶段①Minit 模块初始化阶段,可以初始化php扩展、类库的内部变量、注册常量,定义模块使用的类等。②Rinit 请求初始化阶段,在模块初始化并激活后,会创建PHP运行环境,初始化本次请求所需的环境变量,比如 $_SERVER,$_SESSION 。③Rshutdown 请求关闭阶段,执行最后的清理工作,释放所有处理本次请求的资源(申请的变量)。请求完成可能是执行到脚本完成,也可能是调用die()或exit()函数完成④Mshutdown 模块回收阶段,用于关闭自己的内核子系统,释放没存。3. SAPI 5种运行模式①单进程模式(CLI,CGI),每次执行PHP脚本,都会执行第二部分讲的四个INT和Shutdown事件。当用户请求数量非常多时,会大量挤占系统的资源如内存,CPU时间等,造成系统开销很大②多进程模式(Apache下的prefork MPM模式),会fork很多子进程,每个子进程拥有自己独立的进程地址空间,在一个子进程中,PHP的生命周期是调用MINT启动后,执行多次请求(RINT/RSHUTDOWN),在Apache关闭或进程结束后,才会调用MSHUTDOWN进行回收阶段。 多进程模型中,每个子进程都是独立运行,没有代码和数据共享,因此一个子进程终止退出和重新生成,不会影响其他子进程的稳定。③多线程模式(Apache2的Worker MPM),在一个进程下创建多个线程,在同一个进程地址空间执行④fastCGI模式,nginx+php-fpm就是这个模式,fast-cgi是CGI的升级版本,FastCGI可以看成是一个常驻型的CGI,它可以一直执行着,运行后可以fork多个进程,不用花费时间动态Fork子进程。也不需要每次请求都调用MINT/MSHUTDOWN。 ⑤内嵌模式,允许在C/C++语言中调用PHP提供的函数,运行模式和CGI一样,执行4个阶段二、php-fpm运行原理CGI:是个协议,服务器发起请求,传给PHP解析器,传递哪些数据,以什么格式,由CGI决定fastcgi:是个协议,提高CGI性能的,不用每次都去初始化,进程不够用,会预先启动几个进程,进程空闲太多了也会停掉一些,fastCGI对进程的管理,提高性能,节约了资源php-fpm:实现fastCGI协议的程序,被PHP官方收了,也提供了进程管理功能,进程包含 master 进程和 worker 进程两种进程。 master 进程只有一个,负责监听端口分发请求,接收来自 Web Server 的请求,而 worker 进程则一般有多个(具体数量根据实际需要配置),每个进程内部都嵌入了一个 PHP 解释器,是 PHP 代码真正执行的地方。php-cgi:cgi解释器进程FastCGI的工作原理:Web Server启动时载入FastCGI进程管理器FastCGI进程管理器自身初始化,启动多个CGI解释器(可见多个php-cgi)并等待来自Web Server的连接当客户端请求到达Web Server时,FastCGI进程管理器选择并连接到一个CGI解释器。Web Server将CGI环境变量和标准输入发送到FastCGI子进程php-cgiFastCGI子进程完成处理后将标准输出和错误信息从同一连接返回Web Server。当FastCGI子进程关闭连接时,请求便告知处理完成。FastCGI子进程接着等待并处理来自FastCGI继承管理器的下一个连接使用FastCGI,系统开销小。另外,对于数据库和Memcache的持续连接可以工作。数据库短连接connect:请求关闭阶段,释放请求所用的资源,数据库连接句柄也会被释放 数据库长连接pconnect:请求关闭后,PHP会收留此次连接,即使主动关闭也不会关闭而是收留,下次有打开相同连接的请求时,PHP直接把收留的句柄拿出来,省去建立连接的过程。php-fpm实现长连接也需要配合数据库一些配置,一个进程收留一个连接,数据库连接的数量就是子进程数量,所以数据库允许连接数就要大于子进程数。三、php-fpm进程管理的三种模式php-fpm支持三种运行模式,分别为static、ondemand、dynamic,默认为dynamic 。static : 静态模式,启动时分配固定的worker进程。只需要考虑max_children的数量,数量取决于cpu的个数和应用的响应时间。ondemand: 按需分配,启动时不分配任何进程,当收到用户请求时才启动进程。 master进程检查work进程的数量是否受限,是否有空闲的work进程,没有就新建work进程。在大流量的系统上master进程会变得繁忙,占用系统cpu资源,不适合大流量环境的部署。dynamic: 动态模式,启动时分配固定的进程。伴随着请求数增加,在设定的浮动范围调整worker进程。 pm = dynamic //动态进程管理,对于专用服务器,可以设置为static,静态一次性启动最大子进程数,不会变化。 pm.max_children = 50 //最大子进程数,ps aux可以查看 pm.start_servers = 20 //启动服务时会启动的进程数 pm.min_spare_servers = 5 //保证空闲子进程数的最小值,如果空闲进程小于这个值,php-fpm服务会创建新的子进程。 pm.max_spare_servers = 35 //保证空闲子进程数的最大值,如果空闲进程高于这个值,就进行清理。 pm.max_requests = 500 //定义一个子进程最多处理的请求数,达到这个值,进程自动退出。目的是为了控制内存溢出,使内存在一个可控范围内。但是如果设置的很小,有可能多个进程同时达到这个值,同时重启,就会导致PHP停止响应直到重启完毕。设置为0表示一直接受请求。 四、php-fpm慢日志如果一个php网站可以访问,就是访问速度变慢了,可以通过php-fpm的慢执行日志,清晰的了解到php的脚本哪里执行时间长,它可以定位到具体的代码行 vim /usr/local/php/etc/php-fpm.d/www.conf request_slowlog_timeout = 1 //超时时间 slowlog = /usr/local/php/var/log/www-slow.log 重启php-fpm /etc/init.d/php-fpm reload我在php文件中加了一行sleep(3);,运行之后返回结果参考文献1、https://www.jianshu.com/p/c9a… php-fpm进程管理的三种模式2、https://www.jb51.net/article/… SAPI的5种运行模式3、http://blog.51cto.com/1260661… php-fpm慢日志4、https://www.cnblogs.com/wpjam… php-fpm与mysql长连接 ...

January 30, 2019 · 1 min · jiezi

PHP-FPM 与 Nginx 的通信机制总结

PHP-FPM 介绍CGI 协议与 FastCGI 协议每种动态语言( PHP,Python 等)的代码文件需要通过对应的解析器才能被服务器识别,而 CGI 协议就是用来使解释器与服务器可以互相通信。PHP 文件在服务器上的解析需要用到 PHP 解释器,再加上对应的 CGI 协议,从而使服务器可以解析到 PHP 文件。由于 CGI 的机制是每处理一个请求需要 fork 一个 CGI 进程,请求结束再kill掉这个进程,在实际应用上比较浪费资源,于是就出现了CGI 的改良版本 FastCGI,FastCGI 在请求处理完后,不会 kill 掉进程,而是继续处理多个请求,这样就大大提高了效率。PHP-FPM 是什么PHP-FPM 即 PHP-FastCGI Process Manager, 它是 FastCGI 的实现,并提供了进程管理的功能。进程包含 master 进程和 worker 进程两种;master 进程只有一个,负责监听端口,接收来自服务器的请求,而 worker 进程则一般有多个(具体数量根据实际需要进行配置),每个进程内部都会嵌入一个 PHP 解释器,是代码真正执行的地方。Nginx 与 php-fpm 通信机制当我们访问一个网站(如 www.test.com)的时候,处理流程是这样的: www.test.com | | Nginx | |路由到www.test.com/index.php | |加载nginx的fast-cgi模块 | |fast-cgi监听127.0.0.1:9000地址 | |www.test.com/index.php请求到达127.0.0.1:9000 | | 等待处理…Nginx 与 php-fpm 结合在 Linux 上,Nginx 与 php-fpm 的通信有 tcp 和 unix socket 两种方式。tcp 的优点是可以跨服务器,当 Nginx 和 PHP-fpm 不在同一台机器上时,只能使用这种方式。Unix socket 又叫 IPC(inter-process communication 进程间通信) socket,用于实现同一主机上的进程间通信,这种方式需要在 nginx配置文件中填写 php-fpm 的 socket 文件位置。两种方式的数据传输过程如下图所示:二者的不同:由于 Unix socket 不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。所以其效率比 tcp 的方式要高,可减少不必要的 tcp 开销。不过,Unix socket 高并发时不稳定,连接数爆发时,会产生大量的长时缓存,在没有面向连接协议的支撑下,大数据包可能会直接出错不返回异常。而 tcp 这样的面向连接的协议,可以更好的保证通信的正确性和完整性。Nginx 与 php-fpm 结合只需要在各自的配置文件中做设置即可:1) Nginx 中的配置以 tcp 通信为例server { listen 80; #监听80端口,接收http请求 server_name www.test.com; #就是网站地址 root /usr/local/etc/nginx/www/huxintong_admin; # 准备存放代码工程的路径 #路由到网站根目录www.test.com时候的处理 location / { index index.php; #跳转到www.test.com/index.php autoindex on; } #当请求网站下php文件的时候,反向代理到php-fpm location ~ .php$ { include /usr/local/etc/nginx/fastcgi.conf; #加载nginx的fastcgi模块 fastcgi_intercept_errors on; fastcgi_pass 127.0.0.1:9000; # tcp 方式,PHP-fpm 监听的IP地址和端口 # fasrcgi_pass /usr/run/php-fpm.sock # unix socket 连接方式 }}2) php-fpm 的配置listen = 127.0.0.1:9000# 或者下面这样listen = /var/run/php-fpm.sock注意,在使用 Unix socket 方式连接时,由于 socket 文件本质上是一个文件,存在权限控制的问题,所以需要注意 Nginx 进程的权限与 php-fpm 的权限问题,不然会提示无权限访问。(在各自的配置文件里设置用户)通过以上配置即可完成 php-fpm 与 Nginx 的通信。 ...

January 28, 2019 · 1 min · jiezi

Linux安装二进制PHP7.2

通过性能评测,可以看出PHP7对性能进行了较大的优化,相比与PHP5.x有50%-150%的性能提升,因此,为了提升我们服务的响应速度,降低机器负载,需要进行版本升级。因为对二进制比较熟悉,所以没有用yum的方式进行安装,采用的二进制安装方式比较灵活,但是因为第一次安装PHP的高版本,也引入了很多的问题,总而言之,就是在错误中不断摸索错误,最终找到一个还能用的道路。下载PHP7.2官方下载地址:wget http://cn2.php.net/get/php-7.2.13.tar.bz2/from/this/mirror -O php-7.2.13.tar.bz2tar -xjvf php-7.2.13.tar.bz2// 用于后面编译的生成代码目录mkdir php7cd php-7.2.13配置PHPPHP编译前提供了大量的参数进行配置,包括支持的扩展、执行用户等,可以查看参数列表。我们进行最简单的配置,只支持php-fpm管理,因为我们的PHP是配合Ngnix来进行服务,因此还要指定执行的用户:./configure –prefix=/home/work/lnmp/php7 –enable-fpm –with-fpm-user=nginx –with-fpm-group=nginx我的第一次编译报错:configure: error: OpenSSL version 1.0.1 or greater required.解决这个问题,需要首先看自己的openssl的版本信息:$ openssl versionOpenSSL 1.0.0-fips 29 Mar 2010因此更新openssl版本:wget https://www.openssl.org/source/openssl-1.1.0j.tar.gztar -xzvf openssl-1.1.0j.tar.gzcd openssl-1.1.0j./config –prefix=/usr/local/ssl shared zlib-dynamicmakemake installmv /usr/bin/openssl /usr/bin/openssl1.0.0ln -s /usr/local/ssl/bin/openssl /usr/bin/openssl安装完毕再次配置依然报相同错误,因此我们需要手动指定openssl的位置:// 查看指定openssl的参数$./configure –help | grep openssl –with-openssl=DIR Include OpenSSL support (requires OpenSSL >= 1.0.1) –with-openssl-dir=DIR FTP: openssl install prefix –with-openssl-dir=DIR SNMP: openssl install prefix$ ./configure –prefix=/home/work/lnmp/php7 –enable-fpm –with-fpm-user=nginx –with-fpm-group=nginx –with-openssl=/usr/bin/openssl安装 make && make install 启动因为我是升级,所以原有Nginx和代码以及配置文件都是OK的状态,可能在这个阶段你会遇到不同的问题,这个得结合你的情况进行解决。cd php7// 复制php.ini和php-fpm.conf到etc/目录下,这个过程你也可以自己配置啊// 生成两个目录用于日志和sock文件保存mkdir logmkdir runsbin/php-fpm -c etc/php.ini -y etc/php-fpm.conf -p .启动成功,访问URL,报错:502 Bad Gateway502 Bad Gateway根据nginx的访问日志可以看出:$ cat error.log2018/12/14 10:54:18 [crit] 6260#0: *206 open() “./run/factcgi_temp/0000000015” failed (13: Permission denied) while reading upstream, client: 172.24.162.178, server: , request: “GET /oss/index.php HTTP/1.1”, upstream: “fastcgi://unix:run/phpfpm.sock:”, host: “xx.xx.com"查阅【资料1】【资料2】可以知道,在PHP老版本里,有一个bug,任何能够连接socket文件的用户可以通过它执行任何命令,特别是在Ubuntu系统里允许www-data用户执行任何代码。因此最新版本里修复了这个错误,但也导致我们出现了502的问题,因此我们需要配套升级我们的配置文件:// 在nginx.conf头部添加执行用户user www www;// 在php-fpm.conf里放弃注释这3行; Set permissions for unix socket, if one is used. In Linux, read/write; permissions must be set in order to allow connections from a web server. Many; BSD-derived systems allow connections regardless of permissions.; Default Values: user and group are set as the running user; mode is set to 0666listen.owner = wwwlisten.group = wwwlisten.mode = 0660重启nginx和php-fpm进程,依然报错:nginx: [emerg] getpwnam(“www”) failed因为我们没有加上这个用户:useradd -r www搞定,重启nginx和php-fpm进程,服务正常。总结使用二进制来安装PHP7.2,在编译的时候按需加载扩展,如果有问题,我们可以重新编译,也可以动态扩展。过程比较简单,但我的服务并没有正常服务,因为使用的Yii2.0不能够完美兼容PHP7,我还得对Yii2.0进行升级,以及对自身的代码进行升级。参考资料PHP7.2下载地址:http://php.net/downloads.phpPHP的性能演进:http://www.laruence.com/2016/…OpenSSl downloads:https://www.openssl.org/source/OpenSSL 安装、介绍:https://www.jianshu.com/p/291…Centos7 安装 PHP7最新版:https://www.jianshu.com/p/246…CentOS 7 Linux 安装PHP7.2 - 编译安装:https://blog.csdn.net/ai_zxc/…nginx error connect to php5-fpm.sock failed (13: Permission denied):https://stackoverflow.com/que…nginx安装 nginx: [emerg] getpwnam(“www”) failed 错误:https://blog.csdn.net/justdoi… ...

December 18, 2018 · 1 min · jiezi

Windows下PHP服务nginx不能使用file_get_contents的原因

注意:本文为转载,原文链接:Windows下PHP服务nginx不能使用file_get_contents/curl/fopen的原因!一、问题说明在Windows环境下搭建了一个本地开发服务环境,使用Nginx做服务,但是在使用file_get_contents()获取本地的链接时http://127.0.0.1/index.php,出现了这样的错误:file_get_contents(http://127.0.0.1/index.php) [<a href=‘function.file-get-contents’>function.file-get-contents</a>]: failed to open stream: HTTP request failed! 本地电脑php环境为:nginx+php+mysql;于是找到这篇文章做个笔记,记录下!这两天一直在搞windows下nginx+fastcgi的file_get_contents请求。我想,很多同学都遇到当file_get_contents请求外网的http/https的php文件时毫无压力,比如echo file_get_contents(‘http://www.baidu.com’) ,它会显示百度的页面。但当你请求localhost/127.0.0.1本地网络的php服务时却一直是timeout,无论你将请求时间和脚本运行时间多长都无法返回数据,如file_get_contents(‘http://localhost/phpinfo.php’) 。然而当你尝试请求html这样的静态文件时却完全没有问题。是什么原因呢?!首先,我们知道file_get_contents/curl/fopen打开一个基于tcp/ip的http请求时,请求数据发送到nginx,而nginx则委托给php-cgi(fastcgi)处理php文件,一般情况fastcgi处理完一个php请求后会马上释放结束信号,等待下一个处理请求(当然也有程序假死,一直占用资源的情况)。打开nginx.conf,我们看到下面这一行:location ~ .php { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME d:/www/htdocs$fastcgi_script_name; include fastcgi_params;}上面已经清楚地看到,所有使用php结尾的文件都经过fastcgi处理,而在php.ini的配置文件中也有一句:cgi.force_redirect = 1表明,所有php程序安全地强制转向交给cgi处理。但在windows中,本地127.0.0.1:9000怎样与php-cgi联系的呢?!答案是增加一个php-cgi进程,用它来监听127.0.0.1:9000。通过控制器命令:RunHiddenConsole.exe D:/www/php/php-cgi.exe -b 127.0.0.1:9000 -c C:/WINDOWS/php.ini我们就可以在启动windows时,开启一个php-cgi.exe进程监听来自127.0.0.1:9000 的请求。在dos命令下打开netstat –a就可以看到本地计算机下的9000端口处于listening状态(也就是空置,如果没有发送任何请求的话)。好了,该说说在php中使用file_get_contents()、curl()、fopen()函数访问localhost时为什么不能返回结果。我们再来试验在index.php中加入file_get_contents(‘http://127.0.0.1/phpinfo.php’) 语句向phpinfo.php发送一个请求,这时浏览器中的状态指示一直在打转,表示它一直在工作中。打开Dos中的netstat命令,可以看到本地的9000端口的状态为:ESTABLISHED,表示该进程在联机处理中。实际上,这里我们已经同时向nginx发送了两个基于http的php请求,一个是解析index.php,而另一个是phpinfo.php,这样矛盾就出来了,因为我们的windows系统只加载了一个http进程,因此,它无法同时处理两个php请求,它只能先处理第一个请求(index.php),而index.php却又在等待phpinfo.php处理结果,phpinfo.php没人帮它处理请求,因为它一直在等待index.php释放结束信号,因此,造成了程序的阻塞状态,陷入了死循环。所以我们就看到了浏览器的状态指示一直在打转。Curl()与fopen函数的原因也相同。二、解决方法找到了原因,我们也就有了解决办法。一是,向系统增加一个http请求,当一个php-cig内要加载另一个请求时,它能够分配其它http处理额外的php请求。这时需给另一个http sever分配不同的端口,比如8080。nginx的案例如下:http { server { listen 80; server_name 127.0.0.1; location / { index index.php; root /web/www/htdocs; } } server { listen 8080; server_name 127.0.0.1; location / { index index.html; root /web/www/htdocs; } } include /opt/nginx/conf/vhosts/php.conf; }这样,端口80与8080可以分别处理不同的程序,比如:test.php echo file_get_contents(‘http://localhost:8080/phpinfo.php’);当然,在*unix下有更多选择,比如fork。另外提醒下,网上有人说,通过去掉地址中的http://协议标记,而使用相对地址就规避函数的检查,实际情况是不是这样呢?!当在index.php中使用file_get_contents(‘phpinfo.php’); 时,我们可以看到函数输出了phpinfo.php的源代码,相当于file_get_contents(‘file:c:wwwphpinfo.php’); ,它实际上只是读取你的文本内容,因为file_get_contents()函数首先是处理file协议的,而curl则直接报错无法解析。因此这些人纯粹是不学无术的骗子。还有人提出修改hosts文件,增加localhost www.xxx.com影射关系,函数通过www.xxx.com访问本地php,这其实也是不治本的偏方,因为这只是方便计算机的dns解析,最终www.xxx.com交给127.0.0.1,而后者交给唯一http,还是阻塞。 ...

September 5, 2018 · 1 min · jiezi