WEB服务器 - Apache、Nnginx、Lighttpd的比较和择优

1. Apache服务器和nginx的优缺点:
我们之前大量使用Apache来作为HTTPServer。 Apache具有很优秀的性能,而且通过模块可以提供各种丰富的功能。
1) 首先Apache对客户端的响应是支持并发的
,运行httpd这个daemon进程之后,它会同时产生多个孩子进程/线程,每个孩子进程/线程分别对客户端的请求进行响应;
2) 另外,Apache可以提供静态和动态的服务
,例如对于PHP的解析不是通过性能较差的CGI实现的而是通过支持PHP的模块来实现的(通常为mod_php5,或者叫做apxs2)。
3) 缺点:
因此通常称为Apache的这种Server为process-based server
,也就是基于多进程的HTTPServer,因为它需要对每个用户请求创建一个孩子进程/线程进行响应;
这样的缺点是,如果并发的请求非常多(这在大型门户网站是很常见的)就会需要非常多的线程,从而占用极多的系统资源CPU和内存。因此对于并发处理不是Apache的强项。
4)解决方法:
目前来说出现了另一种WebServer,在并 发方面表现更加优越,叫做asynchronous servers异步服务器。最有名的为Nginx和Lighttpd。
所谓的异步服务器是事件驱动程序模式的event-driven,除了用户的并发请求通常只需要一个单一的或者几个线程。因此占用系统资源就非常少。这几 种又被称为lightweight web server。
举例,对于10,000的并发连接请求,nginx可能仅仅使用几M的内存;而Apache可能需要使用几百M的内存资源。


2. 实际中单一的使用:
1)关于单一使用Apache来作为HTTPServer的情况我们不用再多做介绍,非常常见的应用;
上面我们介绍到Apache对于PHP等服务器端脚本的支持是通过自己的模块来实现的,而且性能优越。
2)我们同样可以单单使用 nginx或者lighttpd来作为HTTPServer来使用。
nginx和lighttpd和Apache类似都通过各种模块可以对服务器的功能进行丰富的扩展,同样都是通过conf配置文件对各种选项进行配置。
对于PHP等,nginx和lighttpd都没有内置的 模块来对PHP进行支持,而是通过FastCGI来支持的。
Lighttpd 通过模块可以提供CGI, FastCGI和SCGI等服务,Lighttpd is capable of automatically spawning FastCGI backends as well as using externally spawned processes.
nginx则没有自己提供处理PHP的功能,需要通过第三方的模块来提供对PHP进行FastCGI方式的集成。
nginx has module support for FastCGI via a built-in module, SCGI and WSGI via 3rd Party module. The user must be able to spawn the processes separately because nginx is not able to automatically spawn them [9]. nginx does not support normal CGI applications [10], which is actually a security benefit.

Lighttpd vs nginx
http://www.wikivs.com/wiki/Lighttpd_vs_nginx

3.反向代理Reverse Proxy:
0) 代理服务器的概念proxy server:

代理服务器

的概念很容易理解,就是通常作为两台机器中间的机器,需要提供的功能往往有:

缓 存caching,安全, 负载均衡load banlancing。

所谓的负载均衡就是,很多机器使用一个代理的时候,代理服务器需要对各个服务器进行均衡。
我们常见的代理是正向的代理,例如我们机房有20台电脑要上网,现在只有一个电脑可以上网,那么可以使用这台电脑作为代理服务器,所有通过网络的数据传输 都要经过该代理服务器。

而反向代理,是和正向代理相反的
,正向代理针对服务接收方用户来说,反向代理或者叫做服务器端代理是针对服务器端的,意思是有多台服务器,反向代理服务器对用户的请求代理发送给其中的一 台服务器进行处理。

Proxy server
http://en.wikipedia.org/wiki/Proxy_server

1)
实际中对于一个大型网站,我们通常使用很多台sever来构成一个cluster来对用户的各种请求进行响应。
因此通常需要一台或者多台反向代理服务器来对多台Server进行服务。
这个反向代理服务器需要提供的功能一般都包括:
    安全方面;缓存压缩功能;负载均衡 功能;

Reverse proxy
http://en.wikipedia.org/wiki/Reverse_proxy

