Archive

Archive for the ‘PHP’ Category

HTTP 1.1 Cache 和 Apache

August 4th, 2010 admin No comments
基础知识
         1) 什么是”Last-Modified”?

       在浏览器第一次请求某一个URL时,服务器端的返回状态会是200,内容是你请求的资源,同时有一个Last-Modified的属性标记(Http Reponse Header)此文件在服务期端最后被修改的时间,格式类似这样:

        Last-Modified: Fri, 12 May 2006 18:53:33 GMT

         客户端第二次请求此URL时,根据 HTTP 协议的规定,浏览器会向服务器传送 If-Modified-Since 报头(Http Request Header),询问该时间之后文件是否有被修改过:

        If-Modified-Since: Fri, 12 May 2006 18:53:33 GMT

         如果服务器端的资源没有变化,则自动返回 HTTP 304 (Not Changed.)状态码,内容为空,这样就节省了传输数据量。当服务器端代码发生改变或者重启服务器时,则重新发出资源,返回和第一次请求时类似。从而保证不向客户端重复发出资源,也保证当服务器有变化时,客户端能够得到最新的资源。
:如果If-Modified-Since的时间比服务器当前时间(当前的请求时间request_time)还晚,Apache会认为是个非法请求

         2) 什么是”Etag”?

         HTTP 协议规格说明定义ETag为“被请求变量的实体值” (参见 —— 章节 14.19)。 另一种说法是,ETag是一个可以与Web资源关联的记号(token)。典型的Web资源可以一个Web页,但也可能是JSON或XML文档。服务器单独负责判断记号是什么及其含义,并在HTTP响应头中将其传送到客户端,以下是服务器端返回的格式:

        ETag: “50b1c1d4f775c61:df3″

         客户端的查询更新格式是这样的:

        If-None-Match: “50b1c1d4f775c61:df3″

         如果ETag没改变,则返回状态304然后不返回,这也和Last-Modified一样。本人测试Etag主要在断点下载时比较有用。
        
       Last-Modified和Etags如何帮助提高性能?
         聪明的开发者会把Last-Modified 和ETags请求的http报头一起使用,这样可利用客户端(例如浏览器)的缓存。因为服务器首先产生 Last-Modified/Etag标记,服务器可在稍后使用它来判断页面是否已经被修改。本质上,客户端通过将该记号传回服务器要求服务器验证其(客户端)缓存。
         过程如下:
                 1. 客户端请求一个页面(A)。
                 2. 服务器返回页面A,并在给A加上一个Last-Modified/ETag。
                 3. 客户端展现该页面,并将页面连同Last-Modified/ETag一起缓存。
                 4. 客户再次请求页面A,并将上次请求时服务器返回的Last-Modified/ETag一起传递给服务器。
                 5. 服务器检查该Last-Modified或ETag,并判断出该页面自上次客户端请求之后还未被修改,直接返回响应304和一个空的响应体。

注:
1、Last-Modified和Etag头都是由Web Server发出的Http Reponse Header,Web Server应该同时支持这两种头。
2、Web Server发送完Last-Modified/Etag头给客户端后,客户端会缓存这些头;
3、客户端再次发起相同页面的请求时,将分别发送与Last-Modified/Etag对应的Http Request Header:If-Modified-Since和If-None-Match。我们可以看到这两个Header的值和Web Server发出的Last-Modified,Etag值完全一样;
4、通过上述值到服务器端检查,判断文件是否继续缓存;

关于Etag和Last-Modified网上还有更精辟的解释

1、关于Last-Modified

HTTP的Response中还会有另外一个Header叫Last-Modified,比如

Last-Modified: Thu, 06 Apr 2006 21:17:12 GMT”,

浏览器访问一个URI得到这样的Resposne之后,就知道这个资源最后一次的修改时间,下次需要再次获得这个资源的时候,会发一个Request给Server,不过这个Request中有一条

“If-Unmodified-Since: Thu, 06 Apr 2006 21:17:12 GMT”,

