加速图片显示

原文:http://blog.rexsong.com/?p=746#comments

加速的关键,不是降低重量,而是减少个数。如果重量在200K以内,只要网络不是特别慢,效率都差不多。但是,如果图片个数多一倍,效率将明显低一个档次。

传统切图讲究精细,图片规格越小越好,重量越小越好,其实规格大小无所谓,计算机统一都按Byte计算。客户端每显示一张图片都会向服务器发送请求,所以,图片越多请求次数越多,造成延迟的可能性也就越大。因为一张图片的传输时间,通常远小于请求等待的时间。

减少图片的三个技巧(CSS Sprite):

1. 图片限制(Image Slicing)

典型如文本编辑器,小图标特别多,打开时一张张跑出来,给用户的感觉很不好。如果能用一张图解决,则不会有这个问题,比如百度空间、163博客、Gmail都是这么做的。

Image Slicing’s Kiss of Death
http://www.alistapart.com/articles/sprites

2. 单图转滚(Single-image Rollovers)

触发切换图片的需求,传统方案得重新请求新图片,因为网络问题经常造成停留或等待。如果能把多种状态合并成一张图,就能完美解决,然后再使用背景图技术模拟动态效果。

ColorScheme Ratings
http://demo.rexsong.com/200608/colorscheme_ratings/

3. 延长背景(Extend Background Image)

如果图片的某边可以背景平铺无限延长,则不需要每个角、每条边单独搞出来,图片能少一个就少一个。其实,这个理论还可以扩展到四角容器里,好处是能大大简化HTML Structure。

Extend Background Image
http://demo.rexsong.com/200705/extend_background_image/

综合案例

Google Korea(1和2技巧)
http://demo.rexsong.com/200705/google_korea/

CSS Menus(2和3技巧)
http://demo.rexsong.com/200705/css_background_menus/

prototype.js开发笔记

官方手册地址
Chapter 1. Programming Guide

 

1.1. Prototype是什么?

 

或许你还没有用过它, prototype.js 是一个由Sam Stephenson写的JavaScript包。这个构思奇妙编写良好的一段兼容标准的一段代码将承担创造胖客户端, 高交互性WEB应用程序的重担。轻松加入Web 2.0特性。

如果你最近体验了这个程序包,你很可能会发现文档并不是它的强项之一。像所有在我之前的开发者一样,我只能一头扎进prototype.js的源代码中并且试验其中的每一个部分。 我想当我学习他的时候记写笔记然后分享给其他人将会很不错。

我也一起提供了这个包的对象,类,方法和扩展的 非官方参考

1.2. 关联文章

 

高级JavaScript指南

1.3. 通用性方法

 

这个程序包里面包含了许多预定义的对象和通用性方法。编写这些方法的明显的目的就是为了减少你大量的重复编码和惯用法。

1.3.1. 使用 $()方法

 

$() 方法是在DOM中使用过于频繁的 document.getElementById() 方法的一个便利的简写,就像这个DOM方法一样,这个方法返回参数传入的id的那个元素。

比起DOM中的方法,这个更胜一筹。你可以传入多个id作为参数然后 $() 返回一个带有所有要求的元素的一个 Array 对象。下面的例子会向你描述这些。

<HTML>
<HEAD>
<TITLE> Test Page </TITLE>
<script src="prototype-1.3.1.js"></script>
<script>
function test1()
{
var d = $('myDiv');
alert(d.innerHTML);
}
function test2()
{
var divs = $('myDiv','myOtherDiv');
for(i=0; i<divs.length; i++)
{
alert(divs[i].innerHTML);
}
}
</script>
</HEAD>
<BODY>
<div id="myDiv">
<p>This is a paragraph</p>
</div>
<div id="myOtherDiv">
<p>This is another paragraph</p>
</div>
<input type="button" value=Test1 onclick="test1();"><br>
<input type="button" value=Test2 onclick="test2();"><br>
</BODY>
</HTML>

这个方法的另一个好处就是你可以传入id字符串或者元素对象自己,这使得在创建可以传入任何形式参数的方法的时候, 它变得非常有用。

AJAXRequest v0.7

版权声明:未作特别说明的内容可以转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明。本文地址:http://www.xujiwei.cn/blog/?id=776

AJAXRequest是一个轻量级的AJAX应用程序开发框架,兼容Firefox、IE、Opera、Safari,可以方便地进行一些AJAX中经常需要用到的操作,从而简化开发步骤,减少重复代码编写量。

