Nginx+PHP-CGI(php-fpm) 的Web环境
突然发现系统负载上升,top 查看后发现很多 php-cgi 进程 CPU 使用率接近100%
找其中一个 CPU 100% 的 php-cgi 进程的 PID,用strace -p 10747跟踪,结果发现以下结果:
select(7, [6], [6], [], {15, 0}) = 1 (out [6], left {15, 0})
poll([{fd=6, events=POLLIN}], 1, 0) = 0 (Timeout)
select(7, [6], [6], [], {15, 0}) = 1 (out [6], left {15, 0})
poll([{fd=6, events=POLLIN}], 1, 0) = 0 (Timeout)
select(7, [6], [6], [], {15, 0}) = 1 (out [6], left {15, 0})
poll([{fd=6, events=POLLIN}], 1, 0) = 0 (Timeout)
select(7, [6], [6], [], {15, 0}) = 1 (out [6], left {15, 0})
poll([{fd=6, events=POLLIN}], 1, 0) = 0 (Timeout)
select(7, [6], [6], [], {15, 0}) = 1 (out [6], left {15, 0})
poll([{fd=6, events=POLLIN}], 1, 0) = 0 (Timeout)
select(7, [6], [6], [], {15, 0}) = 1 (out [6], left {15, 0})
poll([{fd=6, events=POLLIN}], 1, 0) = 0 (Timeout)
select(7, [6], [6], [], {15, 0}) = 1 (out [6], left {15, 0})
poll([{fd=6, events=POLLIN}], 1, 0) = 0 (Timeout)
select(7, [6], [6], [], {15, 0}) = 1 (out [6], left {15, 0})
poll([{fd=6, events=POLLIN}], 1, 0) = 0 (Timeout)
select(7, [6], [6], [], {15, 0}) = 1 (out [6], left {15, 0})
poll([{fd=6, events=POLLIN}], 1, 0) = 0 (Timeout)
select(7, [6], [6], [], {15, 0}) = 1 (out [6], left {15, 0})
poll([{fd=6, events=POLLIN}], 1, 0) = 0 (Timeout)
几乎可以肯定是file_get_contents()导致的问题,
原因是:file_get_contents的目标网站如果反应过慢,file_get_contents就会一直卡在那里不会超时,
我们知道php.ini 里面max_execution_time 可以设置 PHP 脚本的最大执行时间,但是,在 php-cgi(php-fpm) 中,该参数不会起效。真正能够控制 PHP 脚本最大执行时间的是 php-fpm.conf 配置文件中的以下参数:
The timeout (in seconds) for serving a single request after which the worker process will be terminated
Should be used when 'max_execution_time' ini option does not stop script execution for some reason
'0s' means 'off'
<value name="request_terminate_timeout">0s</value> 默认值为 0 秒,也就是说,PHP 脚本会一直执行下去。这样,当所有的 php-cgi 进程都卡在 file_get_contents() 函数时,这台 Nginx+PHP 的 WebServer 已经无法再处理新的 PHP 请求了,Nginx 将给用户返回“502 Bad Gateway”。修改该参数,设置一个 PHP 脚本最大执行时间是必要的,但是,治标不治本。例如改成 30s,如果发生 file_get_contents() 获取网页内容较慢的情况,这就意味着 150 个 php-cgi 进程,每秒钟只能处理 5 个请求,WebServer 同样很难避免“502 Bad Gateway”。
要做到彻底解决,不妨重新封装一下file_get_contents函数:
代码如下 | 复制代码 |
function _file_get_content($str) { |
如此 用_file_get_content代替直接使用file_get_contents 问题解决。
PHP 5.3 ereg() 无法正常使用,提示“Function ereg() is deprecated Error”是因为它长ereg 函数进行了升级处理哦,需要像preg_match使用/ /来规则了,当然也是php5.3把ereg给费掉的节奏了。PHP 5.3 ereg() 无法正常使用,提示“Function ereg() is deprecated Error”。
问题根源是php中有两种正则表示方法,一个是posix,一个是perl,php6打算废除posix的正则表示方法所以后来就加了个preg_match。此问题解决办法很简单,在ereg前加个过滤提示信息符号即可:把ereg()变成@ereg()。这样屏蔽了提示信息,但根本问题还是没有解决,php在5.2版本以前ereg都使用正常,在5.3以后,就要用preg_match来代替ereg。所以就需要变成这样,
原来:ereg("^[0-9]*$",$page)变成:preg_match("/^[0-9]*$/",$page)
特别提醒:posix与perl的很明显的表达区别就是是否加斜杠,所以与ereg相比,后者在正则的前后分别增加了两个"/"符号,不能缺少。
例
改前:function inject_check($sql_str) {
$sql_str = strtolower($sql_str);
return eregi('fopen|post|eval|select|insert|and|or|update|delete|'|/*|*|../|./|union|into|load_file|outfile', $sql_str); // 进行过滤
}
解决方法:
找到代码所在的文件 位置
function inject_check($sql_str) {
$sql_str = strtolower($sql_str);
return preg_match('/fopen|post|eval|select|insert|and|or|update|delete|'|/*|*|../|./|union|into|load_file|outfile/', $sql_str); // 进行过滤
}
注意:一定要加'/'开头与结束哦。此段参考:http://www.111cn.net/phper/31/47360.htm
Tips:此问题在php5.2之前版本不会出现。
header分为三部分:
第一部分为HTTP协议的版本(HTTP-Version);
第二部分为状态代码(Status);
第三部分为原因短语(Reason-Phrase)。
header()函数使用说明:
一、作用:
~~~~~~~~~
PHP只是以HTTP协议将HTML文档的标头送到浏览器,告诉浏览器具体怎么处理这个页面,至于传送的内容则需要熟悉一下HTTP协议了,与PHP无关了,可参照http://www.w3.org/Protocols/rfc2616/rfc2616。
传统的标头一定包含下面三种标头之一,并只能出现一次。
Location: xxxx:yyyy/zzzz
Content-Type: xxxx/yyyy
Status: nnn xxxxxx
二、先来了解一下HTTP协议的运作方式
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
HTTP协议是基于请求/响应范式的。一个客户机与服务器建立连接后,发送一个请求给服务器,请求方式的格式为,统一资源标识符、协议版本号,后边是MIME信息包括请求修饰符、客户机信息和可能的内容。服务器接到请求后,给予相应的响应信息,其格式为一个状态行包括信息的协议版本号、一个成功或错误的代码,后边是MIME信息包括服务器信息、实体信息和可能的内容。
它分四个过程,在HTTP协议中,服务端是指提供HTTP服务的部分,客户端是指你使用的浏览器或者下载工具等等。在通讯时,由客户端发出请求连接,服务端建立连接;然后,客户端发出HTTP请求(Request),服务端返回响应信息(Respond),由此完成一个HTTP操作。
三、HTTP协议状态码表示的意思
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1×× 保留
2×× 表示请求成功地接收
3×× 为完成请求客户需进一步细化请求
4×× 客户错误
5×× 服务器错误
例
代码如下 | 复制代码 |
// fix 404 pages: 用这个header指令来解决URL重写产生的404 header // set 404 header: 页面没找到 // 页面被永久删除,可以告诉seo/seo.html" target="_blank">搜索引擎更新它们的urls // 重定向到一个新的位置 延迟一段时间后重定向 // 覆盖 X-Powered-By value // 内容语言 (en = English) //最后修改时间 (在缓存的时候可以用到) // 告诉浏览器要获取的内容还没有更新 // 设置内容的长度 (缓存的时候可以用到): // 用来下载文件: // 禁止缓存当前文档: // plain text file // JPG picture // ZIP file // PDF file // Audio MPEG (MP3,…) file // 显示登录对话框,可以用来进行HTTP认证 |
现在表单的填写,我们可以用AJAX对用户随时进行验证,进行友好的提示,但是在用户没有留意AJAX友好提示,提交了错误的表单,跳回原页,而填写的信息却全部丢失了。要支持页面回跳,有以下的办法:
1. 使用session_cache_limiter方法: session_cache_limiter(‘private,must-revalidate’);但是要值得注意的是 session_cache_limiter()方法要写在session_start()方法之前才有用;
2.用header来设置控制缓存的方法: header(‘Cache-control:private,must-revalidate’);
页面跳转要注意的几个问题总结
1、location和“:”号间不能有空格,否则会出错。
2、在用header前不能有任何的输出。
3、header后的PHP代码还会被执行。
下面是和asp中重定向response.redirect的比较:
例1:
代码如下 | 复制代码 |
response.redirect "../test.asp" header("location:../test.php"); |
两者区别:
asp的redirect函数可以在向客户发送头文件后起作用.
如
代码如下 | 复制代码 |
<html><head></head><body> <%response.redirect "../test.asp"%> </body></html> 查是php中下例代码会报错: <html><head></head><body> <? header("location:../test.php"); ?> </body></html> 只能这样: <? header("location:../test.php"); ?> <html><head></head><body>...</body></html> |
即header函数之前不能向客户发送任何数据.
例2:
asp中
代码如下 | 复制代码 |
<html><head></head><body> <% response.redirect "../a.asp" response.redirect "../b.asp" %> </body></html> 结果是重定向a.asp文件. php呢? <? header("location:../a.php"); header("location:../b.php"); ?> <html><head></head><body></body></html> |
我们发现它重定向b.php.
原来在asp中执行redirect后不会再执行后面的代码.
而php在执行header后,继续执行下面的代码.
在这方面上php中的header重定向不如asp中的重定向.有时我们要重定向后,不能执行后面的代码:
一般地我们用
代码如下 | 复制代码 |
if(...) header("..."); else { ... } |
但是我们可以简单的用下面的方法:
代码如下 | 复制代码 |
if(...) { header("...");exit();} |
还要注意的是,如果是用Unicode(UTF-8)编码时也会出现问题,需要调整缓存设置.
代码如下 | 复制代码 |
<[email=%@]%@LANGUAGE="VBSCRIPT[/email]" CODEPAGE="936"%> <%if Request.ServerVariables("SERVER_NAME")="s.111cn.net" then response.redirect "news/index.htm" else%> <%end if%> <script> var url = location.href; if(url.indexOf('http://www.111cn.net/')!=-1)location.href='/index/index.htm'; if(url.indexOf('http://www.zhutiy.com/')!=-1)location.href='/index1/index.htm'; if(url.indexOf('http://www.111cn.net/')!=-1)location.href='/cn/index.asp'; if(url.indexOf('http://www.baidu.com/')!=-1)location.href='/cn/index.asp'; </script> |
php闭包函数比如你现在就可以这样使用:
代码如下 | 复制代码 |
$closure = function($param) { echo $param; }; |
感觉和js是不是一样的用法了.
一些闭包函数实例
代码如下 | 复制代码 |
var_dump($test(‘hello word!’)); } |
上例输出
2013-11-19 16:24:56teststring(11) “hello word!”
这样子参数便可以用函数了。
条件是,php3.0以后php 4.0以后闭包函数支持$this用法
闭包函数通常被用在preg_match等有callback的函数
代码如下 | 复制代码 |
class A { $bcl1 = Closure::bind($cl1, null, ‘A’); |
输出
1
2
bind将类可以在闭包函数中使用
代码如下 | 复制代码 |
$ob1 = new A1(1); $cl = $ob1->getClosure(); |
以上例程的输出类似于:
1
2
bindto在类里可以再次绑定类
代码如下 | 复制代码 |
$fn = function(){ class Bar{ $bar = new Bar(); $fn1 = $fn->bindTo($bar, ‘Bar’); // specify class name echo $fn1(); // 2 ?> |
在类之外需要绑定类才能用,绑定可以是类名,也可以是对象,绑定过之后可以再次绑定不需要提拱类名或对象
isset函数我们多用于判断变量是不是存在了,如isset($a)了,如果存在返回 true,否则返回false了,下面我们一起来看看。isset函数是检测变量是否设置。
格式:bool isset ( mixed var [, mixed var [, ...]] )
返回值:
若变量不存在则返回 FALSE
若变量存在且其值为NULL,也返回 FALSE
若变量存在且值不为NULL,则返回 TURE
同时检查多个变量时,每个单项都符合上一条要求时才返回 TRUE,否则结果为 FALSE
如果已经使用 unset() 释放了一个变量之后,它将不再是 isset()。若使用 isset() 测试一个被设置成 NULL 的变量,将返回 FALSE。同时要注意的是一个 NULL 字节("")并不等同于 PHP 的 NULL 常数。
获取用户属性时有的用户能,有的用户不能,同样的逻辑为什么不能经检查使用了类似这样的代码
代码如下 | 复制代码 |
$userInfo=’abc’; |
发现$account得到的是空,查了一些资料也没得到结果
做了一个实验得到了结论
代码如下 | 复制代码 |
$a=’abc’; |
结果是a
当是字符串key没有时,它会转化成数字key,是0,所以得到了a
现在php5.4已经修复
趁这机会,整理了一下isset的用法,供大家借鉴
代码如下 | 复制代码 |
<?php $var = ''; if (isset($var)) { // 在后边的例子中,我们将使用 var_dump函数 输出 isset() 的返回值。 $a = "test"; var_dump( isset($a) ); // TRUE unset ($a); var_dump( isset ($a) ); // FALSE $foo = NULL; ?> |
这对于数组中的元素也同样有效:
代码如下 | 复制代码 |
<?php $a = array ('test' => 1, 'hello' => NULL); var_dump( isset ($a['test') ); // TRUE // 'hello' 等于 NULL,所以被认为是未赋值的。 ?> |
键 ‘b’ 的值等于 NULL,所以被认为是未置值的。
但是键’c'的值是空结果是true,空值是代表 有设置的 如果想检测 NULL 键值,可以试试下边的方法。
代码如下 | 复制代码 |
var_dump( array_key_exists(‘hello’, $a) ); // TRUE |
警告: isset() 只能用于变量,因为传递任何其它参数都将造成解析错误。若想检测常量是否已设置,可使用 defined() 函数。
一些应用中实例
PHP isset()对PHP参数判断您可以使用 PHP isset() 来判断一个参数是否被定义,注意如果该参数为空,或者"n"(NULL字节)使用 PHP isset() 判断之后,都会为TRUE。
代码如下 | 复制代码 |
<?php if(isset($weigeti)){ echo '参数$weigeti已经设定,且值不为NULL'; } if(isset($weigeti0)){ echo '参数$weigeti0已经设定,且值不为NULL'; } if(isset($vget)){ echo '参数$vget已经设定,且值不为NULL'; } if(isset($weigeti2)){ echo '参数$weigeti2已经设定,且值不为NULL'; } if(isset($weigeti3)){ echo '参数$weigeti3已经设定,且值不为NULL'; } unset($weigeti4); //这里给$weigeti4 释放掉了 |
PHP isset() 对数组的判断在使用PHP数组的时候,需要判断数组的某个值是否存在也可以使用 PHP isset() 函数。
代码如下 | 复制代码 |
<?php // var_dump用于输出TRUE 或 FALSE
var_dump(isset($V['V-Get']['e'])); var_dump(isset($V['V-Get']['wuliu']['yiwu'])); var_dump(isset($V['V-Get']['wuliu']['sh'])); |
PHP isset()多参数判断很多时候我们需要对多个参数进行判断,可以使用isset($A)&&isset($B)……来判断这些参数是不是都已经设定了,而且都不为NULL,对此您可以使用isset()多参数来判断是不是都设定了。
代码如下 | 复制代码 |
<?php isset($v1)&&isset($v2)&&isset($v3)…… 等价于 isset($v1,$v2,$v3……) ?> |
使用isset()判断多个参数,需要所有参数都被设定,且不为NULL,只要其中有一个参数没有被设定过,或者为NULL,那么整个isset()就为FALSE。
PHP isset()判断$_POST、$_GET、$_REQUEST等值。用isset() 或者empty() 判断通过表单传递来的参数是isset() 最常见的用法。
代码如下 | 复制代码 |
if(isset($_POST['from'])&&'E.V-Get.com'==$_POST['from']){ echo '网站通过POST传递的来源是E.V-Get.com'; } |
根据上面我们可以用isset判断多参数,在对表单传递来的值进行判断就最方便了。
代码如下 | 复制代码 |
<?php } |
PHP isset($var{字符串长度}) 判断字符串长度用PHP判断字符串是否存在或者判断字符串长度是否超过某一数字或者判断字符串长度是否为空一般使用strlen(),但是其实使用isset()性能更优越。
代码如下 | 复制代码 |
<?php $weigeti=isset($_POST['from'])?$_POST['from']:''; //判断$weigeti存在,且字符串长度大于0 // 性能更优越的写法,判断$weigeti第1个字符是否设定了 |