如果在Server端在这个日期之后对这个资源进行了修改,就会照常返回这个资源给Client端,但是如果没有修改,就会返回一个304 (Not Modified) Response而不返回资源,告诉Client端:“这个资源从上次给你之来从来没改过,你放心用你Cache中的好了。” 一个304 Response比一个静态资源通常小多了,这样就节省了网络带宽。

点击查看原始尺寸

2、Last-Modified和Expires的区别

让我们回过头来比较一下Expires和Last-Modified这两个东西,似乎Last-Modified比不上Expires,因为虽然它能够节省一点带宽,但是还是逃不掉发一个HTTP请求出去,而Expires却使得浏览器干脆连HTTP请求都不用发,岂不痛快!那还要Last- Modified这个物体干什么?理想状况的确是这样,不过当用户在IE或者Firefox里面按F5或者点击Refresh按钮的时候(不是在URL栏里重新输入一遍URL然后回车),就算对于有Expires的URI,一样也会发一个HTTP请求出去,所以,Last-Modified还是要用的,而且要和Expires一起用。

3、Etag

除了Last-Modified,HTTP Response中还可能有另外一个Header: ETag,使得Server上的静态资源有点“版本控制”的味道Smile 假如HTTP Response中包含

ETag: “abcdefg1234:0001″

等于告诉Client端,你拿到的这个版本的资源有个ID,叫做abcdefg1234:0001,下次需要发Request索要同一个URI的时候,在Request里面加一条

If-None-Match: “abcdefg1234:0001″

好,Server 端做了一些修改,下次这个Client再来了一个请求,但是这时候资源已经改了,所以返回这个新资源,还有新的tag “ETag: “abcdefg4567:0001″”(这个etag我是胡写的),这样,Client端等于Cache了两份,在需要索要这个资源的时候,可以包含这样的Header: “If-None-Match: “abcdefg1234:0001″ “abcdefg4567:0001″”,这样,即使Server端头脑发热,把这个资源Roll back回原来的版本,依然会返回304 (Not Modified) Response,因为它知道Client端Cache着以前的版本呢,这点功能是Last-Modifed/If-Not-Modified没法做到的。

4、Etag的弊端

不过ETag/If-None-Match这点功能实在是个鸡肋,首先,Server端的资源不大可能Roll Back,更重要的是,有可能造成Client Performance下降。对于只有一个Server的网站,没什么问题,但是现在稍微上点规模的网站都需要Scale Out,也就是说需要前端一个Load Balancer,后面接多台Server来处理请求,俗称Cluster,既然是Cluster,那么每个请求到底返回什么结果应该和分配到哪个 Server无关,不过这个ETag可能就坏事了。假如用户的第一次请求分配给Server A,返回“ETag: “abcdefg1234:0001″”,但是第二次请求分配给了Server B,Server B上这个资源和Server A上的一模一样,但是计算出这个资源的ETag是”abcdefg1234:0002″,这下麻烦了,虽然内容一样,但是ETag不匹配,还是浪费了带宽把资源发送了一遍,冤枉啊!而事实上,不同Server上的ETag很有可能不同,对于Apache,ETag的计算考虑了inode,对于 IIS,ETag考虑了metabase的修改版本,要保证不同server上的这些信息一致,有点小难。不过不是有Last-Modified/If- Not-Modified吗?Server端看到If-Modified-Since,对照一下时间对得上,不管If-None-Match,可以直接发回304(Not Modified)呀,很不幸,RFC2616对这种情况做了规定,如果既有If-None-Match又有If-Modified-Since,除非两者不冲突,不然不会返回304。

图像101

所以说ETag就是一个害人精,按照Yahoo的建议,别费劲想办法同步不同Server上的ETag了,干脆就把ETag删除得了(缺省,Apache和 IIS都是有ETag的),我Sniff了一下Yahoo的若干网页返回HTTP Response,的确没有ETag,人家的确是知行合一Smile

对于Apache,在httpd.conf或者.htaccess中加一行就搞定了:

