分类目录归档:PHP开发

unixsocket内核优化

在常见的nginx+unixsocket相比nginx+tcp回环,更能提升性能,但由于内核参数的限制,导致unixsockets不稳定,需要进行内核参数优化。
参考swoole的优化。
https://wiki.swoole.com/wiki/page/11.html

【sysctl -p】激活
———————
1.ulimit设置,
vim /etc/security/limits.conf

* soft nofile 262140
* hard nofile 262140
root soft nofile 262140
root hard nofile 262140
* soft core unlimited
* hard core unlimited
root soft core unlimited
root hard core unlimited

2.修改sysctl.conf配置
vim /etc/sysctl.conf

fs.file-max = 65535
fs.file-max = 65535
net.unix.max_dgram_qlen = 100
net.ipv4.tcp_mem = 379008       505344  758016
net.ipv4.tcp_wmem = 4096        16384   4194304
net.ipv4.tcp_rmem = 4096          87380   4194304
net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_syn_backlog = 81920
net.ipv4.tcp_synack_retries = 3
net.ipv4.tcp_syn_retries = 3
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 300
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.ip_local_port_range = 20000 65000
net.ipv4.tcp_max_tw_buckets = 200000
net.ipv4.route.max_size = 5242880

PHP中安装主从插件

1. wget http://pecl.php.net/get/mysqlnd_ms-1.5.2.tgz 
2. tar xzvf mysqlnd_ms-1.5.2.tgz
3. cd mysqlnd_ms-1.5.2
4. /path/to/phpize
5. ./configure --enable-mysqlnd-ms --with-php-config=/usr/local/php/bin/php-config
6. make
7. make install
8. sudo /etc/init.d/php-fpm restart
9. php -m | grep mysql #看到"mysqlnd_ms"扩展表示安装成功
 
执行代码
      if (function_exists('mysqlnd_ms_set_qos')) {
            try {
                $db = $this->db;
                $mysqli = $db->conn_id;
                mysqlnd_ms_set_qos($mysqli, MYSQLND_MS_QOS_CONSISTENCY_SESSION);
            }catch (Exception $e) {
 
            }
 
        }

PHP的伪注解:@var

/* @var $apiService MapiService */请注意:@var前是需要空格隔开的。
$apiService = Factory::create ( 'MapiService' );
$orderInfo = $apiService->getOrderInfo($orderSn);

lnmp1.4的xdebug配置

1.从lnmp.org网站,下载lnmp1.4包。
2.通过install.sh安装PHP7.1版
3.通过addons.sh安装redis\memcached\apcu。
3.下载xdubug源码,按以下设置并编译。php-config与php程序在同一个目录。

./configure --enable-xdebug --with-php-config=/usr/local/php/bin/php-config
make install

4.设置php.ini调试信息

创建/usr/local/php/conf.d/010-xdebug.ini文件。
添加信息如下:
[Xdebug]
zend_extension="xdebug.so"
xdebug.remote_enable = On
xdebug.profiler_enable = On
xdebug.profiler_enable_trigger = On
 
xdebug.auto_trace = on
xdebug.auto_profile = on
xdebug.collect_params = on
xdebug.collect_return = on
xdebug.profiler_enable = on
xdebug.trace_output_dir = "/home/wwwlogs/xdebug"
xdebug.profiler_output_dir = "/home/wwwlogs/xdebug"
xdebug.dump.GET = *
xdebug.dump.POST = *
xdebug.dump.COOKIE = *
xdebug.dump.SESSION = *
xdebug.var_display_max_data = 9056
xdebug.var_display_max_depth = 50

5.方便调试,后续步聚可以如下修改。

chattr -i /home/wwwroot/default/.user.ini
chown abc:abc wwwroot -R
chown abc:abc wwwlog -R

thrift开发环境的搭建

1.http://thrift.apache.org/docs/
2.http://thrift.apache.org/docs/BuildingFromSource
3.http://thrift.apache.org/docs/install/
4.http://thrift.apache.org/docs/install/centos