如果在使用过程中发现了问题,或者有好的意见和建议,可以给我发邮件,vipxjw[at]163[dot]com。

2007-04-20 AJAXRequest v0.7

    1.添加了onrequeststart,onrequestend两个属性,分别为请求开始和结束时的回调函数

    2.改进了get、post、postf方法,参数中callback可以为HTML元素对象(DIV,INPUT等),类将自动更新对象内容为请求响应内容

    3.改进了update方法,更新对象可以为一个函数对象,定义同oncomplete

    4.改进了postf方法,可以使用form对象的onsubmit属性来验证表单

更多详细说明请参见AJAXRequest开发者手册:http://www.xujiwei.cn/works/ajaxrequest/external link

更多使用示例请参见AJAXRequest开发示例:http://www.xujiwei.cn/demo/ajaxrequest/external link

更新说明

1. onrequeststart与onrequestend属性

用onrequeststart和onrequestend两个回调函数可以在请求开始和结束时进行相应的处理,一个比较简单的应用就是显示“loading”,例:

  1. // 使用一个loadingDiv作为提示对象,在请求开始时显示它,并在请求结束后隐藏
  2. var ajax=new AJAXRequest();
  3. ajax.onrequeststart=function() {
  4.     document.getElementById("loadingDiv").style.display="block";
  5. }
  6. ajax.onrequestend=function() {
  7.     document.getElementById("loadingDiv").style.display="none";
  8. }

2. 改进的 get, post, postf 方法

在之前的版本中,get等方法只能使用回调函数来处理服务器返回的响应,只有update方法可以更新响应内容到一个HTML元素,而在v0.7中,get等方法也可以更新响应内容到HTML元素了,而调用方法不变,只需要将原来是回调函数的参数改成HTML元素对象即可,当然,直接写一个对象ID也是可以的,例:

  1. var ajax=new AJAXRequest();
  2. ajax.get("newslist.asp","divNewsList");

3. 改进的 update 方法

同 get 等方法所改进的相反,update改进之后支持更新到一个回调函数,而不是只能是一个HTML元素对象,例:

  1. var ajax=new AJAXRequest();
  2. function callback(obj) {
  3.     document.getElementById("divNewsList").innerHTML=obj.responseText;
  4. }
  5. // 5秒更新一次新闻列表
  6. ajax.update(callback,"newslist.asp",5000);

4. 改进的 postf 方法

改进之后的 postf 方法将支持使用 form 对象的 onsubmit 属性来验证表单,例:

  1. <form id="form1" action="submit.asp" method="post" onsubmit="return checkForm();">
  2. Name: <input type="text" id="username" name="username" /><br />
  3. <input type="button" onclick="submitForm();" value="Submit" />
  4. </form>
  5. <script type="text/javascript">
  6. function checkForm() {
  7.     if(!document.getElementById("username").value) {
  8.         alert("请输入用户名!");
  9.         return false;
  10.     }
  11.     else
  12.         return true;
  13. }
  14. function submitForm() {
  15.     var ajax=new AJAXRequest();
  16.     ajax.postf("form1");
  17. }
  18. </script>

在上面的代码中,如果用户没有输入用户名,那么在提交表单时,验证函数就会返回一个 false , AJAXRequest 就会中断请求的发送。

下载

 下载 AJAXRequest v0.7

Prototype 使用 Ajax.Request类

这是一篇转贴文章,自已很早就在用prototype了,这个博客很多的地方就使用了这个轻型框架,如果将jquery与prototype结合起来用,一定要记得先好好了解jquery,因为他改写某些prototype的方法.

如果你不使用任何的帮助程序包,你很可能编写了整个大量的代码来创建XMLHttpRequest对象并且异步的跟踪它的进程, 然后解析出响应 然后处理它。当你不需要支持多于一种类型的浏览器时你会感到非常的幸运。

为了支持 AJAX 功能。这个包定义了 Ajax.Request 类。

假如你有一个应用程序可以通过url http://yoursever/app/get_sales?empID=1234&year=1998与服务器通信。它返回下面这样的XML 响应。

 

<?xml version="1.0" encoding="utf-8" ?>
<ajax-response>
<response type="object" id="productDetails">
<monthly-sales>
<employee-sales>
<employee-id>1234</employee-id>
<year-month>1998-01</year-month>
<sales>$8,115.36</sales>
</employee-sales>
<employee-sales>
<employee-id>1234</employee-id>
<year-month>1998-02</year-month>
<sales>$11,147.51</sales>
</employee-sales>
</monthly-sales>
</response>
</ajax-response>

 