5、Apache中的Etag设置
补充:
Apache默认开启Etag,可以使用FileEtag来设置

FileETag none|INode|MTime|Size|All

从apache的实现中http_etag.c我们可以发现,Apache的Etag包括了Inode|Mtime|Size这些因素。

对于IIS 6,可就有点费劲了,首先,似乎没有办法通过Config来把ETag去掉,查了很多资料,问了很多人,似乎能够去掉ETag的办法只有写一个ISAPI Filter来弄,Sniff了一下Microsoft的几个网页的结果显示ETag都稳当当的存在,估计目前真的没有什么好方法。

只好退而取其次,保证不同Server上的ETag一致了。 IIS对Etag的计算算法是ETag = {Filetimestamp:ChangeNumber}, Filetimestamp保持一致没什么问题,ChangeNumber是metabase的change number,就有点难保证Cluster中每个Server都一样了,所以,干脆就把它设成固定值好了,这个连接告诉我们该怎么办,很可惜,没有找到彻底删除ETags的配置。

转自:http://hi.baidu.com/injava/blog/item/3b8a3e34d4887a3c5bb5f5d8.html

Categories: PHP, 网站开发 Tags:

Ajax提交数据时,参数丢失问题

July 10th, 2010 admin No comments

在购物车的页面提交数据,时常有客户的订单出现空白现象,最后发现,原来在地址中包含#号等字符时,url后面的参数被全部忽略掉了。

怎么解决?
先表单的值用 escape 处理之后,问题解决。

Categories: PHP, 网站开发 Tags:

网站开发日记(14)-MYSQL子查询和嵌套查询优化

July 6th, 2010 admin No comments

查询游戏历史成绩最高分前100

Sql代码 复制代码
  1. SELECT ps.* FROM cdb_playsgame ps WHERE ps.credits=(select MAX(credits)    
  2.  FROM cdb_playsgame ps1    
  3. where ps.uid=ps1.uid AND ps.gametag=ps1.gametag) AND ps.gametag=‘yeti3′    
  4. GROUP BY ps.uid order by ps.credits desc LIMIT 100;  
Sql代码 复制代码
  1. SELECT ps.*    
  2. FROM cdb_playsgame ps,(select ps1.uid, ps1.gametag, MAX(credits) as credits   
  3. FROM cdb_playsgame ps1 group by uid,gametag) t   
  4. WHERE ps.credits=t.credits AND ps.uid=t.uid AND ps.gametag=t.gametag AND ps.gametag=‘yeti3′    
  5. GROUP BY ps.uid order by ps.credits desc LIMIT 100;  

执行时间仅为0.22秒,比原来的25秒提高了10000倍

查询当天游戏最好成绩

Sql代码 复制代码
  1.  SELECT ps. * , mf. * , m.username   
  2. FROM cdb_playsgame ps   
  3. LEFT JOIN cdb_memberfields mf ON mf.uid = ps.uid   
  4. LEFT JOIN cdb_members m ON m.uid = ps.uid   
  5. WHERE ps.gametag = ‘chuansj’  
  6. AND FROM_UNIXTIME( ps.dateline, ‘%Y%m%d’ ) = ’20081008′  
  7. AND ps.credits = (   
  8. SELECT MAX( ps1.credits )   
  9. FROM cdb_playsgame ps1   
  10. WHERE ps.uid = ps1.uid   
  11. AND ps1.gametag = ‘chuansj’  
  12. AND FROM_UNIXTIME( ps1.dateline, ‘%Y%m%d’ ) = ’20081008′ )   
  13. GROUP BY ps.uid   
  14. ORDER BY credits DESC  
  15. LIMIT 0 , 50   

像查询里

Sql代码 复制代码
  1. AND ps.credits=(SELECT MAX(ps1.credits)    
  2.   FROM {$tablepre}playsgame ps1 where ps.uid=ps1.uid AND ps1.gametag = ‘$game’     
  3.   AND FROM_UNIXTIME(ps1.dateline, ‘%Y%m%d’) = ‘$todaytime’  )  