在纯净的CentOS7.3的环境中,应该如下操作。
sudo yum -y groupinstall “Development Tools”
sudo yum -y zlib-devel openssl-devel ant
安装libevent-2.0.22-stable.tar.gz
安装boost_1_53_0.tar.gz
安装thrift-0.9.3.tar.gz

JAVA和antoconf、automake、bison是系统自带的,只需要增加ant的安装。

————————————————
相关环境变量的配置

#set java environment
export JAVA_HOME=/jdk1.8.0_101
export JRE_HOME=/jdk1.8.0_101/jre
export CLASS_PATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib
export PATH=$PATH:$JAVA_HOME/bin:$JRE_HOME/bin
export ANT_HOME=/apache-ant-1.9.9
export PATH=$PATH:$ANT_HOME/bin


———————


nginx静态文件Cache配置

一直不知道nginx自带了静态文件Cache功能,现在可好了,可以优化一下我的博客了。

开启功能:

1.  open_file_cache max=65535 inactive=60s;

这个将为打开文件指定缓存,默认是没有启用的,max 指定缓存数量,建议和打开文件数一致,inactive 是指经过多长时间文件没被请求后删除缓存。

2.  open_file_cache_valid 80s;

这个是指多长时间检查一次缓存的有效信息。

3.  open_file_cache_min_uses 1

open_file_cache 指令中的inactive 参数时间内文件的最少使用次数,如果超过这个数字,文件描述符一直是在缓存中打开的,如上例,如果有一个文件在inactive 时间内一次没被使用,它将被移除。

文件Cache的实现原理:http://www.pagefault.info/?p=123

备注:相较传统read/write方式,2.1版本内核引进的sendfile已经减少了内核缓冲区到user缓冲区,再由user缓冲区到socket相关缓冲区的文件copy,而在内核版本2.4之后,文件描述符结果被改变,sendfile实现了更简单的方式,系统调用方式仍然一样,细节与2.1版本的不同之处在于,当文件数据被复制到内核缓冲区时,不再将所有数据copy到socket相关的缓冲区,而是仅仅将记录数据位置和长度相关的数据保存到socket相关的缓存,而实际数据将由DMA模块直接发送到协议引擎,再次减少了一次copy操作。

Jetbrain-IDE大项目性能优化

1.修改vmoptions配置文件,该文件存在以下几个位置,需要修改用户自定义配置的文件,才有效。以CLion-IDE为例。
/clion-2016.2/bin/clion64.vmoptions,如果修改这个参数,则需要把一下行的配置中同名清除掉,否则不会生效。
/home/abc/.CLion2016.2/clion64.vmoptions,建议修改这个,本人是亲测这个参数的修改。
vmoptions的加载优先级是,先加载bin目录下,再加载user目录下,在合并参数时,后者会自动覆盖前者。
2.修改以下几个参数。
原始参数如下:
-Xss2m
-Xms256m
-Xmx2000m
-XX:NewSize=128m
-XX:MaxNewSize=128m
-XX:ReservedCodeCacheSize=96m
修改为如下:
-Xss2m
-Xms1024m
-Xmx4096m
-XX:NewSize=128m
-XX:MaxNewSize=128m
-XX:ReservedCodeCacheSize=1024m
3.检测是否生效,启动CLion,执行ps -ef|grep java。
ps -ef|grep java
abc 3961 3909 16 01:04 ? 00:01:48 /clion-2016.2/bin/../jre/jre/bin/java -Xbootclasspath/a:/clion-2016.2/bin/../lib/boot.jar -classpath /clion-2016.2/bin/../lib/bootstrap.jar:/clion-2016.2/bin/../lib/extensions.jar:/clion-2016.2/bin/../lib/util.jar:/clion-2016.2/bin/../lib/jdom.jar:/clion-2016.2/bin/../lib/log4j.jar:/clion-2016.2/bin/../lib/trove4j.jar:/clion-2016.2/bin/../lib/jna.jar -Xss2m -Xms1024m -Xmx4096m -XX:NewSize=128m -XX:MaxNewSize=128m -XX:ReservedCodeCacheSize=1024m -XX:+UseConcMarkSweepGC -XX:SoftRefLRUPolicyMSPerMB=50 -ea -Dsun.io.useCanonCaches=false -Djava.net.preferIPv4Stack=true -XX:+HeapDumpOnOutOfMemoryError -XX:-OmitStackTraceInFastThrow -Dawt.useSystemAAFontSettings=lcd -Djb.vmOptionsFile=/home/abc/.CLion2016.2/clion64.vmoptions -XX:ErrorFile=/home/abc/java_error_in_CL_%p.log -XX:HeapDumpPath=/home/abc/java_error_in_CL.hprof -Djb.restart.code=88 -Didea.paths.selector=CLion2016.2 -Didea.platform.prefix=CLion -Didea.no.jre.check=true com.intellij.idea.Main
4.以真实大项目验证吧,如PHP5.57的源码分析,在没有配置前,CLion一直都卡在build symbols这步骤,最终爆内存错误,修改为大内存版的配置后,可以成功完成所有步骤。
5.以上4个步骤后,仍然失败,则检查一下是否虚拟机的内存配置太少了,按上述配置虚拟机物理内存至少为4G,才可以顺利执行。

