知乎专栏 |
.---> media [mp3, wma, wmv, rmvb, asf, divx]-\ / +------------+ .-----> photo [gif, jpg, png, swf] ----> | Raid Array | <--. /------------------- <---------\/ +------------+ \ user -> dns -> load balancing -> squid -> [cache] <----[html]----\ / | \ \ \______<______/\ +-------------+ / | \ \ \-----> web app ----> | html |--------^ | \ \____________________________/\ | php,jsp,cgi | | \ \ +-------------+ | \ `-----> memcached [node1, node2, node(n)] | \__________________________________________/\ | `------> Database cluster | +--------------------------------------+ \ | | Author: neo chen <openunix#163.com> | `-------------+ | Nickname: netkiller | | Homepage: http://netkiller.github.io | +--------------------------------------+
一个网站由下面几部分组成:
网站组成
硬件 hardware
操作系统 operation system
应用软件 application software
网站程序以及开发语言 web program and script
硬件包括
网络硬件(路由器 route, 交换机 switch)
服务器 server
KVM over IP
其他包括,机柜等
操作系统包括
Windows Server
Linux
FreeBSD
Other(Sun,Novell,Sco...)
其中应用软件按平台分类
Windows 应用软件包括
dns (dns)
web (IIS)
ftp (IIS)
mail (Exchange)
database (SQL Server)
ldap (Active Directory)
Unix like 应用软件包括
dns (bind)
web (apache, lighttpd, tomcat)
ftp (proftpd, pureftpd, wu-ftp, vsftpd)
mail (sendmail,postfix, qmail)
database (PostgreSQL, MySQL)
ldap (OpenLDAP)
其他应用软件包括
cache (squid, nginx, memcached...)
web (jboss, weblogic...)
database (Oracle, DB2...)
网站程序以及开发语言
php
java (jsp)
.net (aspx)
fastcgi (python,perl,rubby,c/c++ ...)
怎样定义多大的网站叫大型网站呢?我也不知道,但凡大型网站都具备本文所提的几点。
门户网站的需求
海量用户访问
海量用户存储
国内外互通及南北互通
快速响应
7×24不间断运行
易于维护
门户网站的几个技术要点
智能域名服务器 Smart DNS
集群负载均衡 Cluster
缓存技术 Cache
静态化
图片服务器分离
压缩数据传输
时间同步
数据存储
集群有很多实现方法,分为硬件和软件,集群可以在不同网络层面上实现
实现IP轮循(Bind DNS)
硬件四层交换(硬件负载均衡设备 F5 BIG IP)
软件四层交换(linux virtual server)
应用层上实现(tomcat)
越是低层性能越好,越是上层功能更强
集群的分类
高可用性集群
负载均衡集群
超级计算集群
网站一般用到两种集群分别是高可用性集群和负载均衡集群
这是早期出现的负载均衡技术,直到现在,很多网站仍然使用DNS负载均衡。
你可通过ping命令观看它是如何工作的,例如你可反复ping个网域名。
C:\>ping www.163.com Pinging www.cache.split.netease.com [220.181.28.52] with 32 bytes of data: Reply from 220.181.28.52: bytes=32 time=226ms TTL=53 Reply from 220.181.28.52: bytes=32 time=225ms TTL=53 Reply from 220.181.28.52: bytes=32 time=226ms TTL=53 Reply from 220.181.28.52: bytes=32 time=226ms TTL=53 Ping statistics for 220.181.28.52: Packets: Sent = 4, Received = 4, Lost = 0 (0% loss), Approximate round trip times in milli-seconds: Minimum = 225ms, Maximum = 226ms, Average = 225ms C:\>ping www.163.com Pinging www.cache.split.netease.com [220.181.28.53] with 32 bytes of data: Reply from 220.181.28.53: bytes=32 time=52ms TTL=52 Reply from 220.181.28.53: bytes=32 time=53ms TTL=52 Reply from 220.181.28.53: bytes=32 time=52ms TTL=52 Reply from 220.181.28.53: bytes=32 time=52ms TTL=52 Ping statistics for 220.181.28.53: Packets: Sent = 4, Received = 4, Lost = 0 (0% loss), Approximate round trip times in milli-seconds: Minimum = 52ms, Maximum = 53ms, Average = 52ms C:\>ping www.163.com Pinging www.cache.split.netease.com [220.181.28.50] with 32 bytes of data: Reply from 220.181.28.50: bytes=32 time=51ms TTL=53 Reply from 220.181.28.50: bytes=32 time=52ms TTL=53 Reply from 220.181.28.50: bytes=32 time=52ms TTL=53 Reply from 220.181.28.50: bytes=32 time=51ms TTL=53 Ping statistics for 220.181.28.50: Packets: Sent = 4, Received = 4, Lost = 0 (0% loss), Approximate round trip times in milli-seconds: Minimum = 51ms, Maximum = 52ms, Average = 51ms C:\>
DNS负载均衡主要优点
技术简单,容易实现,灵活,方便,成本低
Web服务器可以位于互联网的任意位置上,无地理限制。
DNS的主从结构非常稳定
可以有效的分散DDOS攻击。
你甚至可以在DNS服务商那里实现,自己不需要添加设备。而且没有带宽开销。
DNS负载均衡主要缺点
DNS负载均衡采用的是简单的轮循负载算法,不能够按照服务器节点的处理能力分配负载。
不支持故障转移(failover)和自动恢复failback ,如果某台服务器拓机,DNS仍会将用户解析到这台故障服务器上,导致不能响应客户端。
如果添加节点或撤出节点,不能即时更新到省市级DNS,可导致部分地区不能访问。
占用大量静态IP。
软件四层交换负载均衡为我们解决了几个问题
能够按照服务器节点的处理能力分配负载。
支持故障转移(failover)和自动恢复failback ,如果某节点拓机,调度器自动将它剔除,不响应客户端访问,当节点故障排除调度器立即恢复节点。
可以随时添加节点或撤出节点,即时生效,方便网站扩容。
软件四层交换负载均衡优点
仅仅需要一个静态IP。
节点位于私有网络上与WAN隔离,用户面对的只是调度器。
可以随时添加节点或撤出节点。
通过端口可以组建多个集群。
俗称:双机热备份
关键词:心跳线
两部服务器,或多部服务器,形成一个集群,当主服务器崩溃是,立即切换到其它节点上。
两部服务器要做到,内容实时同步,保持数据一直。
一般用 heartbeat + DRBD 实现。heartbeat负责切换服务器,DRBD用于同步数据。
负载均衡成熟产品
F5 Big IP
Array
这些设备可提供3,4,7层负载均衡HA,硬件已经压缩,HTTP头改写,URL改写...
其中3层交换部分多采用硬件实现。
首先要说明,很多缓存技术依赖静态化。下面展示了缓存可能出现的位置。
用户user -> 浏览器缓存 IE/Firefox Cache -> 逆向代理缓存 Reverse proxy Cache -> WEB服务器缓存 Apache cache -> 应用程序缓存 php cache -> 数据库缓存 database cache
当然交换机,网络适配器,硬盘上也有Cache 但这不是我们要讨论的范围。
缓存存储方式主要是内存和文件两种,后者是存于硬盘中。
网站上使用的缓存主要包括五种:
浏览器 缓存
逆向代理/CDN缓存
WEB服务器缓存
应用程序缓存
数据库缓存
将上面的缓存合理地,有选择性的使用可大大提高网站的访问能力。
总之,想让你的网站更快,更多并发,答案是cache,cache 再 cache
通过 Cache-Control 设置页面缓存时间
max-age max-age 格式写为:max-age=n,n是以秒为单位, 这个值是告知客户端GMT + N 后页面过期,缓存服务器在s-maxage值为空的时候也会使用这个参数的值。 s-maxage s-maxage的格式跟max-age一样,只不过他是给缓存服务器使用的。 must-revalidate 这个参数用来告知客户端和缓存服务器,在GET请求的时候必须与源服务器验证实体是否为最新版本。 Cache-Control:max-age=1200,s-maxage=3600
Last-Modified 这个参数提供了实体最近一次被修改的时间。这个名字起得不错,当实体被修改了之后,这个参数也就会被修改.
ETag
ETag ETag是根据内容生成的一段hash字符串,采用信息摘要算法,保证每一个页面有一个唯一字串。
expires
expires 是HTTP 1.0 中定义的,已经不能满足用户的需要在 HTTP 1.1 加入了max-age,建议使用 max-age替代expires
指令 含义 public 可以在任何地方缓存 private 只能被浏览器缓存 no-cache 不能在任何地方缓存 must-revalidate 缓存必须检查更新版本 proxy-revalidate 代理缓存必须检查更新版本 max-age 内容能够被缓存的时期,以秒表示 s-maxage 覆盖共享缓存的max-age设置
在Squid, Varnish, Apache, Lighttpd, Nginx 中都可是实现HTTP Cache-Control推送,每次修改都需要重新加载,不太灵活。
ExpiresActive On ExpiresByType image/gif "access plus 1 month" ExpiresByType image/png "access plus 1 month" ExpiresByType image/jpeg "access plus 1 month" ExpiresByType text/css "access plus 1 month" ExpiresByType text/javascript "access plus 1 month" ExpiresByType application/x-javascript "access plus 1 month" ExpiresByType application/x-shockwave-flash "access plus 1 month" server.modules = ( ... "mod_expire", ... ) $HTTP["url"] =~ "^/images/" { expire.url = ( "" => "access 30 days" ) }
我喜欢自己控制TTL时间,且每个页面单独设置,可以随时调整设置。
在MVC框架中每个控制器下的方法都可以单独操作Cache
Class blog extend Controller{ blog(){ header('Cache-Control: max-age=28800'); } list(){ header('Cache-Control: max-age=3600'); } details(){ header('Cache-Control: max-age=160'); } }
你还可以封装到Controller中
Class blog extend Controller{ blog(){ this->cache('28800'); } list(){ this->cache('3600'); } details(){ this->cache('160'); } }
首先做一个Rewrite让程序接管所有图片请求
url.rewrite = ( "^/(.+)" => "/index.php/$1" )
然后程序通过PATHINFO取出图片URL
http://images.example.com/your/dir/test.jpg => http://images.example.com/index.php/your/dir/test.jpg
程序取出 /your/dir/test.jpg 设置 Content-type 并输出二进制流
详细参考
<?php // Test image. $images = '/test/foo.png'; $headers = apache_request_headers(); if (isset($headers['If-Modified-Since']) && (strtotime($headers['If-Modified-Since']) == filemtime($images))) { header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($images)).' GMT', true, 304); } else { header('Content-Type: image/png'); print file_get_contents($fn); if (file_exists($images)) { header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($images)).' GMT', true, 200); header("Cache-Control: max-age=3600, must-revalidate"); header('Content-Length: '.filesize($images)); header('Content-type: ' .mime_content_type($images)); flush(); readfile($images); exit; } }
javascript 文件也可以使用类似方法处理
private function js($file){ if (file_exists($file)) { header("Cache-Control: max-age=3600, must-revalidate"); header('Content-type: text/javascript'); flush(); readfile($file); exit; } }
只要向浏览器输出过期时间HTTP协议头,不论是html还是动态脚本,都能被缓存。
HTML META
<meta http-equive="Expires" content=" Mon, 10 Jan 2000 00:00:00 GMT"/> <meta http-equive="Cache-Control" content="max-age=300"/> <meta http-equive="Cache-Control" content="no-cache"/>
动态脚本
Expires: Mon, 10 Jan 2000 00:00:00 GMT Cache-Control: max-age=300 Cache-Control: no-cache header("Expires: " .gmdate ("D, d M Y H:i:s", time() + 3600 * 24 * 7). " GMT"); header("Cache-Control: max-age=300"); header("Cache-Control: no-cache");
很多web server都提供 Expires 模块
![]() | 提示 |
---|---|
有些浏览器可能不支持。 |
If-Modified-Since 小于 Last-Modified 返回 200
neo@neo-OptiPlex-780:/tmp$ curl -I http://www.163.com/ HTTP/1.1 200 OK Server: nginx Content-Type: text/html; charset=GBK Transfer-Encoding: chunked Vary: Accept-Encoding Expires: Mon, 16 May 2011 08:12:05 GMT Cache-Control: max-age=80 Vary: User-Agent Vary: Accept Age: 38 X-Via: 1.1 ls100:8106 (Cdn Cache Server V2.0), 1.1 lydx156:8106 (Cdn Cache Server V2.0) Connection: keep-alive Date: Mon, 16 May 2011 08:11:23 GMT
If-Modified-Since 大于 Last-Modified 返回 304
neo@neo-OptiPlex-780:/tmp$ curl -H "If-Modified-Since: Fri, 12 May 2012 18:53:33 GMT" -I http://www.163.com/ HTTP/1.0 304 Not Modified Content-Type: text/html; charset=GBK Cache-Control: max-age=80 Age: 41 X-Via: 1.0 ls119:80 (Cdn Cache Server V2.0), 1.0 lydx154:8106 (Cdn Cache Server V2.0) Connection: keep-alive Date: Mon, 16 May 2011 08:11:14 GMT Expires: Mon, 16 May 2011 08:11:14 GMT
neo@neo-OptiPlex-780:/tmp$ curl -I http://images.example.com/test/test.html HTTP/1.1 200 OK Cache-Control: s-maxage=7200, max-age=900 Expires: Mon, 16 May 2011 09:48:45 GMT Content-Type: text/html Accept-Ranges: bytes ETag: "1984705864" Last-Modified: Mon, 16 May 2011 09:01:07 GMT Content-Length: 22 Date: Mon, 16 May 2011 09:33:45 GMT Server: lighttpd/1.4.26
neo@neo-OptiPlex-780:/tmp$ curl -H 'If-None-Match: "1984705864"' -I http://images.example.com/test/test.html HTTP/1.1 304 Not Modified Cache-Control: s-maxage=7200, max-age=900 Expires: Mon, 16 May 2011 09:48:32 GMT Content-Type: text/html Accept-Ranges: bytes ETag: "1984705864" Last-Modified: Mon, 16 May 2011 09:01:07 GMT Date: Mon, 16 May 2011 09:33:32 GMT Server: lighttpd/1.4.26
具有代表性的逆向代理服务器:
Squid
Nginx
Varnish
Apache cache module
其它逆向代理服务器
一些提供cache的硬件设备
最近几年出现了的 China Cache 服务商,也称CDN
很多CDN厂商使用Squid 二次开发做为CDN节点,通过全球负载均衡使用分发
这些CDN厂商主要做了一下二次开发
logs 日志集中
流量限制
push,pull操作
url 刷新
s-maxage 与 max-age用法类似,s-maxage针对代理服务器缓存。同样适用于CDN
s-maxage 与 max-age 组合使用可以提高CDN性能
方向代理一般都支持PURGE协议,Squid,Varnish等等向管理端口发送 PURGE 即可是使用页面刷新
PURGE http://netkiller.github.net/index.html
有些方向代理如:Varnish 可以使用正则表达式
同时这些代理服务器都承受管理命令
squid: squidclient
varnish: varnishadm
例如这样的URL
http://images.example.com/logo.gif http://images.example.com/product.jpg
我们可以通过Rewrite或PATHINFO等技术做为静态化。例如首次版本
http://images.example.com/logo.1.gif => logo.gif http://images.example.com/product.1.jpg => product.jpg
原图发生变化后,版本递增
http://images.example.com/logo.2.gif => logo.gif http://images.example.com/product.2.jpg => product.jpg
就的URL将丢弃
http://images.example.com/logo.1.gif http://images.example.com/product.1.jpg
CDN 就回源去下面的URL,并且取到的是新图
http://images.example.com/logo.2.gif http://images.example.com/product.2.jpg
F5 Big-IP, Array 等设备都提供硬件加速,其原理与squid, apache提供的功能大同小异
其中Array 页面压缩采用硬件压缩卡实现,SSL加速也采用硬件实现
在这个领域百花齐放,相信你一定能找到适合你的。这些cache会为你提供一些api,来访问它。
代表性的 memcached 据我所是sina广泛使用,腾讯也曾经使用过后来开发了TC(Tencent Cache),台湾雅虎则使用APC Cache。
另外模板引擎也有自己的缓存系统
www 负责静态文件浏览, 台数不定, 可以采用零成本的DNS轮询, 或者4层LVS, 或者7层HAProxy, 还可以用F5, Array 等负载均衡设备.
cms 负责静态文件生成. 生成后的文件要同步到www中, 或者采用网络共享, 再者使用分布式文件系统, 总之将生成的文件交给www服务器, 根据你压力横向扩展即可
img 负责图片文件浏览. 通过给图片加版本号, 结局图片更新问题, 这样更新网站不用频繁刷新 CDN
这里不谈论负载均衡, 以及存储方案, 有情绪可以延伸阅读: http://netkiller.github.com/architect/index.html
你掌握了这个方案, 你可以很容易实现 向"京东商城", "VANCL凡客诚品", "走秀网" 这样的网站
这些网站的特点是: 浏览量大, 数据存储主要是图片, 登录/注册与购物车,用户中心 访问量只占到 5% 使用负责均衡很容易解决.
静态化网站可不避免的使用ajax做局部更新, ajax请求也要考虑缓存问题
静态化另一个目的是改善SEO
首次访问服务器
访问www服务器
nginx 判断文件是否存在,如果存在将文件显示出来
如果文件不存在,去cms服务器上查找, 如果存在便返回给www服务器,并显示出来
如果cms上文件不存在,cms服务器便使用rewrite生成该文件, 同时将内容返回给www服务器,www将内容缓存在自己的服务器上,并将内容显示出来
第二次访问
访问www服务器
nginx 判断文件是否存在,如果存在将文件显示出来
如果文件不存在,去cms服务器上查找, 如果存在便返回给www服务器,并显示出来
如果cms上文件不存在,cms服务器便使用rewrite生成该文件, 同时将内容返回给www服务器,www将内容缓存在自己的服务器上,并将内容显示出来
静态化方法包括:
生成方式
抓取方式
伪静态化
混合方式
主要由程序实现
例如
content = "<html><title>my static</title><body>hello world</body></html>" file = open( your static file) file.write(content) file.close()
主要由程序实现
程序中抓取
content = get_url('http://netkiller.8800.org/index.php') file = open( index.html) file.write(content) file.close()
使用软件抓取,不仅限于wget。
wget http://netkiller.8800.org/index.php -O index.html
这时只给出简单例子,使用复杂参数实现更复杂的拾取,然后将脚本加入crontab中可。
伪静态化是主要是通过在URL上做一些手脚,使你看去是静态的,实质上它是动态脚本。
伪静态化实现主要包括两种方法:
Rewrite rule
path_info
下面是一个PATH_INFO例子
http://netkiller.8800.org/zh-cn/photography/browse/2009.html
根本就不存在这个目录'zh-cn/photography/browse/'和文件'2009.html'
下面是一个Rewrite例子
http://example.org/bbs/thread-1003872-1-1.html
其实目前网站使用的基本上都是上面几种方法混合方式。
例如首先将动态url(example.org/news.php?cid=1&id=1) 通过rewrite转换为 (example.org/new_1_1.html)
接下来就比较容易解决了,一种方法是使用wget example.org/new_1_1.html,另一种方法你无需静态化,直接使用squid规则配置让他永不过期
如何使用 cdn 来缓存你的网站内容
让你的网页缓存在 cdn 节点上的方式有下面几种
让cdn的客服帮你配置缓存的规则, 他们很喜欢一刀切, 例如所有html都缓存2小时
在他们管理后台自行使用正则配置缓存的时间, 这个他们一般不会提供, 某些公司的CDN会提供这个功能. 非常方便.
通过HTTP头自行控制缓存时间, 一般是使用 max-age / s-maxage / Last-Modified 判断缓存时间
我比较喜欢最后一种, 通常我们使用max-age 与 s-maxage 同时使用, 这样我可以按照我的意向来决定文件的缓存时间 这里有更详细的解释说明.
下面给出一个精简后的配置例子
如果文件不存在就会连接后端cms服务器生成文件,并且显示出来,同时加上缓存. 生成的文件会从cms中同步到www服务器上.
你可以采用
rsync同步方案
nfs/samba/gluster 共享方案
iscsi 共享存储方案
分布式文件系统方案
参考阅读: 分布式文件系统 , Netkiller Linux Storage 手札
upstream cms.mydomain.com { server 192.168.2.11 weight=5 max_fails=3 fail_timeout=30s; server 192.168.2.21 weight=5 max_fails=3 fail_timeout=30s; server 192.168.2.23 backup; server 192.168.2.23 down; } server { listen 80; server_name www.mydomain.com; charset utf-8; access_log /var/log/nginx/www.mydomain.com.access.log main; location / { root /www/mydomain.com/www.mydomain.com; index index.html index.htm; if ($request_uri ~* "\.(ico|css|js|gif|jpe?g|png|html)$") { expires 1d; } if ($request_uri ~* "\.(xml|json)$") { expires 1m; } valid_referers none blocked *.mydomain.com; if ($invalid_referer) { #rewrite ^(.*)$ http://www.mydomain.com/cn/$1; return 403; } proxy_intercept_errors on; if (!-f $request_filename) { proxy_pass http://cms.mydomain.com; break; } } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} }
CMS 内容管理系统的主要功能
内容分类管理
内容模板管理
内容编辑与发布
内容生成
服务应该实现
当发现目录中文件不存, 通过rewrite生成html, 这样可能根据需要生成html页面
当页面更新的时候,应该通过api 刷新cdn的缓存, 图片的版本好应该加一
将页面分成多个模块, 通过SSI拼装页面, 避免有重大改版时, 整站生成HTML.
避免使用seesion技术, 这样在负载均衡的时候可以使用最小连接数算法
例如:
rewrite ^/product/(phone|notebook)/(\d+).html /product/$1.php?id=$2 last;
URL 唯一, url设计要考虑唯一性, 不要出现同一个url处理两个任务, 例如下面的例子, 每个用户的profile一个URL, 当被访问的时候便可以缓存在CDN或者用户浏览器上.
http://www.mydomain.com/profile/neo.html http://www.mydomain.com/profile/jam.html
server { listen 80; server_name www.mydomain.com; #charset koi8-r; access_log /var/log/nginx/www.mydomain.com.access.log main; location / { root /www/mydomain.com/www.mydomain.com; index index.html; } } server { listen 80; server_name cms.mydomain.com; charset utf-8; access_log /var/log/nginx/cms.mydomain.com.access.log main; location / { root /www/mydomain.com/cms.mydomain.com; index index.html index.php; } location ~ ^/(cn|tw)/(comment|affiche)/.*\.html { root /www/mydomain.com/www.mydomain.com; if (!-f $request_filename) { rewrite ^/(cn|tw)/(comment|affiche)/(\d+).html /publish/$2.php?id=$3&lang=$1 last; } } location /xml/ { root /www/mydomain.com/www.mydomain.com/xml; } location ~ ^/(config|include|crontab)/ { deny all; break; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # location ~ \.php$ { root /www/mydomain.com/cms.mydomain.com; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /www/mydomain.com/cms.mydomain.com$fastcgi_script_name; include fastcgi_params; fastcgi_param DOCUMENT_ROOT /www/mydomain.com/cms.mydomain.com; fastcgi_param HOSTNAME cms.mydomain.com; } # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # location ~ /\.ht { deny all; } }
img.mydomain.com
server { listen 80; server_name img.mydomain.com; charset utf-8; access_log /var/log/nginx/img.mydomain.com.access.log main; location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|ico)$ { expires 7d; } location ~ .*\.(js|css)$ { expires 1d; } location ~ .*\.(html|htm)$ { expires 15m; } location / { root /img/mydomain.com/img.mydomain.com; index index.html; rewrite "/theme/([0-9] {4})([0-9] {2})([0-9] {2})/(.+)\.(.+)\.(.+)" /theme/$1/$2/$3/$4.$6; rewrite "/news/([0-9] {4})([0-9] {2})([0-9] {2})/(.+)\.(.+)\.(.+)" /news/$1/$2/$3/$4.$6; rewrite "/product/([0-9] {4})([0-9] {2})([0-9] {2})/(.+)\.(.+)\.(.+)" /product/$1/$2/$3/$4.$6; } }
/theme/2012/08/15/images.1.jpg 实际上就是 /theme/2012/08/15/images.jpg 文件
/theme/2012/08/15/images.2.jpg 也是 /theme/2012/08/15/images.jpg
/theme/2012/08/15/images.3.jpg 也是 /theme/2012/08/15/images.jpg
但CDN与你的浏览器会每次下载新的文件, 这样只要更新CDN中的html页面即可, 不用去理睬图片, 你的浏览器会用新的地址下载图片. 这样就解决了烦琐的刷新工作.
为什么要将图片服务器分离出来?
图片通常比较大,下载需要更长的时间,而web容器并发数也是相当宝贵的仅次于数据库。
传统浏览器一个窗口只占用一个链接数,目前主流浏览器都支持多线程下载,下载HTML页面同时,采用多线程下载其它多媒体数据。
我们举一个例子,你的服务器并发能力只用1000,早期浏览器不支持多线程,所以同一时刻,你的服务器可以承受1000个人同时访问。 但现在不同了,基本所有的浏览器都支持多线程,假如你的页面中有9张小图片,同一时刻你的服务器仅仅能应付1000/10 = 100个用户。
所以我们要将图片和其他多媒体文件分离出来,单独使用一台服务器处理请求。
![]() | 提示 |
---|---|
图片服务器建议使用lighttpd与squid缓存配合使用效果更好或购买CDN的服务。 |
日期有利于归档
/www/images /www/images/2008 /www/images/2008/01 /www/images/2008/01/01
分类不同用途的文件
/www/images /www/images/theme/2009 # article id 000001 /www/images/article/2009/01/000001 # product id 00001 /www/images/product/2009/01/01/00001 # member name neo /www/images/member/2009/01/01/neo
根据你的数据量,创建目录深度, 并且目录深度有规律可循。
虽然64bit 文件系统不限制文件数量与目录深度,但是我还是建议按我的方式规划目录。
这样规划目录便于缓存控制,如:
images/2008/* 永久缓存
images/2009/* 缓存一个月
images/2010/* 缓存一小时
images/2010/06/* 缓存5分钟
某天我的前同事给我打电话,说他们的负载很高,经查发现网站首页有20M,原因是首页直接引用高清图片,没有安装分辨率生成缩图。于是我便想出了下面的方案。
我认为方案需求有如下几个要素:
图片压缩
尺寸修改
图片缓存
带宽因素
例如用户使用手机访问网站,手机屏幕尺寸非常多样化,常见的有QVGA(320×240)、HGVA(480×320)、WVGA(800×480)、QCIF(176×144)、SVGA(640x480)、WXGA(1280×800)。如果一个用户的手机屏幕是320×240,打开网站后显示1027*768图片很不切合实际。同时用户也多出不少带宽开销。
我们需要给用户更好的体验,就要多从用户的角度去考虑,如根据用户网速,带宽,分辨率,为用户提供更适合他终端的多媒体资源。
B/S结构应用程序无法获取客户端的分辨率等信息,我们将采用Javascript取出参数,然后告知服务器端。
有下面几种实现方式:
通过cookie
post传递给服务器,然后存储在session中
get 传递给服务器,然后存储在session中
仅举一个例子
<script type="text/javascript"> $(function(){ var width=window.screen.height; var height=window.screen.width; $.post('http://www.example.com/screen/resize.html',{w:width,h:height}); }); </script>
HTML页面中的图片的引用路径
<img src="http://img.example.com/sample.jpg" />
图片服务器rewrite处理
http://img.example.com/sample.jpg => http://img.example.com/index.php/sample.jpg
index.php会首先载入sample.jpg文件,然后综合网速,带宽,分辨率等因素,重新压缩图片,修改尺寸,发送mime头,输出正文。
为了防止图片地址冲突,我们首先需要URL唯一化,这样每访问一次会生成一张符合你需求尺寸的图片。
http://img.example.com/sample_(width)x(height)_(quality).jpg
<img src="http://img.example.com/sample_1980x1080_100.jpg" /> <img src="http://img.example.com/sample_800x600_80.jpg" /> <img src="http://img.example.com/sample_640x480_50.jpg" />
配置nginx通过try_files配置项可以实现检查静态文件是否存在,如果不存在边调用index.php生成图片,当再次访问时会直接读取静态文件,不会再重新生成。
server { listen 80; server_name inf.example.com; charset utf-8; access_log /var/log/nginx/inf.example.com.access.log main; error_log /var/log/nginx/inf.example.com.error.log; location / { root /www/example.com/inf.example.com/images; index index.html; try_files $uri $uri/ /index.php?_url=$request_uri; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # location ~ /index\.php$ { root html; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /www/example.com/inf.example.com/frontend/public$fastcgi_script_name; include fastcgi_params; } # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # location ~ /\.ht { deny all; } }
通过这种方法还可以实现更复杂的需求,例如调整亮度,对比度,饱和度,色阶,图层叠加等等......
服务器将html或脚本输出压缩,用户从服务器取得数据后由浏览器解压
压缩数据传输实现方法:
apache mod_deflate
lighttpd compress module
SSL 加密传输,为电子商务提供交易安全保护,什么时候该使用 SSL呢:
使用SSL
用户登录
购物流程
支付
什么时候不使用SSL? 经过SSL加密后,你就失去了很多功能,你不能在对页面做Cache/CDN,SSL加密与解密需要耗费你的服务器CPU与内存资源,能不使用尽量不使用。
对于SSL消耗你服务器资源这方面有两个方案解决
将SSL证书安装到CDN上,目前蓝讯,网宿等等CDN厂商都提供SSL服务。我与上两家技术人员沟通过,也安装了证书实际测试一下,你可以放心是使用。
将SSL证书安装到负载均衡设备,这些设备都采用专用硬件处理SSL请求,我测试过F5,Array,Banggoo
采用上面两种方案,无需改变你目前的服务器配置,他们的原理是
user (https://www.example.com) --> CDN or SLB (SSL) --> http://www.example.com
用户访问https,到达CDN或者负载均衡,CDN/SLB 通过http://请求源站,然后将内容SSL加密,返回给用户,这样用户得到的是加密内容。
用户提交数据,交给CDN/SLB,CDN/SLB将SSL加密数据卸载证书,然后将解密后数据发回源站。
CDN与SLB加载卸载证书原理很简单,不难理解。
我来教你DIY一个,你可以使用Squid,Nginx,Apache等等反向代理服务器,将证书安装在反向代理上,请求源站仍然采用http。
![]() | SSL注意事项 |
---|---|
你如果认为把SSL挂载到网站前端就,大功告成,完事了,那你错了。 幸运的话你会成功,但有时的时候你发现你的证书不被信任。如果你是个细心的人,你会发现单个图片,或者你创建换成测试文件 echo helloworld > index.html 证书都是OK的。 这个问题出在你的html页面中,安装有SSL证书的网站,不能有外链js,flash等等不安全内容。 |
每个搜索关键字都应该有一个惟一的URL,例如
https://www.google.com.hk/search?sourceid=chrome&ie=UTF-8&q=netkiller&sei=9v-QT_q1L6SZiAel2bGnBA&gbv=2 https://www.google.com.hk/search?aq=f&sourceid=chrome&ie=UTF-8&q=neo https://www.google.com.hk/search?sourceid=chrome&ie=UTF-8&q=bg7nyt
每搜索一次新的关键字就会产生一条唯一的URL,这样就可以实现反向代理缓存,甚者通过HTTP头,实现浏览器段的缓存。
<meta name="robots" content="noarchive">
例 1.1. example robots.txt
http://www.google.com/robots.txt
User-agent: * Disallow: /search Disallow: /groups Disallow: /images Disallow: /catalogs Disallow: /catalogues Disallow: /news Allow: /news/directory Disallow: /nwshp Disallow: /setnewsprefs? Disallow: /index.html? Disallow: /? Disallow: /addurl/image? Disallow: /pagead/ Disallow: /relpage/ Disallow: /relcontent Disallow: /imgres Disallow: /imglanding Disallow: /keyword/ Disallow: /u/ Disallow: /univ/ Disallow: /cobrand Disallow: /custom Disallow: /advanced_group_search Disallow: /googlesite Disallow: /preferencessection Disallow: /setprefs Disallow: /swr Disallow: /url Disallow: /default Disallow: /m? Disallow: /m/? Disallow: /m/blogs? Disallow: /m/ig Disallow: /m/images? Disallow: /m/local? Disallow: /m/movies? Disallow: /m/news? Disallow: /m/news/i? Disallow: /m/place? Disallow: /m/setnewsprefs? Disallow: /m/search? Disallow: /m/swmloptin? Disallow: /m/trends Disallow: /wml? Disallow: /wml/? Disallow: /wml/search? Disallow: /xhtml? Disallow: /xhtml/? Disallow: /xhtml/search? Disallow: /xml? Disallow: /imode? Disallow: /imode/? Disallow: /imode/search? Disallow: /jsky? Disallow: /jsky/? Disallow: /jsky/search? Disallow: /pda? Disallow: /pda/? Disallow: /pda/search? Disallow: /sprint_xhtml Disallow: /sprint_wml Disallow: /pqa Disallow: /palm Disallow: /gwt/ Disallow: /purchases Disallow: /hws Disallow: /bsd? Disallow: /linux? Disallow: /mac? Disallow: /microsoft? Disallow: /unclesam? Disallow: /answers/search?q= Disallow: /local? Disallow: /local_url Disallow: /froogle? Disallow: /products? Disallow: /products/ Disallow: /froogle_ Disallow: /product_ Disallow: /products_ Disallow: /print Disallow: /books Disallow: /bkshp?q= Allow: /booksrightsholders Disallow: /patents? Disallow: /patents/ Allow: /patents/about Disallow: /scholar Disallow: /complete Disallow: /sponsoredlinks Disallow: /videosearch? Disallow: /videopreview? Disallow: /videoprograminfo? Disallow: /maps? Disallow: /mapstt? Disallow: /mapslt? Disallow: /maps/stk/ Disallow: /maps/br? Disallow: /mapabcpoi? Disallow: /maphp? Disallow: /places/ Disallow: /maps/place Disallow: /help/maps/streetview/partners/welcome/ Disallow: /lochp? Disallow: /center Disallow: /ie? Disallow: /sms/demo? Disallow: /katrina? Disallow: /blogsearch? Disallow: /blogsearch/ Disallow: /blogsearch_feeds Disallow: /advanced_blog_search Disallow: /reader/ Allow: /reader/play Disallow: /uds/ Disallow: /chart? Disallow: /transit? Disallow: /mbd? Disallow: /extern_js/ Disallow: /calendar/feeds/ Disallow: /calendar/ical/ Disallow: /cl2/feeds/ Disallow: /cl2/ical/ Disallow: /coop/directory Disallow: /coop/manage Disallow: /trends? Disallow: /trends/music? Disallow: /trends/hottrends? Disallow: /trends/viz? Disallow: /notebook/search? Disallow: /musica Disallow: /musicad Disallow: /musicas Disallow: /musicl Disallow: /musics Disallow: /musicsearch Disallow: /musicsp Disallow: /musiclp Disallow: /browsersync Disallow: /call Disallow: /archivesearch? Disallow: /archivesearch/url Disallow: /archivesearch/advanced_search Disallow: /base/search? Disallow: /base/reportbadoffer Disallow: /base/s2 Disallow: /urchin_test/ Disallow: /movies? Disallow: /codesearch? Disallow: /codesearch/feeds/search? Disallow: /wapsearch? Disallow: /safebrowsing Allow: /safebrowsing/diagnostic Allow: /safebrowsing/report_error/ Allow: /safebrowsing/report_phish/ Disallow: /reviews/search? Disallow: /orkut/albums Disallow: /jsapi Disallow: /views? Disallow: /c/ Disallow: /cbk Disallow: /recharge/dashboard/car Disallow: /recharge/dashboard/static/ Disallow: /translate_a/ Disallow: /translate_c Disallow: /translate_f Disallow: /translate_static/ Disallow: /translate_suggestion Disallow: /profiles/me Allow: /profiles Disallow: /s2/profiles/me Allow: /s2/profiles Allow: /s2/photos Allow: /s2/static Disallow: /s2 Disallow: /transconsole/portal/ Disallow: /gcc/ Disallow: /aclk Disallow: /cse? Disallow: /cse/panel Disallow: /cse/manage Disallow: /tbproxy/ Disallow: /comparisonads/ Disallow: /imesync/ Disallow: /shenghuo/search? Disallow: /support/forum/search? Disallow: /reviews/polls/ Disallow: /hosted/images/ Disallow: /hosted/life/ Disallow: /ppob/? Disallow: /ppob? Disallow: /ig/add? Disallow: /adwordsresellers Disallow: /accounts/o8 Allow: /accounts/o8/id Disallow: /topicsearch?q= Disallow: /xfx7/ Disallow: /squared/api Disallow: /squared/search Disallow: /squared/table Disallow: /toolkit/ Allow: /toolkit/*.html Disallow: /qnasearch? Disallow: /errors/ Disallow: /app/updates Disallow: /sidewiki/entry/ Disallow: /quality_form? Disallow: /labs/popgadget/search Disallow: /buzz/post Sitemap: http://www.gstatic.com/s2/sitemaps/profiles-sitemap.xml Sitemap: http://www.google.com/hostednews/sitemap_index.xml Sitemap: http://www.google.com/ventures/sitemap_ventures.xml Sitemap: http://www.google.com/sitemaps_webmasters.xml Sitemap: http://www.gstatic.com/trends/websites/sitemaps/sitemapindex.xml Sitemap: http://www.gstatic.com/dictionary/static/sitemaps/sitemap_index.xml
User-agent: * Allow: * Disallow: /management/ Sitemap: http://netkiller.sourceforge.net/sitemaps.xml.gz
#!/bin/bash DOMAIN="http://www.netkiller.cn" PUBLIC_HTML=~/public_html if [ ! -z $1 ]; then DOMAIN=$1 fi lastmod=`date "+%Y-%m-%d"` echo '<?xml version="1.0" encoding="UTF-8"?>' echo '<?xml-stylesheet type="text/xsl" href="gss.xsl"?>' echo '<urlset xmlns="http://www.google.com/schemas/sitemap/0.84" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.google.com/schemas/sitemap/0.84 http://www.google.com/schemas/sitemap/0.84/sitemap.xsd">' #echo '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">' for htmlfile in $(find $PUBLIC_HTML/ -type f -name "*.html") do url=`echo $htmlfile | sed -e "s:$PUBLIC_HTML/::"` echo ' <url>' echo ' <loc>'${DOMAIN}'/'${url}'</loc>' echo ' <lastmod>'${lastmod}'</lastmod>' echo ' <changefreq>daily</changefreq>' echo ' <priority>0.5</priority>' echo ' </url>' done for htmlfile in $(find $PUBLIC_HTML/ -type f -name "*.epub") do url=`echo $htmlfile | sed -e "s:$PUBLIC_HTML/::"` echo ' <url>' echo ' <loc>'${DOMAIN}'/'${url}'</loc>' echo ' <lastmod>'${lastmod}'</lastmod>' echo ' <changefreq>daily</changefreq>' echo ' <priority>0.5</priority>' echo ' </url>' done for htmlfile in $(find $PUBLIC_HTML/ -type f -name "*.mobi") do url=`echo $htmlfile | sed -e "s:$PUBLIC_HTML/::"` echo ' <url>' echo ' <loc>'${DOMAIN}'/'${url}'</loc>' echo ' <lastmod>'${lastmod}'</lastmod>' echo ' <changefreq>daily</changefreq>' echo ' <priority>0.5</priority>' echo ' </url>' done for htmlfile in $(find $PUBLIC_HTML/ -type f -name "*.chm") do url=`echo $htmlfile | sed -e "s:$PUBLIC_HTML/::"` echo ' <url>' echo ' <loc>'${DOMAIN}'/'${url}'</loc>' echo ' <lastmod>'${lastmod}'</lastmod>' echo ' <changefreq>daily</changefreq>' echo ' <priority>0.5</priority>' echo ' </url>' done for htmlfile in $(find $PUBLIC_HTML/ -type f -name *.pdf) do url=`echo $htmlfile | sed -e "s:$PUBLIC_HTML/::"` echo ' <url>' echo ' <loc>'${DOMAIN}'/'${url}'</loc>' echo ' <lastmod>'${lastmod}'</lastmod>' echo ' <changefreq>daily</changefreq>' echo ' <priority>0.5</priority>' echo ' </url>' done echo "</urlset>"