(需要注意反向代理服务器和防火墙优点类似,但是防火墙一般只有安全方面的考虑,没有缓存和负载均衡方面的功能。)

3) 综上,实际中Web服务器端的架构
通常是多台Web服务器运行并行地提 供服务;同时还需要在Web服务器前段部署一台或者多台反向代理服务器,一方面缓存一些静态数据,或者将Web服务器动态产生的一些内容缓存,另一方面通 过负载均衡功能,可以均匀地将用户的并发请求传递给多台Web服务器进行处理。

这样一方面可以大大降低后面每台Web服务器的负担;另一方 面可以实现多台服务器的负载均衡。

4. 实际中使用nginx或者lighttpd当做反向代理服务器,后台布置多台ApacheHTTPServer:
1)上面说到,nginx和lighttpd的优点在于速度快,轻量级,在处理多用户并发方面要大大优于Apache服务器。
因此我们通常可以把他们作为反向代理服务器放置到多台的Apache Web服务器前段,来一方面缓存数据,另一方面实现多台服务器的负载均衡。
2) 当然了Apache本身通过mod_proxy和mod_cache也可以实现反向代理和缓存功能
,但是在处理高并发方面还是无法与nginx和lighttpd这种轻量的异步模式的服务器来比较。
3)另外,利用nginx和lighttpd的反响代理功 能,我们可以通过设置其configuration文件,当客户端请求的是静态内容(例如一些图片,js,html文件等)的话,直接由nginx或者 lighttpd进行响应;

如果需要访问动态内容(通常需要实时从数据库中读取)的话, 则通过反向代理,nginx等可以将请求发送给后台等待的Apache进行响应,然后Apache将相应的结果返回给nginx,后者再响应用户的时候还 可以进行缓存。
4)有时候还可以使用一些缓存的工具,例如Squid。
另外nginx也提供了对一些缓存功能的支持,例如memcache
等。

5)因此如果从图形来分析的话,通常的架构如下:

nginx作为最前端的web cache系统

这个结构的优点:

1、可以使用nginx前端进行诸多复杂的配置,这些配置从前在squid是没法做或者做起来比较麻烦的,比如针对目录的防盗链。

2、nginx前端可以直接转发部分不需要缓存的请求。

3、因为nginx效率高于squid,所以某些情况下可以利用nginx的缓存来减轻squid压力。

4、可以实现url hash等分配策略。

5、可以在最前端开启gzip压缩,这样后面的squid缓存的纯粹是无压缩文档,可以避免很多无谓的穿透。

6、因为nginx稳定性比较高,所以lvs不需要经常调整,通过nginx调整就可以。

7、squid的文件打开数按默认的1024就绰绰有余,不过处理的请求可一个都不会少。

8、可以启用nginx的日志功能取代squid,这样做实时点击量统计时可以精确定位到url,不必要再用低效率的grep来过滤。

9、因为nginx的负载能力高于squid,所以在用lvs分流时可以不必分得特别均衡,出现单点故障的几率比较低。

nginx和squid配合搭建的web服务器前端系统

前端的lvs和squid,按照安装方法,把epoll打开,配置文件照搬,基本上问题不多。

这个架构和app_squid架构的区别,也是关键点就是:加入了一级中层代理,中层代理的好处实在太多了:

1、gzip压缩

压缩可以通过nginx做,这样,后台应用服务器不管是apache、resin、lighttpd甚至iis或其他古怪服务器,都不用考虑压缩的功能问 题。

2、负载均衡和故障屏蔽

nginx可以作为负载均衡代理使用,并有故障屏蔽功能,这样,根据目录甚至一个正则表达式来制定负载均衡策略变成了小case。

3、方便的运维管理,在各种情况下可以灵活制订方案。

例如,如果有人用轻量级的ddos穿透squid进行攻击,可以在中层代理想办法处理掉;访问量和后台负载突变时,可以随时把一个域名或一个目录的请求扔 入二级cache服务器;可以很容易地控制no-cache和expires等header。等等功能。。。

4、权限清晰

这台机器就是不写程序的维护人员负责,程序员一般不需要管理这台机器,这样假如出现故障,很容易能找到正确的人。