Xhprof源码分析

核心监控流程:

  1. 初始化监控:通过xhprof_enable函数,设置函数钩子和统计函数。涉及的主要函数钩子有如下:

    zend_compile_file:当使用include\require\include_one\require_once函数加载文件时,均会经历编译过程。这编译过程就是通过该函数完成。

    zend_execute_ex:php引擎每执行一个函数,均会通过该函数进行调用,像xdebug也是通过该函数建立运行堆栈信息,xhprof也不例外,钩子建立过程。

    _zend_execute_ex = zend_compile_file

    zend_compile_file = hp_execute_ex

    zend_execute_internal:该函数只有xhprof_enable配置了XHPROF_FLAGS_NO_BUILTINS参数时,才会被拦截。

  2. 监控核心过程:被动地监控每个函数的执行,由于函数HOOKER的机制,PHP每执行一个函数都将进入钩子的回调函数中,并在相应的回调函数中执行以下过程:

    BEGIN_PROFILING->function execute->END_PROFILING

    函数式表示如下:

    Function hp_execute_ex() //这是钩子的回调函数。

    {

        BEGIN_PROFILING; //开始分配内存、初始化捕获的开始时间,内存值等

        _zend_execute_ex(); //执行函数内容。

        END_PROFILING; //计算函数执行的成本:CPU和内存

    }

    另外三个函数,也是采用同样的方式。

  3. 结束监控,返回数据。

 

源码分析

  1. 初始化函数钩子。并默认把main()函数当作为监控入口,它是一个虚拟函数,目的是表示监控的开始和结束,xhprof_enable调用表示开始执行该虚拟函数,xhprof_disable表示结束该虚拟函数。

 

 

  1. 由于钩子的拦截关系,PHP引擎每执行一个函数,均会进入相应钩子回调中,如hp_execute_ex函数为例。重点关注两个宏定义,分别是BEGIN_PROFILING和END_PROFILING.

  1. BEGIN_PROFILING和END_PROFILING的定义如下:

    这两个宏完成以下三件事:

    1. 分配栈帧,每一个hp_entry_t结构,是栈结构上的一个元素。它记录了当前函数的开始信息,如当前函数名、递归深度、开始时间、开始内存。

      注:它不保存当前函数的结束信息,结束的信息会减去开始信息,然后把结果输出到另一个数组中。

    2. hp_mode_common_beginfn函数是用于执行压栈动作,并检测栈中是否存在递归函数;hp_mode_common_endfn函数用于执行弹栈动作。
    3. begin_fn_cb函数是用于填充hp_entry_t结构信息,也即是函数的开始信息;end_fn_cb函数是用于计算函数的消耗结果,并把该结果输出到另外一个全局数组中,该数组并没有体现在这两个宏定义中。
  2. 在xhprof实际使用过程中,常会看到如下一些情况,举例说明:

    PHP代码如下:

    Xhprof的图表中输出如下:

    图表中的@1,@2,@3代表什么?这个数字代表这个函数在执行过程中的递归深度,具体算法如下。

    这段源码部份变量解释如下:

    func_hash_counters,全局数组是用于快速判断当前栈列表中是否存在同码函数

    hash_code,记录是函数的hash码,xhprof把函数转换为一个0~255的整数。

    name_hprof,记录是函数的真正名字,如bar这个函数。

    rlvl_hprof,记录的是当前函数在栈列表中深度。

    entries,永远指向栈列表的栈顶。

    综上所述,该段函数的功能是指函数在被压入栈表前,由栈顶向栈底搜索是否存在同名函数,如果栈表已经存在同名函数,则表示当前正在执行函数递归操作,并记录当前函数的递归深度。

    下面函数应该可以加强上述的理解。

    hp_get_entry_name就是获取函数名。

  3. 偶然也会遇到如下一些现象,依旧举例说明。

    Php代码如下:

    Xhprof图表如下:

    load::my/MyTest.php也是一个虚拟的函数,它的功能是代表把MyTest.php由源文件翻译成opcode文件的消耗。关键算法截图如下:

