经常有社区的同学问: “我的php程序有没有阻塞,我的php程序有没有开启协程(对自己写好的代码表示不自信),我的php程序有没有问题”.然后贴出了自己的程序,然后进入了愉快的灌水环节,随着时间的流逝,我们并没有形成系统的解决方法.求人不如求己,我们自己来解决这些问题.
最近接触到一个牛x的名词-方法论.
引用了百度百科一段名词解释
方法论,就是关于人们认识世界、改造世界的方法的理论。
它是人们用什么样的方式、方法来观察事物和处理问题。概括地说,世界观主要解决世界“是什么”的问题,方法论主要解决“怎么办”的问题。
方法论是一种以解决问题为目标的理论体系或系统,通常涉及对问题阶段、任务、工具、方法技巧的论述。方法论会对一系列具体的方法进行分析研究、系统总结并最终提出较为一般性的原则。
方法论也是一个哲学概念。人们关于“世界是什么、怎么样”的根本观点是世界观。用这种观点作指导去认识世界和改造世界,就成了方法论。 方法论是普遍适用于各门具体社会科学并起指导作用的范畴、原则、理论、方法和手段的总和。历史唯物主义的著作中经常提到方法论这个概念。
我们php程序员是不是也可以有一个方法,解决一些很容易碰到的基础问题.借这个平台抛砖引玉,希望大家多多指教.
还是引用一个牛x的命题,我是谁,从哪来,到哪去?我们也来扪心三连问
这是一个几千年来无数人探索的问题.
php是一个解释型的语言,我们首先要上来看看自己的执行的php在哪里,是什么,以免在陌生的机器上一顿操作猛如虎才发现没对上号,比如笔者的机器上装了好多个版本的php.不要笑话这个问题,真的有不少人工作几年忽略了这个问题.直到他某一天在这个上面浪费了两小时….(逃
1 $ which php2 /home/shiguangqi/bin/php3 $ ll /home/shiguangqi/bin/php4 lrwxrwxrwx 1 shiguangqi shiguangqi 30 10月 25 12:09 /home/shiguangqi/bin/php -> /usr/local/php-7.2.14//bin/php*
php安装的版本,加载了什么配置文件,php的编译参数是什么,有没有你关注的扩展被加载,加载的配置是什么版本等一系列问题.
比如我这里是把不同版本的php对应到自己的目录的一个软连上,自由切换不同版本
1 $ php -v2 php 7.2.14 (cli) (built: jul 4 2019 11:02:01) ( nts debug )3 copyright (c) 1997-2018 the php group4 zend engine v3.2.0, copyright (c) 1998-2018 zend technologies
加载什么配置
1 $ php --ini2 configuration file (php.ini) path: /usr/local/php-7.2.14/lib3 loaded configuration file: /usr/local/php-7.2.14/lib/php.ini4 scan for additional .ini files in: (none)5 additional .ini files pard: (none)
编译参数, 我这个版本是debug版本,默认静态编译了这些模块.
1 $ php -i | grep configure2 configure command => './configure' '--prefix=/usr/local/php-7.2.14' '--with-config-fi动听近义词le-path=/usr/local/php-7.2.14/lib' '--with-mysqli=mysqlnd' '--with-jpeg-dir' '--with-png-dir' '--with-iconv-dir' '--with-freetype-dir学生免费一卡二卡三卡9' '--with-zlib' '--with-mhash' '--with-libxml-dir' '--with-curl' '--with-mcrypt' '--with-gd' '--with-openssl' '--enable-ftp' '--enable-xml' '--enable-bcmath' '--enable-gd-native-ttf' '--enable-mbregex' '--enable-mbstring' '--enable-pcntl' '--enable-sockets' '--enable-fpm' '--enable-soap' '--enable-zip' '--enable-debug'
查看php扩展相关, 其实可以通过php --help
查看更多的用法.
比如大家常用的
php --ri swoole
以目标为导向
这一部分对于我们来说最熟悉不过,我们每天干的就是这个事情.
设计服务之前要心中要蓝图,最好把90%的时间用来想,10%的时间用来写.不仅犯错少,而且质量高.
比如我要查询redis,然后写了两个最简单的代码
1 <?php 2 $redis = new redis; 3 $redis->connect("127.0为什么白带是褐色的.0.1", 6379); 4 $var = $redis->get("key"); 5 var_dump($var); 6 <?php 7 swoole\runtime::enablecoroutine(); 8 go(function () { 9 $redis = new redis;10 $redis->connect("127.0.0.1", 6379);11 $var = $redis->get("key");12 var_dump($var);13 });
然后我们愉快的得出了同样的结果.
以目标为导向,以结果为标准.
得出结果很容易,问题是我们干的质量怎么样呢,上面的例子只是类比,大家不要耻笑这两个例子太简单.
strace 将是我们有帮手
我们可以这样,看到核心业务的系统调用
1 strace -s 1000 php redis.php 2 socket(af_inet, sock_stream, ipproto_ip) = 3 3 fcntl(3, f_getfl) = 0x2 (flags o_rdwr) 4 fcntl(3, f_tfl, o_rdwr|o_nonblock) = 0 5 connect(3, {sa_family=af_inet, sin_port=htons(6379), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 einprogress (operation now in progress) 6 poll([{fd=3, events=pollin|pollout|pollerr|pollhup}], 1, 60000) = 1 ([{fd=3, revents=pollout}]) 7 getsockopt(3, sol_socket, so_error, [0], [4]) = 0 8 fcntl(3, f_tfl, o_rdwr) = 0 9 tsockopt(3, sol_tcp, tcp_nodelay, [1], 4) = 010 tsockopt(3, sol_socket, so_keepalive, [0], 4) = 011 poll([{fd=3, events=pollin|pollpri|pollerr|pollhup}], 1, 0) = 0 (timeout)12 ndto(3, "*2\r\n$3\r\nget\r\n$3\r\nkey\r\n", 22, msg_dontwait, null, 0) = 2213 poll([{fd=3, events=pollin|pollpri|pollerr|pollhup}], 1, 0) = 0 (timeout)14 poll([{fd=3, events=pollin|pollerr|pollhup}], 1, 60000) = 1 ([{fd=3, revents=pollin}])15 recvfrom(3, "$6\r\nvalue2\r\n", 8192, msg_dontwait, null, null) = 1216 write(1, "string(6) \"", 11string(6) ") = 1117 write(1, "value2", 6value2) = 6
我们还可以加上一些参数,得到更详细的数据,当前系统调用的消耗的时间和当前时间戳
1 $ strace -ttt -t -s 1000 php redis.php 2 1572266793.054360 socket(af_inet, sock_stream, ipproto_ip) = 3 <0.000013> 3 157226视力多少才算正常6793.054389 fcntl(3, f_getfl) = 0x2 (flags o_rdwr) <0.000007> 4 1572266793.054414 fcntl(3, f_tfl, o_rdwr|o_nonblock) = 0 <0.000008> 5 1572266793.054438 connect(3, {sa_family=af_inet, sin_port=htons(6379), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 einprogress (operation now in progress) <0.000070> 6 1572266793.054533 poll([{fd=3, events=pollin|pollout|pollerr|pollhup}], 1, 60000) = 1 ([{fd=3, revents=pollout}]) <0.000008> 7 1572266793.054565 getsockopt(3, sol_socket, so_error, [0], [4]) = 0 <0.000008> 8 1572266793.054594 fcntl(3, f_tfl, o_rdwr) = 0 <0.000006> 9 1572266793.054619 tsockopt(3, sol_tcp, tcp_nodelay, [1], 4) = 0 <0.000008>10 1572266793.054644 tsockopt(3, sol_socket, so_keepalive, [0], 4) = 0 <0.000007>11 1572266793.054689 poll([{fd=3, events=pollin|pollpri|pollerr|pollhup}], 1, 0) = 0 (timeout) <0.000007>12 1572266793.054717 ndto(3, "*2\r\n$3\r\nget\r\n$3\r\nkey\r\n", 22, msg_dontwait, null, 0) = 22 <0.000030>13 1572266793.054788 poll([{fd=3, events=pollin|pollpri|pollerr|pollhup}], 1, 0) = 1 ([{fd=3, revents=pollin}]) <0.000007>14 1572266793.054816 recvfrom(3, "$", 1, msg_peek, null, null) = 1 <0.000009>15 1572266793.054851 poll([{fd=3, events=pollin|pollerr|pollhup}], 1, 60000) = 1 ([{fd=3, revents=pollin}]) <0.000007>16 1572266793.054878 recvfrom(3, "$6\r\nvalue2\r\n", 8192, msg_dontwait, null, null) = 12 <0.000009>17 1572266793.054923 write(1, "string(6) \"", 11string(6) ") = 11 <0.000010>18 1572266793.054951 write(1, "value2", 6value2) = 6 <0.000009>19 1572266793.054977 write(1, "\"\n", 2"
我们就可以逐行查看程序系统调用的详情.很容易发现,这个程序是阻塞的,虽然每个系统调用的时间极短.但换个业务,换个依赖可就不是这样.但万变不离其宗.
我们再来看第二个程序,也就是开启协程后的系统调用
1 1572266985.463011 socket(af_inet, sock_stream|sock_cloexec, ipproto_ip) = 4 <0.000016> 2 1572266985.463048 fcntl(4, f_getfl) = 0x2 (flags o_rdwr) <0.000006> 3 1572266985.463070 fcntl(4, f_tfl, o_rdwr|o_nonblock) = 0 <0.000006> 4 1572266985.463092 tsockopt(4, sol_tcp, tcp_nodelay, [1], 4) = 0 <0.000007> 5 1572266985.463127 tsockopt(4, sol_tcp, tcp_nodelay, [1], 4) = 0 <0.000005> 6 1572266985.463167 connect(4, {sa_family=af_inet, sin_port=htons(6379), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 einprogress (operation now in progress) <0.000076> 7 1572266985.463296 epoll_ctl(3, epoll_ctl_add, 4, {epollout, {u32=4, u64=38654705668}}) = 0 <0.000011> 8 1572266985.463353 epoll_wait(3, [{epollout, {u32=4, u64=38654705668}}], 4096, 60000) = 1 <0.000007> 9 1572266985.463383 epoll_ctl(3, epoll_ctl_del, 4, null) = 0 <0.000008>10 1572266985.463407 getsockopt(4, sol_socket, so_error, [0], [4]) = 0 <0.000008>11 1572266985.463445 tsockopt(4, sol_tcp, tcp_nodelay, [1], 4) = 0 <0.一件事作文300字000007>12 1572266985.463467 tsockopt(4, sol_socket, so_keepalive, [0], 4) = 0 <0.000007>13 1572266985.463525 recvfrom(4, 0x7f29bb9b033d, 1, msg_peek, null, null) = -1 eagain (resource temporarily unavailable) <0.000008>14 1572266985.463556 ndto(4, "*2\r\n$3\r\nget\r\n$3\r\nkey\r\n", 22, 0, null, 0) = 22 <0.000033>15 1572266985.463633 recvfrom(4, 0x7f29bb9b033d, 1, msg_peek, null, null) = -1 eagain (resource temporarily unavailable) <0.000006>16 1572266985.463662 recvfrom(4, "$6\r\nvalue2\r\n", 8192, 0, null, null) = 12 <0.000007>17 1572266985.463713 write(1, "string(6) \"", 11string(6) ") = 11 <0.000009>18 1572266985.463740 write(1, "value2", 6value2) = 6 <0.000008>
可以发现,通过创建socket,然后connect操作,然后通过epoll_ctl加入监听,是通过多路复用的方式,这里是没有阻塞等待的,我们可以通过这种方式明确的确认自己的程序细节.
比如,可以从第八行看到这里超时等待的时间是60s
.
然后我们修改代码超时时间为0.5s
1 <?php2 swoole\runtime::enablecoroutine();3 go(function () {4 $redis = new redis;5 $redis->connect("127.0.0.1", 6379, 0.5);6 $var = $redis->get("key");7 var_dump($var);8 });
然后继续查看
1 1572267648.851924 socket(af_inet, sock_stream|sock_cloexec, ipproto_ip) = 4 <0.000011> 2 1572267648.851952 fcntl(4, f_getfl) = 0x2 (flags o_rdwr) <0.000004> 3 1572267648.851968 fcntl(4, f_tfl, o_rdwr|o_nonblock) = 0 <0.000004> 4 1572267648.851986 tsockopt(4, sol_tcp, tcp_nodelay, [1], 4) = 0 <0.000005> 5 1572267648.852011 tsockopt(4, sol_tcp, tcp_nodelay, [1], 4) = 0 <0.000006> 6 1572267648.852042 connect(4, {sa_family=af_inet, sin_port=htons(6379), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 einprogress (operation now in progress) <0.000058> 7 1572267648.852131 epoll_ctl(3, epoll_ctl_add, 4, {epollout, {u32=4, u64=38654705668}}) = 0 <0.000006> 8 1572267648.852164 epoll_wait(3, [{epollout, {u32=4, u64=38654705668}}], 4096, 500) = 1 <0.000006> 9 1572267648.852184 epoll_ctl(3, epoll_ctl_del, 4, null) = 0 <0.000005>10 1572267648.852201 getsockopt(4, sol_socket, so_error, [0], [4]) = 0 <0.000006>11 1572267648.852225 tsockopt(4, sol_tcp, tcp_nodelay, [1], 4) = 0 <0.000058>12 1572267648.852302 tsockopt(4, sol_socket, so_keepalive, [0], 4) = 0 <0.000006>13 1572267648.852350 recvfrom(4, 0x7f71f91b033d, 1, msg_peek, null, null) = -1 eagain (resource temporarily unavailable) <0.000006>14 1572267648.852373 ndto(4, "*2\r\n$3\r\nget\r\n$3\r\nkey\r\n", 22, 0, null, 0) = 22 <0.000029>15 1572267648.852446 recvfrom(4, "$", 1, msg_peek, null, null) = 1 <0.000011>16 1572267648.852486 recvfrom(4, "$6\r\nvalue2\r\n", 8192, 0, null, null) = 12 <0.000006>17 1572267648.852523 write(1, "string(6) \"", 11string(6) ") = 11 <0.000008>18 1572267648.852543 write(1, "value2", 6value2) = 6 <0.000006>
然后第8行可以发现,超时的时间变为500ms, 这样可以发现一些简单,但是非常重要的细节.同样第一个例子的超时时间也可以阅读到.
运行中的进程可以通过 strace -p pid
来查看详情.
关于strace
更详细的用法可以 man strace
来查看.
如果我们的项目很复杂,怎么定位自己运行时问题,阻塞等待时间太长,死循环等问题.其实也是利用strace
工具,可以先结合top
来查看系统的负载,以及进程占用资源.常见的也就是内存和cpu.vmstat
也是非常好的工具.
当然也有全自动化的工具,比如swoole tracker
深入php内核,可以全自动检查阻塞,甚至是内存泄漏.当然也可以自己写脚本来实现.
常用的工具还有gdb
,下一篇我们一起来实验gdb
工具对php
程序员的巨大作用.
本文发布于:2023-04-07 21:55:26,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/cf9dbf0edd0cfd50705b84e455207124.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:PHP程序员.doc
本文 PDF 下载地址:PHP程序员.pdf
留言与评论(共有 0 条评论) |