对于应用服务器和数据库服务器,最好是从维护人员的视线中消失,我的目标是,这些服务只要能跑得起来就可以了,其它的事情全部可以在外部处理掉。

General Architecture of LVS Clusters

For transparency, scalability, availability and manageability of the whole system, we usually adopt three-tie architecture in LVS clusters illustrated in the following figure.

The three-tie architecture consists of

  • Load Balancer
    , which is the front-end machine of the whole cluster systems, and balances requests from clients among a set of servers, so that the clients consider that all the services is from a single IP address.
  • Server Cluster
    , which is a set of servers running actual network services, such as Web, Mail, FTP, DNS and Media service.
  • Shared Storage
    , which provides a shared storage space for the servers, so that it is easy for the servers to have the same contents and provide the same services.

Load balancer is the single entry-point of server cluster systems, it can run IPVS
that implements IP load balancing techniques inside the Linux kernel, or KTCPVS
that implements application-level load balancing inside the Linux kernel. When IPVS is used, all the servers are required to provide the same services and contents, the load balancer forward a new client request to a server according to the specified scheduling algorithms and the load of each server. No matter which server is selected, the client should get the same result. When KTCPVS is used, servers can have different contents, the load balancer can forward a request to a different server according to the content of request. Since KTCPVS is implemented inside the Linux kernel, the overhead of relaying data is minimal, so that it can still have high throughput.

The node number of server cluster can be changed according to the load that system receives. When all the servers are overloaded, more new servers can be added to handle increasing workload. For most Internet services such as web, the requests are usually not highly related, and can be run parallely on different servers. Therefore, as the node number of server cluster increases, the performance of the whole can almost be scaled up linearly.

Shared storage can be database systems, network file systems, or distributed file systems. The data that server nodes need to update dynamically should be stored in data based systems, when server nodes read or write data in database systems parallely, database systems can guarantee the consistency of concurrent data access. The static data is usually kept in network file systems such as NFS and CIFS, so that data can be shared by all the server nodes. However, the scalability of single network file system is limited, for example, a single NFS/CIFS can only support data access from 4 to 8 servers. For large-scale cluster systems, distributed/cluster file systems can be used for shared storage, such as GPFS, Coda and GFS, then shared storage can be scaled up according to system requirement too.

Load balancer, server cluster and shared storage are usually connected by high-speed networks, such as 100Mbps Ethernet network and Gigabit Ethernet network, so that the network will not become the bottleneck of system when the system grows up.

来源:

生产环境中的一些web server(主要是三巨头apache, nginx, lighttpd):http://hudeyong926.javaeye.com/blog/813141
nginx作为最前端的web cache系统:http://sudone.com/archie/app-nginx-squid-nginx.html
nginx和squid配合搭建的web服务器前端系统:http://sudone.com/archie/app_nginx_squid.html
General Architecture of LVS Clusters:http://www.linuxvirtualserver.org/architecture.html
 

php使用jquery.imgareaselect插件上传图片

在开发中难免碰到图片上传问题图片上传问题很好解决,而上传服务器上的图片尺寸大小不一,使表现无法使用统一的规格显示被上传图片。 
那么被上传图片的 等比例缩 与等比率放 还有等比率截图 可能会给我们的开发带来障碍! 
使用 jquery.imgareaselect图片处理插件完全可以解决方面问题; 

jquery.imgareaselect 官方网站:http://odyniec.net/projects/imgareaselect

导入jquery和jquery.imgareaselect

 

例1.

$(window).load(function () {  

  $('#myimg').imgAreaSelect({ selectionColor: 'blue', selectionOpacity: 0.2,  

    borderWidth: 2 });  

});  

注释
myimg:需要处理的图片 
selectionColor:选择区域颜色 
selectionOpacity:选择区域透明度 
borderWidth:选择边框大小 
如果使用selectionColor参数  就必须设置selectionOpacity(透明度) 

 

例2.等比率选择设置选择区域最大宽高

$(window).load(function () {  

  $('#myimg').imgAreaSelect({aspectRatio: '4:3', maxWidth: 400, maxHeight: 300});  

}); 

注释
myimg:需要处理的图片 
aspectRatio:选择框宽高比率 
maxWidth:选择区域透宽最大值 
maxHeight:选择区域透高最大值 

 