该函数解析如下:

zend_compile_file,该函数是把php源文件翻译成opcode指令。

hp_get_base_filename,是把文件路径,截取最后两段路径名作函数名返回来,如下所示

/alidata/www/xhprof/example/../my/MyTest.php => my/MyTest

/a/b/c/a.php =>c/a

???_op,函数是无法获取函数名字的一个默认名称。

  1. Xhprof_disable()返回原始输出结果。

    源码如下:

    以JSON格式输出原始结果如下:

    {“foo==>bar”:{“ct”:5,”wt”:38,”cpu”:0,”mu”:5008,”pmu”:0},”bar==>bar@1″:{“ct”:5,”wt”:22,”cpu”:0,”mu”:4928,”pmu”:0},”bar@1==>bar@2″:{“ct”:4,”wt”:14,”cpu”:0,”mu”:3888,”pmu”:0},”bar@2==>bar@3″:{“ct”:3,”wt”:9,”cpu”:0,”mu”:2848,”pmu”:0},”bar@3==>bar@4″:{“ct”:2,”wt”:3,”cpu”:0,”mu”:1808,”pmu”:0},”main()==>foo”:{“ct”:1,”wt”:80,”cpu”:0,”mu”:6080,”pmu”:0},”bar@4==>bar@5″:{“ct”:1,”wt”:0,”cpu”:0,”mu”:752,”pmu”:0},”main()==>bar”:{“ct”:1,”wt”:8,”cpu”:0,”mu”:1792,”pmu”:0},”main()”:{“ct”:1,”wt”:114,”cpu”:0,”mu”:9240,”pmu”:0}}

    这个JSON如何转换到以下图表呢?

    从图表虽然很直观,但不能很好的反映出它与原始JSON内容的关系,我们选择其中一项,如bar项,展开其父、我、子,这种爷孙三代关系图,截图如下:

    “foo==>bar”:{“ct”:5,”wt”:38,”cpu”:0,”mu”:5008,”pmu”:0}

    “main()==>bar”:{“ct”:1,”wt”:8,”cpu”:0,”mu”:1792,”pmu”:0}

    “bar==>bar@1”:{“ct”:5,”wt”:22,”cpu”:0,”mu”:4928,”pmu”:0}

    通过对比,可以知道Excl.Wall_Time运算公式如下:

    Excl.Wall_Time = Current Function.Incl.Wall_Time – Child Function.Incl.Wall_Time。

    cpu的消耗,个人认为它与wt消耗是重复了。首先cpu的实现方式相比wt的实现方式,毫无优势。如下是它们的对比:

    1. cpu和wt均是时间差计算出来的。
    2. cpu的时间精度为微秒,而wt的时间精度是纳秒。
    3. 每执行一个函数,函数消耗不足微秒时,cpu因精度关系,计算结果为0,导致失误增大,而wt计算结果会被正确地累计。

    mu内存消耗的理解,与wt基本一致。