Ajax.Request对象和服务器通信并且得到这段XML是非常简单的。下面的例子演示了它是如何完成的。

<script>
function searchSales()
{
var empID = $F('lstEmployees');
var y = $F('lstYears');
var url = 'http://yoursever/app/get_sales';
var pars = 'empID=' + empID + '&year=' + y;
var myAjax = new Ajax.Request(
url,
{method: 'get', parameters: pars, onComplete: showResponse}
);


}

function showResponse(originalRequest)
{
//put returned XML in the textarea
$('result').value = originalRequest.responseText;
}
</script>

<select id="lstEmployees" size="10" onchange="searchSales()">
<option value="5">Buchanan, Steven</option>
<option value="8">Callahan, Laura</option>
<option value="1">Davolio, Nancy</option>
</select>
<select id="lstYears" size="3" onchange="searchSales()">
<option selected="selected" value="1996">1996</option>
<option value="1997">1997</option>
<option value="1998">1998</option>
</select>
<br><textarea id=result cols=60 rows=10 ></textarea>

你看到传入 Ajax.Request构造方法的第二个对象了吗? 参数{method: 'get', parameters: pars, onComplete: showResponse} 表示一个匿名对象的真实写法。他表示你传入的这个对象有一个名为 method 值为 'get'的属性,另一个属性名为 parameters 包含HTTP请求的查询字符串,和一个onComplete 属性/方法包含函数showResponse

还有一些其它的属性可以在这个对象里面定义和设置,如 asynchronous,可以为truefalse 来决定AJAX对服务器的调用是否是异步的(默认值是 true)。

这个参数定义AJAX调用的选项。在我们的例子中,在第一个参数通过HTTP GET命令请求那个url,传入了变量 pars包含的查询字符串, Ajax.Request 对象在它完成接收响应的时候将调用showResponse 方法。

也许你知道, XMLHttpRequest在HTTP请求期间将报告进度情况。这个进度被描述为四个不同阶段:Loading, Loaded, Interactive, 或 Complete。你可以使 Ajax.Request 对象在任何阶段调用自定义方法 ,Complete 是最常用的一个。想调用自定义的方法只需要简单的在请求的选项参数中的名为 onXXXXX 属性/方法中提供自定义的方法对象。 就像我们例子中的 onComplete 。你传入的方法将会被用一个参数调用,这个参数是 XMLHttpRequest 对象自己。你将会用这个对象去得到返回的数据并且或许检查包含有在这次调用中的HTTP结果代码的 status 属性。

还有另外两个有用的选项用来处理结果。我们可以在onSuccess 选项处传入一个方法,当AJAX无误的执行完后调用, 相反的,也可以在onFailure选项处传入一个方法,当服务器端出现错误时调用。正如onXXXXX 选项传入的方法一样,这两个在被调用的时候也传入一个带有AJAX请求的XMLHttpRequest对象。

我们的例子没有用任何有趣的方式处理这个 XML响应, 我们只是把这段XML放进了一个文本域里面。对这个响应的一个典型的应用很可能就是找到其中的想要的信息,然后更新页面中的某些元素, 或者甚至可能做某些XSLT转换而在页面中产生一些HTML。

更完全的解释,请参照 Ajax.Request 参考Ajax选项参考

PHP发送头部消息

照彭武兴先生的《PHP BIBLE》中所述,header可以送出Status标头,如
<?php

header("Status: 404 Not Found");

?>

就可以让用户浏览器出现文件找不到的404错误,但是我试了这样是不行的。

后来我到w3.org上查了http的相关资料,终于试出来了如何Header出状态代码(Status),与大家分享。

其实应该是这样的:

<?php

header("http/1.1 403 Forbidden");

?>

第一部分为HTTP协议的版本(HTTP-Version)

第二部分为状态代码(Status)

第三部分为原因短语(Reason-Phrase)

三部分中间用一个空格分开,且中间不能有回车,第一部分和第二部分是必需的,第三部分则是给人看的,可写可不写甚至乱写。

还有,这一句的输出必须在Html文件的第一行。

