关于PHP中的长连接

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

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

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

长连接真的好吗?

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

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

关于docker daemon占用大量内存的问题

问题现象:

问题环境:

dockerd 打开的文件数太多了, ~ 4w

类似如下:

和哪个容器相关还是和特定容器无关?

主要和docker-registry的那个容器相关(应该其他容器的个数少,不一定完全正常)

杀掉docker-registry这个容器,dockerd内存使用并未减少

但是相应的文件是不存在的了:

然后,只好重启dockerd试试了:

重启dockerd,问题解决(还好我们的dockerd重启不会影响容器的正常工作)

 

问题的非最终原因(但是也进了一步):

我们对所有的容器都是有监控的,基本手段是使用docker exec 在容器内执行命令,docker-registry这个容器比较特殊,没有我们要执行的命令,除了我们无法获取该容器信息外,没执行一次不存在的命令,dockerd就会在目录/run/docker/libcontainerd/ 下创建一个FIFO文件,并且始终打开着,不关闭(如果要执行的命令存在,则不会存在不关闭的情况)

详细测试结果如下:

对于一个返回值非零的命令,如果使用exec -i 选项,则不会残留打开的文件,如果不使用 exec -i 选项,会残留打开的文件(但是继续跟踪发现,该残留的时间不会太长)

对于一个不存在的命令,不管是否使用 -i 选项,总是会残留打开的文件;

解决办法:

为确保要执行的命令存在,可以使用如下方式:

该方式不但不会产生多余的未关闭文件描述符,而且可以执行一个命令序列,而直接docker exec -it cmd 只能执行一条命令

参考资料: https://github.com/docker/docker/issues/22095

 

shell 点滴 之 printf

将数组信息已多行格式输出

如何输出为:

 

strace 跟踪多个进程

strace是所有linux程序员都应该熟练掌握的工具,该命令可以strace -p $pid1 -p $pid2 跟踪一个或多个正在运行的进程,跟踪一个进程还好;当我们想strace httpd的所有进程时,我们可以pidof httpd拿到所有的pid,但是不能直接使用,需要在每个pid前面添加-p,用 while循环可以做到,如下:

倒是也不长,其实有更简单的办法:

如果我们已经将pid信息存到了$pids中了,那么可以:

注意:printf 参数中的 -p 最好不要顶着头写 (不过这个在使用xargs的时候不存在这个问题)

关于tcp代理与keepalive

关于tcp代理涉及3个实体:

c: client

p: proxy

s: server

 

c  <—–> p <—–> s

c 和 p建立tcp连接, p 和 s 建立tcp连接;

对于tcp来讲,很可能不是http那样ask and answer then close的, 很有可能连接上长时间没有数据,那么会出现什么问题呢?

  1. 假如 c 和 p 之间的网线短了, c 不可能再关闭这个连接了,那么p如何释放这个连接呢?
    1. p 在该连接上设置keepalive,定期检查连接是否断开,一般系统设置的间隔时间为2小时
    2. 一定程度上,c也关心这个长时间没有使用的连接是否依然可用,那么,c也可以在该连接上设置keepalive
  2. 问题:
    1. p 是否会转发c 的keepalive 数据包?
      1. p 是tcp(传输层)的代理,只有接到数据才会转发,没有数据不会转发
      2. keepalive 实际上是一个数据长度为0的ip数据包
      3. 所以: p 不会转发keepalive
    2. 既然p不会转发keepalive,那么如果p和 s 之间的链路出现问题,c 将不知道其实连接已经不可用了,然后就会数据发送失败

总结:

  1. p 有必要和c、s keepalive

关于http_proxy 与https_proxy

很多程序会自动识别HTTP_PROXY , HTTPS_PROXY 环境变量,然后决定是否走代理,那么,这两个变量是什么意思呢?

如:

前者说明: 如果访问的地址是http的(不是https的),则使用代理:http://proxy.phpor.net:8888

后者说明:如果访问的地址是https的(不是http的),则使用代理:http://proxy.phpor.net:8888

这里根本不要求HTTPS_PROXY 的代理url一定是https的 ,也不要求HTTP_PROXY的代理url一定是http的。

但是,需要注意的是,似乎curl并不支持一个类似于https://proxy.phpor.net:8888 的https的地址,即使写作https,curl依然使用http明文的方式向代理发起请求。

有些程序如(curl)不识别HTTP_PROXY ,只能用http_proxy; 但是HTTPS_PROXY ,却可以使用大写的(小写的优先)。参考: man curl

wget 总是只识别小写的: http_proxy, https_proxy

优雅切换软连接的方法

对于大访问量的网站,上线代码时一般有两种方式:

  1. 直接覆盖代码目录
  2. 上传新代码目录,然后重新创建软连接

对于第一种方法,不好

对于第二种方法,切换软连接也可能会出现问题,尽管速度很快,测试方法:

在一个term中不断地查看a目录下的m文件(a是一个软连接,可能连接到a.1 或 a.2)

命令1:

然后在另一个term中执行命令2:

我们发现命令1有一个出错;那么如果才能使得命令1不会感知到变化呢?

命令3:

执行该命令并不会导致命令1出错,不是凑巧没出错吗?做个压力测试吧:

命令4:

 

PHP5 升级到PHP7时curl注意事项

(从大量的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句柄保存了起来,则可能会出现保存了大量的句柄而自己却不知道(就是所谓的内存泄漏)

 

tinyproxy源码学习

  1. 解析request_line 时,使用sscanf (php中也有该函数)
  2. upstream可以设置upstream代理,如: client要请求phpor.net,tinyproxy中可以配置如果client请求的是phpor.net,则请求proxy.phpor.net,而不是代理请求phpor.net
  3. remove_connection_headers 中定义了上行请求的请求头中包含的connection要去掉,如: connection,proxy-connection