例3:默认选择区域设置 与 键盘支持 

$(window).load(function () {  

  $('#myimg').imgAreaSelect({ x1: 0, y1: 0, x2: 400, y2: 300,keys: { arrows: 15, shift: 5 } });  

});  

注释
myimg:需要处理的图片 
x1:右上角x轴坐标 
y1:右上角y轴坐标 
x2:右下角x轴坐标 
y2:右下角y轴坐标 
key:开启键盘支持 

 

例4:最关键的一个 等比率缩放 

实现原理 需要两个图片 第一图是原图 第二个图是选择区域后显示的图 

         用第一个图上的选择坐标+css控制 产生第二个图 实际上两个图是一样的 只不过通   

         过css控制了第二张图的显示区域与缩放比率 如果需要实现真正截图功能必须使用 

         服务器支持。 例如php  asp aspx jsp ...... 

function preview(img, selection)  

{  

  var scaleX = 100 / (selection.width || 1);  

  var scaleY = 100 / (selection.height || 1);  

  $('#myimg + div > img').css({  

    width: Math.round(scaleX * 400) + 'px',  

    height: Math.round(scaleY * 300) + 'px',  

    marginLeft: '-' + Math.round(scaleX * selection.x1) + 'px',  

    marginTop: '-' + Math.round(scaleY * selection.y1) + 'px'  

  });  

}    

$(document).ready(function () {  

  $('<div><img src="myimg.jpg" style="position: relative;" /></div>')  

    .css({  

      float: 'left',  

      position: 'relative',  

      overflow: 'hidden',  

      width: '100px',  

      height: '100px'  

    })  

    .insertAfter($('#myimg'));  

});  

$(window).load(function () {  

  $('#myimg').imgAreaSelect({ aspectRatio: '1:1', onSelectChange: preview });  

});  

注释

myimg:需要处理的图片 

onSelectChange:选择区域发生变化的时候回调处理 

function preview(img, selection):回调函数 

var scaleX = 100 / (selection.width || 1); 100->新图的宽 

var scaleY = 100 / (selection.height || 1);100->新图的高 

width: Math.round(scaleX * 400) + 'px', 400->原图的宽 

height: Math.round(scaleY * 300) + 'px', 300->原图的高 

$('<div><img src="myimg.jpg" style="position: relative;" /></div>') 

    .css({ 

      float: 'left', 

      position: 'relative', 

      overflow: 'hidden', 

      width: '100px', 

      height: '100px' 

    }) 

100px 是选择后新图显示区域的宽和高 

值得注意的是: 

   回调函数中实际图的宽高  回调函数中新图的宽高 

   这些参数必须设置正确、否则会出现 选择偏差 须设置正确、否则会出现 选择偏差 

 

接下来是php服务端如何处理:

 

$x = $_GET['x'];//客户端选择区域左上角x轴坐标  

$y = $_GET['y'];//客户端选择区域左上角y轴坐标  

$w = $_GET['w'];//客户端选择区 的宽  

$h = $_GET['h'];//客户端选择区 的高  

$lename" title="filename" >filename = "c:/myimg";//图片路径  