特别消耗时间

另外,像:

Sql代码 复制代码
  1. FROM_UNIXTIME(ps1.dateline, ‘%Y%m%d’) = ‘$todaytime’  

这样的语句会导致索引无效,因为对每个dataline的值都需要用函数计算一遍,需要调整为:

Sql代码 复制代码
  1. AND ps1.dateline >= UNIX_TIMESTAMP(‘$todaytime’)    

//更改后

Sql代码 复制代码
  1.  SELECT ps. * , mf. * , m.username   
  2. FROM cdb_playsgame ps, cdb_memberfields mf, cdb_members m, (   
  3.   
  4. SELECT ps1.uid, MAX( ps1.credits ) AS credits   
  5. FROM cdb_playsgame ps1   
  6. WHERE ps1.gametag = ‘chuansj’  
  7. AND ps1.dateline >= UNIX_TIMESTAMP( ’20081008′ )   
  8. GROUP BY ps1.uid   
  9. AS t   
  10. WHERE mf.uid = ps.uid   
  11. AND m.uid = ps.uid   
  12. AND ps.gametag = ‘chuansj’  
  13. AND ps.credits = t.credits   
  14. AND ps.uid = t.uid   
  15. GROUP BY ps.uid   
  16. ORDER BY credits DESC  
  17. LIMIT 0 , 50   

对于每个球员,找出球员号码,名字以及他所引起的罚款的号码,但只是针对那些至少有两次罚款的球员。

更紧凑的查询,在FROM子句中放置一个子查询。

Sql代码 复制代码
  1. SELECT PLAYERNO,NAME,NUMBER   
  2. FROM (SELECT PLAYERNO,NAME,   
  3.              (SELECT COUNT(*)   
  4.               FROM PENALTIES   
  5.               WHERE PENALTIES.PLAYERNO =   
  6.                     PLAYERS.PLAYERNO)   
  7.               AS NUMBER   
  8.        FROM PLYERS) AS PN   
  9. WHERE NUMBER>=2  

FROM子句中的子查询决定了每个球员的号码,名字和罚款的编号。接下来,这个号码变成了中间结果中的一列。然后指定了一个条件(NUMBER>=2);最后,获取SELECT子句中的列。

转自:http://dodomail.javaeye.com/blog/250199

Categories: PHP Tags:

Mysql 批量替换小杀手锏

June 12th, 2010 admin No comments

看代码:

update phpzy set phpzy_url =replace(phpzy_url ,’http://www.action-01.cn’,'http://action-01.cn’)

Categories: PHP Tags:

[转]php header 404

April 23rd, 2010 admin No comments

当使用cgi模式时使用

header(“Status: 404 Not Found”);

mod_php使用

header(“HTTP/1.1 404 Not Found”);

其中Not Found无意义,仅供显示。

万全的方法

function header_status($status)
{

// ‘cgi’, ‘cgi-fcgi’
if (substr(php_sapi_name(), 0, 3) == ‘cgi’)
header(‘Status: ‘.$status, TRUE);
else
header($_SERVER['SERVER_PROTOCOL'].’ ‘.$status);
}

header_status(’404 Not Found’);

Categories: PHP Tags:

wordpress最有效的简单301重定向方法

March 23rd, 2010 admin No comments

意外常常发生,所以有备无患。

  1. 打开根目录,打wp-blog-header.php
  2. 把以下代码放在最上面。over

if (strtolower($_SERVER['SERVER_NAME'])!=’www.action-01.cn’)
{   
    $URIRedirect=$_SERVER['REQUEST_URI'];   
    if(strtolower($URIRedirect)==’/index.php’)    {
       $URIRedirect=’/'; 
   }   
       header(‘HTTP/1.1 301 Moved Permanently’);   
       header(‘Location:http://www.action-01.cn’.$URIRedirect);   
       exit(); 
}

Categories: PHP Tags: ,