下面我给出各代码所代表的意思(是从w3.org上查到的,够权威了):

  * 1xx: Informational - Request received, continuing process

  * 2xx: Success - The action was successfully received, understood,

  and accepted

  * 3xx: Redirection - Further action must be taken in order to

  complete the request

  * 4xx: Client Error - The request contains bad syntax or cannot be

  fulfilled

  * 5xx: Server Error - The server failed to fulfill an apparently

  valid request

       | "100" ; Continue

       | "101" ; Switching Protocols

       | "200" ; OK

       | "201" ; Created

       | "202" ; Accepted

       | "203" ; Non-Authoritative Information

       | "204" ; No Content

       | "205" ; Reset Content

       | "206" ; Partial Content

       | "300" ; Multiple Choices

       | "301" ; Moved Permanently

       | "302" ; Moved Temporarily

       | "303" ; See Other

       | "304" ; Not Modified

       | "305" ; Use Proxy

       | "400" ; Bad Request

       | "401" ; Unauthorized

       | "402" ; Payment Required

       | "403" ; Forbidden

       | "404" ; Not Found

       | "405" ; Method Not Allowed

       | "406" ; Not Acceptable

       | "407" ; Proxy Authentication Required

       | "408" ; Request Time-out

       | "409" ; Conflict

       | "410" ; Gone

       | "411" ; Length Required

       | "412" ; Precondition Failed

       | "413" ; Request Entity Too Large

       | "414" ; Request-URI Too Large

       | "415" ; Unsupported Media Type

       | "500" ; Internal Server Error

       | "501" ; Not Implemented

       | "502" ; Bad Gateway

       | "503" ; Service Unavailable

       | "504" ; Gateway Time-out

       | "505" ; HTTP Version not supported

Win2003中apache2分别整合tomcat5和iis6的终极教程

 

 2006年5月18日:一个值得记住的日子

    为了能在已经存在iis6的服务器上运行自己的java程序,经过历时半年的尝试,今天终于搞定了,好开心啊。
   
    最开始是用iis直接连接tomcat,找遍了网上所有的资料,可是死活也连不通,或许是iis的封闭吧,看来tomcat与iis远没有成为朋友。
     于是只好另辟蹊径,用apache监听80来做请求转发了,请求到iis的站点就转到相应目录,请求到tomcat下的站点就转发到tomcat下,于是开始到处找这方面的资料,终于经过反复试验,右克服请求servlet出错的困难,现在终于可以继续做自己的网站了。

参考文档:
1.http://weblife.blogbus.com/s1659/index.html
2.http://wiki.osportfolio.org/confluence/display/Technical/Apache+Tomcat+mod_jk+Integration

第一篇文档基本解决了所有的问题,第二篇文档解决了请求servlet发生找不到目标的问题