$im = imagecreatefromjpeg($lename" title="filename" >filename);// 读取需要处理的图片  

$newim = imagecreatetruecolor(100, 100);//产生新图片 100 100 为新图片的宽和高  

imagecopyresampled($newim, $im, 0, 0, $x, $y, 100, 100, $w, $h);  

//                  [1]    [2] [3][4] [5] [6] [7]  [8]  [9] [10]  

//[5]  客户端选择区域左上角x轴坐标  

//[6]  客户端选择区域左上角y轴坐标  

//[7]  生成新图片的宽  

//[8]  生成新图片的高  

//[9]  客户端选择区 的宽  

//[10] 客户端选择区 的高            

imagejpeg($newim, $lename" title="filename" >filename);  

imagedestroy($im);  

imagedestroy($newim);  

服务器推技术简介及php实现服务器推技术的聊天室

IBM:Comet:基于 HTTP 长连接的“服务器推”技术

DEMO1:

  首先是首页,包含一个文本输入和一个显示聊天内容的iframe,还有一个隐藏iframe用来提交form表单:

<?php

//chat.php

header('cache-control: private');

header('Content-Type: text/html; charset=utf-8');

?>

<html>

<script type="text/javascript">

function submitChat(obj) {

obj.submit();

document.getElementsByName('content')[0].value = '';

}

</script>

<iframe src="./chat_content.php" height="300" width="100%"></iframe>

<iframe name="say" height="0" width="0"></iframe>

<form method="POST" target="say" action="./say.php" onsubmit="submitChat(this)">

<input type="text" size="30" name="content" /> <input type="button" value="say"onclick="submitChat(this.form)" />

</form>

</html>

  另外一个就是保存用户提交的聊天内容了,我简单的写一下文本,而且没有做什么锁定,这个只是简易版本:

<?php

$content = trim($_POST['content']);

if ($content) {

$fp = fopen('./chat.txt', 'a');

fwrite($fp, $content . "\n");

fclose($fp);

clearstatcache();

}

?>

  接下来看主要的HTTP长连接部分,也就是chat_content.php文件:

<?php

header('cache-control: private');

header('Content-Type: text/html; charset=utf-8');

 

//测试设置30秒超时,一般会设置比较长时间。

set_time_limit(30);

 

//这一行是为了搞定IE这个BT

echo str_repeat(' ', 256);

 

ob_flush();

flush();

$fp = new SplFileObject('./chat.txt', 'r+');

$line = 0;

$totalLine = 0;

while (!$fp->eof()) {

$fp->current();

$totalLine++;

$fp->next();

}

$fp->seek($totalLine);

$i = $totalLine - 1;

while (true) {

if (!$fp->eof()) {

if ($content = trim($fp->current())) {

echo '<div>';

echo htmlspecialchars($content);

echo "</div>";

flush();

$fp->next();

$i++;

}

} else {

$fp->seek($i - 1);

$fp->next();

}

 

{

//这里可以添加心跳检测后退出循环

}

usleep(1000);

}

?>

  06. 设置一个超时时间,由于要保持HTTP长连接,这个时间肯定要比较长,可能要几个小时吧,上面提到的文章里也有说明,这种HTTP长连接只能打开两个,由于浏览器的限制。另外其实即使你设置了一个永不超时,其实上服务器部分(如Apache)的配置文件也可能对HTTP请求设置了最长等待时间,所以也可能效果会不是你想的,一般默认可能都是15分钟超时。如果有兴趣可以自己尝试修改。

  09. 这里输出了一段空白,主要是手册上已经说明了,IE浏览器在前面256个字符是不会直接输出的,所以我们先随便输出些空白,以便让后面的内容输出来,可能其他浏览器也有其他浏览器的设置,具体可以查看PHP手册的frush函数的说明。接下去11、12行就是强制把这些空白符丢给浏览器输出。

  13. ~ 20. 这里主要是为了计算文件行数,以便从这一行后面开始读内容。

  接下去的while循环就是一个死循环了,就是循环输出文件内容,每次判断是否到达文件末尾,如果有用户写入文件,则当前检测肯定不是文件末尾,就将该行读取出来输出,否则将指针往前移动一行,继续循环,每次等待1000微秒,

  39. 如果一直保持长连接,那么即使客户端断开,服务端也不一定能知道客户端已经断开,所以这里可能还需要做一些心跳记录,比如每个用户保持一个心跳flag,每格几秒更新一下最后心跳时间,当检测最后时间很久没更新后,推出这个死循环,关闭这个HTTP连接。

 

 

DEMO2:

 

传统的B/S结构的应用程序,都是采用\"客户端拉\"结束来实现客户端和服务器端的数据交换。 

本文将通过结合Ticks,来实现一个服务器推的PHP聊天室简单构想。 

 

PHPer,尤其是用过set_cookie, header的,一定见过这样的提示信息:\"Warning: Cannot modify header information - headers already sent by.....\", 这是因为通过HTTP协议通信,数据包会包含俩个部分,一个是Header,一个是data。一般来说,都是先Header部分,在Heaer部分指明了Data部分的长度,然后使用\\r\\n\\r\\n来表示header部分结束,接下来是Data部分。 

 

当我们有任何输出的时候,Header部分就发送了,这个时候,你再想header函数来改变一些Header部分的域信息,就会得到上面的提示信息。 

    

一个简单的办法就是使用output_buffering。让它来缓存服务器的输出,不要太早将Header部分发给客户端。 

 

那么,如果不使用output_buffering,是不是就可以实现,每当服务器有输出,就立即发送给客户端呢? 

 

做个如下试验://设置php.ini中output_buffering=0 或者使用ob_end_flush()关闭缓存 

 

set_time_limit(0); 

for($i=0;$i<10;$i++){ 

  echo \"Now Index is :\". $i; 

  sleep(1); 

  结果我们发现,还是要等到脚本全部执行完以后,才能一次看到所有的结果。。 

  为什么呢? 

  这是因为我们只是解决了缓存问题,但是还有一个缓冲问题,PHP会缓冲程序的输出。所以,这个时候,我们还需要调用,flush(), 来强制使得PHP将所有的程序输出发送给客户端。//设置php.ini中output_buffering=0 

ob_end_flush();//关闭缓存 

 

set_time_limit(0); 

for($i=0;$i<10;$i++){ 

  echo \"Now Index is :\". $i; 

  flush(); 

  sleep(1); 

    现在是不是看到了,不断有服务器的数据显示出来? 

 

    有几个概念之间的关系,我这里补充以下: 

    在代码中使用ob_start(), 就相当于在php.ini中使用output_buffering=on一样,使用服务器缓存。 

    在代码中使用ob_end_flush() 就相当于在php.ini中使用output_buffering = false一样,关闭服务器缓存。 

     

     基于前面的讨论,我们就有可能使用Ticks来实现,一个无刷新,无ajax的聊天室: 页面中包含俩个iframe,一个是不断获取聊天室的聊天内容,一个包含用户发表聊天内容的form. 这样,在第一个frame的脚本中: 

ob_end_clear();//关闭缓存 

set_time_limit(0); 

ob_implicit_flush(); //这个语句将强制每当有输出就自动刷新,相当于在每个echo后,调用flush() 

$new_mesg = NULL; 

register_tick_function(\"getNewMesg\"); 

declare(ticks=1){ 

  while(1){ 

     if(!is_null($new_mesg)){ 

          foreach($new_mesg as $msg){ 

                echo $msg; 

          } 

          $new_mesg = null; 

     }      

  } 

 

function getNewMesg(){ 

//通过查询数据库,或者共享内存,来获取现在的聊天室大厅的内容。 

//返回一个数组,包含所有的新的聊天内容 

 

 这样就实现了一个简单的使用服务器推技术的聊天室的框架。 

 当然,关于实时输出,还有一些其他的限制,比如在PHP5手册中讲到的: 

个别web服务器程序,特别是Win32下的web服务器程序,在发送结果到浏览器之前,仍然会缓存脚本的输出,直到程序结束为止。 

 

有些Apache的模块,比如mod_gzip,可能自己进行输出缓存,这将导致flush()函数产生的结果不会立即被发送到客户端浏览器。 [Page]

 

甚至浏览器也会在显示之前,缓存接收到的内容。例如 Netscape 浏览器会在接受到换行或 html 标记的开头之前缓存内容,并且在接受到 </table> 标记之前,不会显示出整个表格。 

 

一些版本的 Microsoft Internet Explorer 只有当接受到的256个字节以后才开始显示该页面,所以必须发送一些额外的空格来让这些浏览器显示页面内容。 

 

 接下来,我贴一个很有趣的代码,有兴趣的同学,可以试试: 

<?php

header('Content-type: multipart/x-mixed-replace;boundary=endofsection'); 

print "endofsection"; 

for( $i = 0; $i <10;$i ++ ) 

        sleep(1); 

        print "Content-type: text/plain "; 

        print "Part $i "; 

        print "--endofsection "; 

        ob_flush(); //强制将缓存区的内容输出 

        flush(); //强制将缓冲区的内容发送给客户端 

  } 

print "Content-type: text plain "; 

print "The end "; 

print "--endofsection-- ";

 

?>

 

  使用firefox打开,看看你看到了什么。 

  这个例子,使用了ob_flush(), 这样可以在代码中控制缓存区内容的输出时机,更加灵活一些。