4月 042017
 

system/exec 函数:

这两个函数会利用/bin/sh 指定的shell来执行指定的命令,所以,指定的命令实际上不仅可以包含可以执行的命令和参数,还可以是对应shell的内置命令以及shell支持的控制结构。

缺点: 无法区分标准输出与标准错误

注意: 这两个函数执行的外部命令默认继承了php进程的标准输入、标准输出和标准错误,你可以通过返回值得到进程的标准输出,但是标准错误却悄悄地流进了php进程定义的标准错误输出里面去了;更加隐晦的是,对于php-fpm进程来讲,该外部进程也默认listen(继承来的)了php-fpm监听的端口,但是肯定不会去处理php-fpm请求,一般情况下不会有问题,特殊情况下回让你莫名其妙

pcntl_exec 函数

该函数会取代当前执行的进程空间,而且是不会再回到当前进程空间,可能你很少会用到该函数,但是,当你确实需要该函数的时候,却又是没有其他函数可以替代的,如:

echo 2 是不可能被执行到的

注意: pcntl_exec不同于其他函数,并不会使用shell来执行指定的命令,而且指定的命令需要是要执行的可执行文件的全路径

proc_open 函数

这个是我最喜欢的函数:

  1. 可以通过proc_close得到外部进程的返回值
  2. 可以定义外部进程的标准输入、标准输出、标准错误,并且可以区分外部进程的标准输出和标准错误
  3. 一定程度上还可以实现并行执行多个外部命令

 

proc_open 示例:

 

 Posted by at 下午 2:59
12月 092016
 

且看一个demo:

问题: 如何在errstr信息前面添加一个HOSTNAME 环境变量信息?

  1. 考虑到errstr有可能写到标准错误,也有可能写到标准输出,也有可能根本就不输出,所以,提前输出HOSTNAME 信息显然不合适
  2. 考虑到引用errstr的地方确实不少,最好一开始就拼接HOSTNAME到该errstr 上面去,那么如何拼接呢?

如果是在脚本语言或者带有gc的语言中,完全可以直接在errstr变量前面拼接那个动态获取到的环境变量,如:

但是c中却不能这样,为什么呢?字符串拼接的函数不是有的吗,如:strcat;

事实却并非如此简单,为什么呢?

上面的errstr变量是存储在栈上的,不需要考虑free的问题,原作者考虑到后续一大堆复杂的if分支,如果把errstr放在堆上,处处要考虑是不是该把errstr free掉,得死多少脑细胞啊!

对于HOSTNAME是个运行时的信息,显然没法放到栈上,如果非要和errstr拼接起来的话,errstr必须在堆上,即要动态的malloc,由此可见,对于脚本语言来讲,一个非常简单的逻辑,在C中却如此的麻烦

 

解决办法:

  1. 为了既不需要free,又能拼接字符串,可以定义一个字符数组,如: errstr[255],然后,把需要的信息加进去;为了避免信息太多而不小心越界,可以使用snprintf函数,自动丢弃超长的部分
 Posted by at 下午 5:24
12月 082016
 

我们知道,nginx的reload是不会影响服务的,php-fpm也有reload 功能,而且确实不是restart,但是却是影响服务的,下面给出一种改进方式,是的php-fpm的reload不影响服务

修改源代码:

 

重写init脚本:

 

注意:

  1. 该功能要求linux内核版本较高,需要支持socket的SO_REUSEPORT 特性,我的3.10的内核已经支持了
  2. php-fpm 的pid文件是php-fpm进程来维护的,同时启动多个php-fpm不太方便,所以需要玩一些手段;虽然不指定pid文件参数也可以,但是我不知道如何才能方便地拿到php-fpm的pid信息,所以还是指定了pid参数
 Posted by at 下午 4:25
11月 292016
 

长连接(也叫持久连接)是啥?

对于PHP来讲,如果是运行在web模式,每次请求完成,php会回收所有需要的申请的资源,但是也可以申请一些特殊的资源,php可以不回收,比如:长连接。

优点: 长连接省却了每次连接的麻烦,也有效减少了timewait的数量

长连接真的好吗?

  1. PHP中没有提供关闭(销毁)长连接的方法,当长连接出现意外时,将无所适从,更常见的是请求超时,都很不方便处理
  2. 同一个服务只能存在一个连接,如果想非阻塞地并发请求同一个服务(相同的目标ip、port),做不到!第二次的pfsockopen得到的是第一次pfsockopen的那个连接,php不会标识一个长连接是否在使用中