下面就从零开始,一步步配置三个服务器,让他们协调工作。

    1.下载安装apache2
   
    在http://bj.onlinedown.net/soft/11528.html中下载apache2.0.55 for windows
    在官方网站上我怎么也找不到,奇怪。下载到本地后双击就可以安装了,安装完成后应该在系统托盘中看到一个小图标了,是个羽毛加绿色播放箭头的图标,这表示正确安装了,如果不能正确安装,请确认80端口没有被别的服务器占有。
   
    2.下载安装tomcat5
   
    Tomcat还是到官方网站下载,地址是:http://tomcat.apache.org/download-55.cgi,选择5.5.17中core:zip,我比较习惯用这个压缩包,解压后设置环境变量就可以用了。在安装路径的bin里面运行startup.bat就可以启动tomcat了。
   
    3.安装IIS6.0
   
    将win2003的安装盘插入光驱,在添加或删除程序中选择“添加/删除windows组件”在“windows组件向导”中选择“应用程序服务器”并打上勾,按向导提示下一步进行,就可以安装了。IIS6.0安装后并不能正常启动,因为默认端口80被apache已经占了。双击打开“管理工具/internet信息服务(IIS)管理器”,在“默认网站(停止)”上右击选择“属性”,在“网站”选项卡中修改“TCP端口”的80为88或者其他端口,确定。选中“默认网站(停止)”然后单击工具栏中的播放图标,就可以启动IIS了,正常启动后“停止”字样就消失了。
   
    4.测试3个服务器
   
    修改Apache安装目录/htdocs中的index.html.en改名为index.html,在浏览器输入http://localhost回车如果看到apache的欢迎界面,说明apache正在运行。在浏览器中输入http://localhost:8080回车如果看到tomcat的欢迎界面,这表示tomcat运行正常。在浏览器中输入http://localhost:88回车如果看到“建设中”这样的提示,表示iis正在运行。
   
    5.配置apache与tomcat的连接
   
    第一步: 在http://www.apache.org/dist/tomcat/tomcat-connectors/jk/binaries/win32/jk-1.2.6
             下载mod_jk_1.2.6_2.0.50.dll文件,将其改名为mod_jk.so后放到apache2/modules目录中。
            
    第二步: 打开记事本,输入下列语句,以workers.properties为文件名保存在tomcat安装目录/conf目录下:
   
             workers.tomcat_home=C:\Program Files\jakarta-tomcat-5.0.28
             workers.java_home=C:\Program Files\Java\jdk1.5.0_06
             ps=\
             worker.list=ajp13
             worker.ajp13.port=8009
             worker.ajp13.host=localhost
             worker.ajp13.type=ajp13
             worker.ajp13.lbfactor=1
            
     第三步:用记事本打开apache/conf/httpd.conf文件末尾,添加下面一段:
    
     LoadModule jk_module modules/mod_jk.so
     JkWorkersFile "D:\Program Files\jakarta-tomcat-5.0.28\conf\workers.properties"

     <VirtualHost *:80>
       ServerAdmin rabbit69@openria.com   
       ServerName localhost
       DirectoryIndex index.html index.htm index.jsp
       JkMount /* ajp13   
       JkAutoAlias "C:\Program Files\jakarta-tomcat-5.0.28\webapps"  
       <Directory "C:\Program Files\jakarta-tomcat-5.0.28\webapps">
          Options Indexes FollowSymLinks
          allow from all
       </Directory>
     </VirtualHost>       

     保存,停止apache服务,在启动,就可以将新配置应用了。
    
     6.测试apache与tomcat的连接
     在服务器中输入http://localhost回车如果看见的是tomcat的欢迎界面,就表示apache和tomcat整合成功了,localhost后面加上jsp-examples目录或者servlet-examples目录就可以看到下面的例子,为了让servlet运行,我可是费了半天劲,修改这个httpd.conf文件。
    
     7.配置apache与IIS6的连接
    
     第一步:修改httpd.conf文件,启用proxy模块
             将文件中的LoadModule proxy_module modules/mod_proxy.so和LoadModule proxy_http_module modules/mod_proxy_http.so这两句
             的“#”去掉就可以了。
            
     第二步:增加IIS的虚拟主机
             在httpd.conf文件末尾加入以下代码:
            
             <VirtualHost *:80>
                ServerAdmin rabbit69@openria.cn
                ServerName localhost
                DocumentRoot "C:/Inetpub/wwwroot"
                DirectoryIndex index.html index.htm index.asp
                Alias /test "C:/Inetpub/wwwroot"
                <Directory "C:/Inetpub/wwwroot">
                   Options MultiViews
                   AllowOverride None
                   order allow,deny
                   Allow from all
                </Directory>
                ProxyPass / http://127.0.0.1:88/
                ProxyPassReverse / http://127.0.0.1:88
             </VirtualHost>
             保存,并重启apache就可以做测试了。
            
      8.测试apaceh与IIS的连接
     
      写一个asp页面,hello.asp:
      <HTML>
         <HEAD>
           <TITLE>Hello World Sample</TITLE>
         </HEAD>
         <BODY>
           <% Response.Write "Hello World" %>
         </BODY>
      </HTML>
      放到C:/Inetpub/wwwroot下面,在浏览器中访问http://localhost/test/hello.asp回车如果出现“hello world”字样就表示配置成功了。如果页面出现空白,在Internet信息服务(IIS)管理器中的web服务扩展里将“active server page”允许,然后清空缓存,刷新页面应该就可以看见hello world了。
     
      具体参数的含义就不多说了,上面的参考文档有介绍。
     
      后记:
     
      一直以为这个整合问题解决不了了,但是我还是不甘心,为什么别人能调通,我就调不通,苦脑啊,今天终于解决了,昨天晚上还弄到1点多,所有的辛苦没有白下,写下这个,做个纪念,也希望能对碰到此问题的网友有帮助,没有互联网就没有大家的成长。

 

ajax跨域问题的解决办法

1. 使用中间层过渡的方式:
中间过渡,很明显,就是在ajax与不同域的服务器进行通讯的中间加一层过渡,这一层过渡可以是php、jsp、c++等任何具备网络通讯功能的语言,由中间层向不同域的服务器进行读取数据的操作。拿php做一个例子,如果需要对不同域的某一个php进行通讯,现在客户端的xmlhttprequest先query本域的一个php,然后由本域的这个php去和不同域的php进行通讯,然后由本域的php输出response;

2. 使用<script>标签
这个方法是利用<script>标签中的src来query一个php获得response,因为<script>标签的src属性不存在跨域的问题。
举个例子来让大家看得更清楚一点吧:

<script language="Javascript" id="get" src="" type="text/javascript"></script>
<script language="javascript" type="text/javascript">
function get(url){
 var obj = document.getElementById("get");
 obj.src = url;
 (obj.readStatus == 200)
 {
  var hdiv = $('ok');
  hdiv.innerHTML = t;
 }
     
}
function query(){
 get("http://192.168.0.38/get.php");
}
function $(id){
 return document.getElementById(id);
}
</script>
<input onclick="javascript:return query()" type="button" value="CLICK ME" /> <span id="ok"></span>

其中get.php的代码是

  1. <?php
  2. echo "var t='www.sunxingfang.cn';";
  3. ?>

最后的运行结果是,当你点击那个button,它会出现一个内容为”www.achome.cn”的对话框。
这个方法又叫做ajaj或者ajax without xmlHttprequest,把x换成了j,是因为使用了<script>标签而没有用到xml和xmlHttprequest的缘故。

怎么样,很简单吧,我看到过很多人不愿意去正视ajax所存在的技术瓶颈,其实ajax更应该是Ajax而不是AJAX,突出第一个A是想强调其实ajax发扬的是一种异步传输的方法,而不是具体到底使用了哪种技术。

如果上面的不行,请看一下有没有多余的空格什么的,这个是从网上找了一个修改的,找的不行,修改了好长时间才搞好,结果也没有看出来有什么不一样的,怀疑是有些空格造成的.特别是(obj.readStatus == 200)这一块的地方.祝大家好运了,呵呵~

基本的页面设计元素布局比例

基本的页面设计元素布局比例,给大家做个参考

标志图案

位置 统计结果
左上角 84%
右上角 6%
上方居中 6%
其他位置 4%

搜索功能

位置 统计结果
右上角 35%
左上角 30%
上方居中 14%
中间居中 12%
其他位置 12%

导航模式

位置 统计结果
左导航栅格 30%
选项卡 30%
通过页面顶端的链接 18%
页面中间的类别 12%
下拉菜单 10%
其他 ( 左边、底部、底部居中 ) 6%

帮助内容

位置 统计结果
右上角 41%
顶部居中 44%
左上角 4%
页中居左 11%
右下角 7%
下方居中 11%
左下方 19%

PHP 实现多服务器共享 SESSION 数据

 

一、问题起源

稍大一些的网站,通常都会有好几个服务器,每个服务器运行着不同功能的模块,使用不同的二级域名,而一个整体性强的网站,用户系统是统一的,即一套用户名、密码在整个网站的各个模块中都是可以登录使用的。各个服务器共享用户数据是比较容易实现的,只需要在后端放个数据库服务器,各个服务器通过统一接口对用户数据进行访问即可。但还存在一个问题,就是用户在这个服务器登录之后,进入另一个服务器的别的模块时,仍然需要重新登录,这就是一次登录,全部通行的问题,映射到技术上,其实就是各个服务器之间如何实现共享 SESSION 数据的问题。

二、PHP SESSION 的工作原理

在解决问题之前,先来了解一下 PHP SESSION 的工作原理。在客户端(如浏览器)登录网站时,被访问的 PHP 页面可以使用 session_start() 打开 SESSION,这样就会产生客户端的唯一标识 SESSION ID(此 ID 可通过函数 session_id() 获取/设置)。SESSION ID 可以通过两种方式保留在客户端,使得请求不同的页面时,PHP 程序可以获知客户端的 SESSION ID;一种是将 SESSION ID 自动加入到 GET 的 URL 中,或者 POST 的表单中,默认情况下,变量名为 PHPSESSID;另一种是通过 COOKIE,将 SESSION ID 保存在 COOKIE 中,默认情况下,这个 COOKIE 的名字为 PHPSESSID。这里我们主要以 COOKIE 方式进行说明,因为应用比较广泛。

那么 SESSION 的数据保存在哪里呢?当然是在服务器端,但不是保存在内存中,而是保存在文件或数据库中。默认情况下,php.ini 中设置的 SESSION 保存方式是 files(session.save_handler = files),即使用读写文件的方式保存 SESSION 数据,而 SESSION 文件保存的目录由 session.save_path 指定,文件名以 sess_ 为前缀,后跟 SESSION ID,如:sess_c72665af28a8b14c0fe11afe3b59b51b。文件中的数据即是序列化之后的 SESSION 数据了。如果访问量大,可能产生的 SESSION 文件会比较多,这时可以设置分级目录进行 SESSION 文件的保存,效率会提高很多,设置方法为:session.save_path="N;/save_path",N 为分级的级数,save_path 为开始目录。当写入 SESSION 数据的时候,PHP 会获取到客户端的 SESSION_ID,然后根据这个 SESSION ID 到指定的 SESSION 文件保存目录中找到相应的 SESSION 文件,不存在则创建之,最后将数据序列化之后写入文件。读取 SESSION 数据是也是类似的操作流程,对读出来的数据需要进行解序列化,生成相应的 SESSION 变量。

三、多服务器共享 SESSION 的主要障碍及解决办法

通过了解 SESSION 的工作原理,我们可以发现,在默认情况下,各个服务器会各自分别对同一个客户端产生 SESSION ID,如对于同一个用户浏览器,A 服务器产生的 SESSION ID 是 30de1e9de3192ba6ce2992d27a1b6a0a,而 B 服务器生成的则是 c72665af28a8b14c0fe11afe3b59b51b。另外,PHP 的 SESSION 数据都是分别保存在本服务器的文件系统中。如下图所示:

确定了问题所在之后,就可以着手进行解决了。想要共享 SESSION 数据,那就必须实现两个目标:一个是各个服务器对同一个客户端产生的 SESSION ID 必须相同,并且可通过同一个 COOKIE 进行传递,也就是说各个服务器必须可以读取同一个名为 PHPSESSID 的 COOKIE;另一个是 SESSION 数据的存储方式/位置必须保证各个服务器都能够访问到。简单地说就是多服务器共享客户端的 SESSION ID,同时还必须共享服务器端的 SESSION 数据。

第一个目标的实现其实很简单,只需要对 COOKIE 的域(domain)进行特殊地设置即可,默认情况下,COOKIE 的域是当前服务器的域名/IP 地址,而域不同的话,各个服务器所设置的 COOKIE 是不能相互访问的,如 www.aaa.com 的服务器是不能读写 www.bbb.com 服务器设置的 COOKIE 的。这里我们所说的同一网站的服务器有其特殊性,那就是他们同属于同一个一级域,如:aaa.infor96.com 和 www.infor96.com 都属于域 .infor96.com,那么我们就可以设置 COOKIE 的域为 .infor96.com,这样 aaa.infor96.com、www.infor96.com 等等都可以访问此 COOKIE。PHP 代码中的设置方法如下:


<?php
ini_set('session.cookie_domain''.infor96.com');
?>

这样各个服务器共享同一客户端 SESSION ID 的目的就达到了。

第二个目标的实现可以使用文件共享方式,如 NFS 方式,但设置、操作上有些复杂。我们可以参考先前所说的统一用户系统的方式,即使用数据库来保存 SESSION 数据,这样各个服务器就可以方便地访问同一个数据源,获取相同的 SESSION 数据了。

解决办法如下图所示:

四、代码实现

首先创建数据表,MySQL 的 SQL 语句如下:

   Create TABLE `sess` (
`sesskey` varchar(32) NOT NULL default '',
`expiry` bigint(20) NOT NULL default '0',
`data` longtext NOT NULL,
PRIMARY KEY  (`sesskey`),
KEY `expiry` (`expiry`)
) TYPE=MyISAM

sesskey 为 SESSION ID,expiry 为 SESSION 过期时间,data 用于保存 SESSION 数据。

默认情况下 SESSION 数据是以文件方式保存,想要使用数据库方式保存,就必须重新定义 SESSION 各个操作的处理函数。PHP 提供了session_set_save_handle() 函数,可以用此函数自定义 SESSION 的处理过程,当然首先要先将 session.save_handler 改成 user,可在 PHP 中进行设置:


<?php 
session_module_name('user');
?>

接下来着重讲一下 session_set_save_handle() 函数,此函数有六个参数:

session_set_save_handler ( string open, string close, string read, string write, string destroy, string gc )

各个参数为各项操作的函数名,这些操作依次是:打开、关闭、读取、写入、销毁、垃圾回收。PHP 手册中有详细的例子,在这里我们使用 OO 的方式来实现这些操作,详细代码如下:


<?php
define('MY_SESS_TIME'3600);   //SESSION 生存时长
//类定义
class My_Sess
{
    function init()
    {
        $domain '.infor96.com';
        //不使用 GET/POST 变量方式
        ini_set('session.use_trans_sid',    0);
        //设置垃圾回收最大生存时间
        ini_set('session.gc_maxlifetime',   MY_SESS_TIME);
        //使用 COOKIE 保存 SESSION ID 的方式
        ini_set('session.use_cookies',      1);
        ini_set('session.cookie_path',      '/');
        //多主机共享保存 SESSION ID 的 COOKIE
        ini_set('session.cookie_domain',    $domain);
        //将 session.save_handler 设置为 user,而不是默认的 files
        session_module_name('user');
        //定义 SESSION 各项操作所对应的方法名:
        session_set_save_handler(
            array('My_Sess''open'),   //对应于静态方法 My_Sess::open(),下同。
            array('My_Sess''close'),
            array('My_Sess''read'),
            array('My_Sess''write'),
            array('My_Sess''destroy'),
            array('My_Sess''gc')
        );
    }   //end function
    function open($save_path$session_name) {
        return true;
    }   //end function
    function close() {
        global $MY_SESS_CONN;
        if ($MY_SESS_CONN) {    //关闭数据库连接
            $MY_SESS_CONN->Close();
        }
        return true;
    }   //end function
    function read($sesskey) {
        global $MY_SESS_CONN;
        $sql 'Select data FROM sess Where sesskey=' $MY_SESS_CONN->qstr($sesskey) . ' AND expiry>=' time();
        $rs =& $MY_SESS_CONN->Execute($sql);
        if ($rs) {
            if ($rs->EOF) {
                return ";
            } else {    //读取到对应于 SESSION ID 的 SESSION 数据
                $v $rs->fields[0];
                $rs->Close();
                return $v;
            }   //end if
        }   //end if
        return ";
    }   //end function
    function write($sesskey$data) {
        global $MY_SESS_CONN;
        
        $qkey $MY_SESS_CONN->qstr($sesskey);
        $expiry time() + My_SESS_TIME;    //设置过期时间
        
        //写入 SESSION
        $arr = array(
            'sesskey' => $qkey,
            'expiry'  => $expiry,
            'data'    => $data);
        $MY_SESS_CONN->Replace('sess'$arr'sesskey'$autoQuote true);
        return true;
    }   //end function
    function destroy($sesskey) {
        global $MY_SESS_CONN;
        $sql 'Delete FROM sess Where sesskey=' $MY_SESS_CONN->qstr($sesskey);
        $rs =& $MY_SESS_CONN->Execute($sql);
        return true;
    }   //end function
    function gc($maxlifetime null) {
        global $MY_SESS_CONN;
        $sql 'Delete FROM sess Where expiry<' time();
        $MY_SESS_CONN->Execute($sql);
        //由于经常性的对表 sess 做删除操作,容易产生碎片,
        //所以在垃圾回收中对该表进行优化操作。
        $sql 'OPTIMIZE TABLE sess';
        $MY_SESS_CONN->Execute($sql);
        return true;
    }   //end function
}   ///:~
//使用 ADOdb 作为数据库抽象层。
require_once('adodb/adodb.inc.php');
//数据库配置项,可放入配置文件中(如:config.inc.php)。
$db_type 'mysql';
$db_host '192.168.212.1';
$db_user 'sess_user';
$db_pass 'sess_pass';
$db_name 'sess_db';
//创建数据库连接,这是一个全局变量。
$GLOBALS['MY_SESS_CONN'] =& ADONewConnection($db_type);
$GLOBALS['MY_SESS_CONN']->Connect$db_host$db_user$db_pass$db_name);
//初始化 SESSION 设置,必须在 session_start() 之前运行!!
My_Sess::init();
?>

五、遗留问题

如果网站的访问量很大的话,SESSION 的读写会频繁地对数据库进行操作,这样效率就会明显降低。考虑到 SESSION 数据一般不会很大,可以尝试用 C/Java 写个多线程的程序,用 HASH 表保存 SESSION 数据,并通过 socket 通信进行数据读写,这样 SESSION 就保存在内存中,读写速度应该会快很多。另外还可以通过负载均衡来分担服务器负载。不过这些都只是我自己的一些想法和假设,并没有实践过

数据库操作类adodb类下载地址

ADOdb is a PHP & Python database class library to provide more powerful abstractions for performing queries and managing databases. ADOdb also hides the differences between the different databases so you can easily switch dbs without changing code.

以下全为zip格式,如果您需要下载其它版本的adodb库,请从官方网站下载

支持php4和php5版本的下载地址:点击下载[官方更新日期:(2007-09-27 09:29) ]

仅支持adodb-php5-only 版本的adodb库下载地址:点击下载[官方更新日期:(2007-09-26 21:16) ]

adodb-python 版本的下载地址:点击下载[官方更新日期:(2007-05-21 04:45) ]

官方下载地址:http://sourceforge.net/project/showfiles.php?group_id=42718