PHP程序员-常用工具
三连问
经常有社区的同学问: “我的PHP程序有没有阻塞,我的PHP程序有没有开启协程(对自己写好的代码表示不自信),我的PHP程序有没有问题”.然后贴出了自己的程序,然后进入了愉快的灌水环节,随着时间的流逝,我们并没有形成系统的解决方法.求人不如求己,我们自己来解决这些问题.
方法论
最近接触到一个牛X的名词-方法论.
引用了百度百科一段名词解释
方法论,就是关于人们认识世界、改造世界的方法的理论。
它是人们用什么样的方式、方法来观察事物和处理问题。概括地说,世界观主要解决世界“是什么”的问题,方法论主要解决“怎么办”的问题。
方法论是一种以解决问题为目标的理论体系或系统,通常涉及对问题阶段、任务、工具、方法技巧的论述。方法论会对一系列具体的方法进行分析研究、系统总结并最终提出较为一般性的原则。
方法论也是一个哲学概念。人们关于“世界是什么、怎么样”的根本观点是世界观。用这种观点作指导去认识世界和改造世界,就成了方法论。 方法论是普遍适用于各门具体社会科学并起指导作用的范畴、原则、理论、方法和手段的总和。历史唯物主义的著作中经常提到方法论这个概念。
我们PHP程序员是不是也可以有一个方法,解决一些很容易碰到的基础问题.借这个平台抛砖引玉,希望大家多多指教.
还是引用一个牛X的命题,我是谁,从哪来,到哪去?我们也来扪心三连问
- 我是谁
- 我要干啥
- 干的咋样
我是谁
这是一个几千年来无数人探索的问题.
PHP是一个解释型的语言,我们首先要上来看看自己的执行的PHP在哪里,是什么,以免在陌生的机器上一顿操作猛如虎才发现没对上号,比如笔者的机器上装了好多个版本的PHP.不要笑话这个问题,真的有不少人工作几年忽略了这个问题.直到他某一天在这个上面浪费了两小时….(逃
$ which php
/home/shiguangqi/bin/php
$ ll /home/shiguangqi/bin/php
lrwxrwxrwx 1 shiguangqi shiguangqi 30 10月 25 12:09 /home/shiguangqi/bin/php -> /usr/local/php-7.2.14//bin/php*
PHP安装的版本,加载了什么配置文件,PHP的编译参数是什么,有没有你关注的扩展被加载,加载的配置是什么版本等一系列问题.
比如我这里是把不同版本的PHP对应到自己的目录的一个软连上,自由切换不同版本
$ php -v
PHP 7.2.14 (cli) (built: Jul 4 2019 11:02:01) ( NTS DEBUG )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
加载什么配置
$ php --ini
Configuration File (php.ini) Path: /usr/local/php-7.2.14/lib
Loaded Configuration File: /usr/local/php-7.2.14/lib/php.ini
Scan for additional .ini files in: (none)
Additional .ini files parsed: (none)
编译参数, 我这个版本是debug版本,默认静态编译了这些模块.
$ php -i | grep configure
Configure Command => './configure' '--prefix=/usr/local/php-7.2.14' '--with-config-file-path=/usr/local/php-7.2.14/lib' '--with-mysqli=mysqlnd' '--with-jpeg-dir' '--with-png-dir' '--with-iconv-dir' '--with-freetype-dir' '--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,然后写了两个最简单的代码
<?php
$redis = new Redis;
$redis->connect("127.0.0.1", 6379);
$var = $redis->get("key");
var_dump($var);
<?php
Swoole\Runtime::enableCoroutine();
go(function () {
$redis = new Redis;
$redis->connect("127.0.0.1", 6379);
$var = $redis->get("key");
var_dump($var);
});
然后我们愉快的得出了同样的结果.
干的咋样
以目标为导向,以结果为标准.
得出结果很容易,问题是我们干的质量怎么样呢,上面的例子只是类比,大家不要耻笑这两个例子太简单.
strace 将是我们有帮手
我们可以这样,看到核心业务的系统调用
strace -s 1000 php redis.php
socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3
fcntl(3, F_GETFL) = 0x2 (flags O_RDWR)
fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0
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)
poll([{fd=3, events=POLLIN|POLLOUT|POLLERR|POLLHUP}], 1, 60000) = 1 ([{fd=3, revents=POLLOUT}])
getsockopt(3, SOL_SOCKET, SO_ERROR, [0], [4]) = 0
fcntl(3, F_SETFL, O_RDWR) = 0
setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_KEEPALIVE, [0], 4) = 0
poll([{fd=3, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)
sendto(3, "*2\r\n$3\r\nGET\r\n$3\r\nkey\r\n", 22, MSG_DONTWAIT, NULL, 0) = 22
poll([{fd=3, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout)
poll([{fd=3, events=POLLIN|POLLERR|POLLHUP}], 1, 60000) = 1 ([{fd=3, revents=POLLIN}])
recvfrom(3, "$6\r\nvalue2\r\n", 8192, MSG_DONTWAIT, NULL, NULL) = 12
write(1, "string(6) \"", 11string(6) ") = 11
write(1, "value2", 6value2) = 6
我们还可以加上一些参数,得到更详细的数据,当前系统调用的消耗的时间和当前时间戳
$ strace -ttt -T -s 1000 php redis.php
1572266793.054360 socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 3 <0.000013>
1572266793.054389 fcntl(3, F_GETFL) = 0x2 (flags O_RDWR) <0.000007>
1572266793.054414 fcntl(3, F_SETFL, O_RDWR|O_NONBLOCK) = 0 <0.000008>
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>
1572266793.054533 poll([{fd=3, events=POLLIN|POLLOUT|POLLERR|POLLHUP}], 1, 60000) = 1 ([{fd=3, revents=POLLOUT}]) <0.000008>
1572266793.054565 getsockopt(3, SOL_SOCKET, SO_ERROR, [0], [4]) = 0 <0.000008>
1572266793.054594 fcntl(3, F_SETFL, O_RDWR) = 0 <0.000006>
1572266793.054619 setsockopt(3, SOL_TCP, TCP_NODELAY, [1], 4) = 0 <0.000008>
1572266793.054644 setsockopt(3, SOL_SOCKET, SO_KEEPALIVE, [0], 4) = 0 <0.000007>
1572266793.054689 poll([{fd=3, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 0 (Timeout) <0.000007>
1572266793.054717 sendto(3, "*2\r\n$3\r\nGET\r\n$3\r\nkey\r\n", 22, MSG_DONTWAIT, NULL, 0) = 22 <0.000030>
1572266793.054788 poll([{fd=3, events=POLLIN|POLLPRI|POLLERR|POLLHUP}], 1, 0) = 1 ([{fd=3, revents=POLLIN}]) <0.000007>
1572266793.054816 recvfrom(3, "$", 1, MSG_PEEK, NULL, NULL) = 1 <0.000009>
1572266793.054851 poll([{fd=3, events=POLLIN|POLLERR|POLLHUP}], 1, 60000) = 1 ([{fd=3, revents=POLLIN}]) <0.000007>
1572266793.054878 recvfrom(3, "$6\r\nvalue2\r\n", 8192, MSG_DONTWAIT, NULL, NULL) = 12 <0.000009>
1572266793.054923 write(1, "string(6) \"", 11string(6) ") = 11 <0.000010>
1572266793.054951 write(1, "value2", 6value2) = 6 <0.000009>
1572266793.054977 write(1, "\"\n", 2"
我们就可以逐行查看程序系统调用的详情.很容易发现,这个程序是阻塞的,虽然每个系统调用的时间极短.但换个业务,换个依赖可就不是这样.但万变不离其宗.
我们再来看第二个程序,也就是开启协程后的系统调用
1572266985.463011 socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_IP) = 4 <0.000016>
1572266985.463048 fcntl(4, F_GETFL) = 0x2 (flags O_RDWR) <0.000006>
1572266985.463070 fcntl(4, F_SETFL, O_RDWR|O_NONBLOCK) = 0 <0.000006>
1572266985.463092 setsockopt(4, SOL_TCP, TCP_NODELAY, [1], 4) = 0 <0.000007>
1572266985.463127 setsockopt(4, SOL_TCP, TCP_NODELAY, [1], 4) = 0 <0.000005>
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>
1572266985.463296 epoll_ctl(3, EPOLL_CTL_ADD, 4, {EPOLLOUT, {u32=4, u64=38654705668}}) = 0 <0.000011>
1572266985.463353 epoll_wait(3, [{EPOLLOUT, {u32=4, u64=38654705668}}], 4096, 60000) = 1 <0.000007>
1572266985.463383 epoll_ctl(3, EPOLL_CTL_DEL, 4, NULL) = 0 <0.000008>
1572266985.463407 getsockopt(4, SOL_SOCKET, SO_ERROR, [0], [4]) = 0 <0.000008>
1572266985.463445 setsockopt(4, SOL_TCP, TCP_NODELAY, [1], 4) = 0 <0.000007>
1572266985.463467 setsockopt(4, SOL_SOCKET, SO_KEEPALIVE, [0], 4) = 0 <0.000007>
1572266985.463525 recvfrom(4, 0x7f29bb9b033d, 1, MSG_PEEK, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable) <0.000008>
1572266985.463556 sendto(4, "*2\r\n$3\r\nGET\r\n$3\r\nkey\r\n", 22, 0, NULL, 0) = 22 <0.000033>
1572266985.463633 recvfrom(4, 0x7f29bb9b033d, 1, MSG_PEEK, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable) <0.000006>
1572266985.463662 recvfrom(4, "$6\r\nvalue2\r\n", 8192, 0, NULL, NULL) = 12 <0.000007>
1572266985.463713 write(1, "string(6) \"", 11string(6) ") = 11 <0.000009>
1572266985.463740 write(1, "value2", 6value2) = 6 <0.000008>
可以发现,通过创建socket,然后connect操作,然后通过epoll_ctl加入监听,是通过多路复用的方式,这里是没有阻塞等待的,我们可以通过这种方式明确的确认自己的程序细节.
比如,可以从第八行看到这里超时等待的时间是60s.
然后我们修改代码超时时间为0.5s
<?php
Swoole\Runtime::enableCoroutine();
go(function () {
$redis = new Redis;
$redis->connect("127.0.0.1", 6379, 0.5);
$var = $redis->get("key");
var_dump($var);
});
然后继续查看
1572267648.851924 socket(AF_INET, SOCK_STREAM|SOCK_CLOEXEC, IPPROTO_IP) = 4 <0.000011>
1572267648.851952 fcntl(4, F_GETFL) = 0x2 (flags O_RDWR) <0.000004>
1572267648.851968 fcntl(4, F_SETFL, O_RDWR|O_NONBLOCK) = 0 <0.000004>
1572267648.851986 setsockopt(4, SOL_TCP, TCP_NODELAY, [1], 4) = 0 <0.000005>
1572267648.852011 setsockopt(4, SOL_TCP, TCP_NODELAY, [1], 4) = 0 <0.000006>
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>
1572267648.852131 epoll_ctl(3, EPOLL_CTL_ADD, 4, {EPOLLOUT, {u32=4, u64=38654705668}}) = 0 <0.000006>
1572267648.852164 epoll_wait(3, [{EPOLLOUT, {u32=4, u64=38654705668}}], 4096, 500) = 1 <0.000006>
1572267648.852184 epoll_ctl(3, EPOLL_CTL_DEL, 4, NULL) = 0 <0.000005>
1572267648.852201 getsockopt(4, SOL_SOCKET, SO_ERROR, [0], [4]) = 0 <0.000006>
1572267648.852225 setsockopt(4, SOL_TCP, TCP_NODELAY, [1], 4) = 0 <0.000058>
1572267648.852302 setsockopt(4, SOL_SOCKET, SO_KEEPALIVE, [0], 4) = 0 <0.000006>
1572267648.852350 recvfrom(4, 0x7f71f91b033d, 1, MSG_PEEK, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable) <0.000006>
1572267648.852373 sendto(4, "*2\r\n$3\r\nGET\r\n$3\r\nkey\r\n", 22, 0, NULL, 0) = 22 <0.000029>
1572267648.852446 recvfrom(4, "$", 1, MSG_PEEK, NULL, NULL) = 1 <0.000011>
1572267648.852486 recvfrom(4, "$6\r\nvalue2\r\n", 8192, 0, NULL, NULL) = 12 <0.000006>
1572267648.852523 write(1, "string(6) \"", 11string(6) ") = 11 <0.000008>
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程序员的巨大作用.
- 很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要的加群(点击→)677079770
PHP程序员-常用工具的更多相关文章
- 【常用软件】木木的常用软件点评(2)------VC程序员常用工具篇
摘自:http://blog.csdn.net/liquanhai/article/details/7215045 木木的常用软件点评(2)------VC程序员常用工具篇 分类: VC++经验总结 ...
- Java程序员常用工具类库
有人说当你开始学习Java的时候,你就走上了一条不归路,在Java世界里,包罗万象,从J2SE,J2ME,J2EE三大平台,到J2EE中的13中核心技术,再到Java世界中万紫千红的Framework ...
- Java程序员常用工具集
我发现很多人没办法高效地解决问题的关键原因是不熟悉工具,不熟悉工具也还罢了,甚至还不知道怎么去找工具,这个问题就大条了.我想列下我能想到的一个Java程序员会用到的常用工具. 一.编码工具 1.IDE ...
- Java程序员常用工具类库 - 目录
有人说当你开始学习Java的时候,你就走上了一条不归路,在Java世界里,包罗万象,从J2SE,J2ME,J2EE三大平台,到J2EE中的13中核心技术,再到Java世界中万紫千红的Framework ...
- java工具类去掉字符串String中的.点。android开发java程序员常用工具类
下面是工具类详细代码: package com.qq986945193.david; /** * qq986945193 Project * ============================= ...
- JAVA程序员常用英语
JAVA程序员常用英语 干程序员这行实在是离不开英语,干程序员是一项很辛苦的工作,要成为一个高水平的程序员尤为艰难.这是因为计算机软件技术更新的速度越来越快,而这些技术大多来源于英语国家,我们在引进这 ...
- 程序员作图工具和技巧,你 get 了么?
分享程序员常用的画图软件和小技巧 大家好,我是鱼皮. 说实话,我觉得做个程序员挺好的.日常工作有很多,写代码.对需求.写方案等等,但我最爱画图:流程图.架构图.交互图.功能模块图.UML 类图.部署图 ...
- 代码自动生成工具MyGeneration之一(程序员必备工具)
代码自动生成工具MyGeneration之一(程序员必备工具) 转 分类: C#2008-08-06 18:12 16064人阅读 评论(12) 收藏 举报 工具数据库相关数据库stringbrows ...
- 十大PHP程序员必备工具
十大PHP程序员必备工具 1.Notepad++ 总结来说就是小而精,7.4版本的软件包只有2.9M,比一般的IDE小数十倍,但是Notepad++的功能确是很全面的,代码高亮,语法折叠,宏功能,内置 ...
随机推荐
- java架构之路-(Redis专题)Redis的主从、哨兵和集群
我们使用的redis,单机的绝对做不到高可用的,万一单机的redis宕机了,就没有备用的了,我们可以采用集群的方式来保证我们的高可用操作. 主从架构 大致就是这样的,一个主节点,两个从节点(一般两个就 ...
- MySQL从库生成大量小的relay log案例模拟
最近看到"八怪"写的<MySQL:产生大量小relay log的故障一例>,因之前也遇到类似的情况,一直没搞懂原理及复现,看完此文章后,本着实践是检验真理的唯一标准的原 ...
- Js极客之路 - 优化操作(性能优化)
1.因为每次For循环都会计算一次arr.length,所以有必要存储数组长度以减少计算.针对这篇文章(http://www.crimx.com/2015/04/21/should-array-len ...
- 三维动画形变算法(Mixed Finite Elements)
混合有限元方法通入引入辅助变量后可以将高阶微分问题变成一系列低阶微分问题的组合.在三维网格形变问题中,我们考虑如下泛函极值问题: 其中u: Ω0 → R3是变形体的空间坐标,上述泛函极值问题对应的欧拉 ...
- 浅谈原理--hashCode方法
我们时常会判断一个元素是否相等重复,可以用equals方法. 每增加一个元素,我们就可以通过equals方法判断集合中的每一个元素是否重复,但是如果集合中有10000个元素了,我们每添加一个元素的时候 ...
- POJO和JavaBean
1.POJO POJO(Plain Ordinary Java Object):POJO就是一个简单的普通的Java对象,它不包含业务逻辑或持久逻辑等,但不是JavaBean.EntityBean等, ...
- python学习-while True语句
while True是不会跳出循环的. 在while中括号里为一个条件值,只有当条件为真的时候,会执行这条语句,直到条件为false的时候,则会跳出该循环语句.而在这里括号里的值为true,也就意味着 ...
- Java基础(39)Arrays.binarySearch方法
1.源码中可以看到,binarySearch方法调用了binarySearch0方法,binarySearch0方法才是标准的二分查找实现. 2.对于binarySearch0方法来说,注意最后的re ...
- 使用 Nginx 搭建静态资源 web 服务器
在搭建网站的时候,往往会加载很多的图片,如果都从 Tomcat 服务器来获取静态资源,这样会增加服务器的负载,使得服务器运行 速度非常慢,这时可以使用 Nginx 服务器来加载这些静态资源,这样就可以 ...
- ulua、tolua原理解析
在聊ulua.tolua之前,我们先来看看Unity热更新相关知识. 什么是热更新 举例来说: 游戏上线后,玩家下载第一个版本(70M左右或者更大),在运营的过程中,如果需要更换UI显示,或者修改游戏 ...