鉴于上面两点,感觉长连接真的好鸡肋

 Posted by at 上午 9:39
11月 092016
 

(从大量的CLOSE_WAIT发现的问题)

脚本:

 

对于curl来说:

  1. 如果请求头中含有 Connection: close 则,执行完请求之后,curl会主动关闭连接,即使不主动curl_close() 也没有关系
  2. 如果请求头中没有Connection: close,则:
    1. 如果curl句柄被销毁了,则连接自动关闭
    2. 如果句柄没有被销毁(如: 被保存到了全局(或其他静态)变量中),则:
      1. PHP5中,curl_close() 可以关闭连接
      2. PHP7中,curl_close() 不会关闭连接(或许他猜测你可能还会用到),如果你不小心把句柄保存起来了,那么会有什么问题呢? 问题:
        服务器端等待足够长时间后要关闭连接,而客户端却不做任何处理,导致连接处于CLOSE_WAIT状态,如果足够多,会导致local port被用完。
        不过,这个问题看似不好解决,也比PHP5中好处理,PHP5中如果调用了curl_close,而且还把curl句柄保存了起来,则可能会出现保存了大量的句柄而自己却不知道(就是所谓的内存泄漏)

 

 Posted by at 下午 7:35
11月 042016
 

用例:

  1. 使用tinyproxy-1.8.3做代理
  2. php访问soap服务时,使用tinyproxy做代理访问http地址(不是https)
  3. http响应数据有点儿多,走的是Transfer-Encoding: chunked

 

问题:

  1. tinyproxy代理之后,响应头中 HTTP协议版本号为1.1,没有content-length, 也没有connection: close ,也没有Transfer-Encoding: chunked,对于这种尴尬的情况,client端就不方便处理了; curl会有如下警告: no chunk, no close, no size. Assume close to signal end
  2. 真实原因: curl虽然使用HTTP/1.1方式发送请求,但是tinyproxy对于所有http请求都以HTTP/1.0的方式转发(但是添加了Connection: close 头),最终的openresty(nginx行为也如此)却无视HTTP/1.0,执意返回HTTP/1.1 响应,并且使用Connection: close; tinyproxy直接透传HTTP/1.1 状态行,却丢弃了响应头中的Connection: close;(或许是tinyproxy以为自己总是工作在HTTP/1.0,所以不需要Connection: close 吧)

tinyproxy转发的请求:(发给nginx,nginx再代理转发给后面的openresty)

openresty收到的请求:

openresty响应:

openresty是藏在一个nginx后面的,nginx的响应:

 

分析:

原本HTTP/1.0 是不支持Host头的,但是很多client还是添加了,很多server也不介意(甚至喜欢)HTTP/1.0协议中使用Host头,参考: http://stackoverflow.com/questions/246859/http-1-0-vs-1-1

Connection 头也是http1.1中定义的,但是tinyproxy也用在了HTTP/1.0中了(难道为了兼容一些不守规矩的server端?),在HTTP/1.0和Connection头的暗示下,server端没有使用Content-length 或 Transfer-encoding也可以理解,也或许正式Connection头的存在,server端才选择响应HTTP/1.1

tinyproxy 源码片段:

tiny proxy发送请求:

由于tinyproxy上行的是http/1.0 ,所以会故意将请求头中的connection相关信息去掉,代码如下:

这个函数不仅在process_client_headers 中用到,也在 process_server_headers中用到

解决办法:

  1. 如果使用隧道代理的方式,tinyproxy就不会做上述多余的处理了,但是, php的soapclient强制写死的逻辑是:如果方式的是https,则使用隧道代理方式,否则,不走隧道代理方式,可是服务提供的就是http,不是https啊!!!!!!
  2. 修复tinyproxy的这个问题,修复方案:
  3. 换个proxy

 

疑问:

  1. 如果 content-length 不存在与http 1.0,那么http 1.0中的post请求,post数据长度如何表示?(难道也是关闭连接为止?
 Posted by at 下午 7:41
10月 132016
 

占用cpu高的线程的堆栈如下:

gdb 跟进发现:线程在函数Dependencies::find_finalizable_subclass(Klass*) 中,应该有一个循环,不断地调用函数Klass::subklass()

 

解决办法:

The fix was simple – disable CMS class unloading options when -Xnoclassgc or -XX:-ClassUnloading are specified.

参考资料:

https://blogs.oracle.com/poonam/entry/jvm_hang_with_cms_collector

 

想阅读jvm源码吗? https://blogs.oracle.com/sundararajan/entry/so_you_want_to_read

 Posted by at 下午 6:04