homeproductsservicessupportabout us

站点公告

August 23rd, 2007 by admin

网站架构华人社区其宗旨是为系统架构师,系统管理进阶者服务,增进系统架构与技术交流,我们期望大家在这一社区获得最大程度的技术成长与积累。

有任何疑问或建议请联系webmaster: admin {at} enissue.com
同时也希望你能积极参与协作,共享技术精华.
注册帐号或将文章发至如下Mail: khan.chan {at} enissue.com

Posted in 社区新闻 | No Comments »

update my enissue.com

December 20th, 2008 by admin

呵呵, 接近瘫痪一年多,重新在自己服务器上架设起来.

09年会将重心移到其他事务上, 也希望新的一年能将程序和db玩转;

多认识朋友,可以发邮件给我, 不管是技术交流亦或想我写点什么~

khan.chan@enissue.com

Posted in 社区新闻 | No Comments »

Postfix with DomainKey(dkfilter)

December 20th, 2008 by khan.chan

对于一些中小型站点使用的EDM MTA, 群发用户的通知或密码更新等邮件非常适用,当然超过一定量也只能出钱买绿色通道了.

请谨慎使用!

 

1. Domainkey, SPF等反垃圾邮件认证.

2. 对发送邮件做些发送规则的限制,如发往yahoo.com的邮件的频率/.

Version 1.2

Author: Khan Chan <Khan.Chan [at] ityum.com>

Last updated: 2008/11/2; 2008/12/17


I1. Domainkey

D

omainKey是一种对所发出的邮件添加数字签名的技术,收件人可以通过它校验邮件的发布来源。这一技术有助于拦截钓鱼式(phishing垃圾邮件攻击

 

需要了解更多,请访问http://tw.promo.yahoo.com/antispam/domainkeys.html

http://domainkeys.sourceforge.net/

I2.安装配置domainkey

环境: CentOS 5.2 | ip: 202.96.209.177

首先配置dns记录:

mail.ityum.com 202.96.209.177

2.1安装必须的Perl Modules.



perl -MCPAN -e’CPAN::Shell->install(”Crypt::OpenSSL::RSA”)’
perl -MCPAN -e’CPAN::Shell->install(”Mail::Address”)’
perl -MCPAN -e’CPAN::Shell->install(”MIME::Base64″)’
perl -MCPAN -e’CPAN::Shell->install(”Net::DNS”)’
perl -MCPAN -e’CPAN::Shell->install(”Test::More”)’
perl -MCPAN -e’CPAN::Shell->install(”Text::Wrap”)’
perl -MCPAN -e’CPAN::Shell->install(”Email::Address”)’
perl -MCPAN -e’CPAN::Shell->install(”Mail::DomainKeys”)’


2.2 安装dkfilter

下载dkfilter:

http://jason.long.name/dkfilter/dkfilter-0.11.tar.gz

tar xvzf dkfilter-0.11.tar.gz

cd dkfilter-0.11

./configure –prefix=/usr/local/dkfilter

make install

 

验证下安装是否正确了,再将sample-dkfilter-init-script.sh拷贝到init.d

cp sample-dkfilter-init-script.sh /etc/init.d/dkfilter

 

2.3 postfix filter

将如下代码加入到/etc/postfix/master.cf

#
# Before-filter SMTP server. Receive mail from the network and
# pass it to the content filter on localhost port 10025.
#
smtp      inet  n       -       n       -       -       smtpd
    -o smtpd_proxy_filter=127.0.0.1:10025
    -o smtpd_client_connection_count_limit=10
#
# After-filter SMTP server. Receive mail from the content filter on
# localhost port 10026.
#
127.0.0.1:10026 inet n  -       n       -        -      smtpd
    -o smtpd_authorized_xforward_hosts=127.0.0.0/8
    -o smtpd_client_restrictions=
    -o smtpd_helo_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o smtpd_data_restrictions=
    -o mynetworks=127.0.0.0/8
    -o receive_override_options=no_unknown_recipient_checks

2.4 生成KEY文件

cd /usr/local/dkfilter

openssl genrsa -out private.key 1024
openssl rsa -in private.key -pubout -out public.key

接下去将public.key内容设置到DNS服务器上.

我这边设置如下:

dk._domainkey IN TXT “k=rsa; t=y; p=XXXXXXXXXXXXXXXX”

这里XXXX部分就是public.key内容.

在这里我修改selectordk,所以你在邮件服务上也需要修改

vi /etc/init.d/dkfilter

–selector=selector1 修改为 –selector=dk

请使用命令dig –t txt dk._domainkey.ityum.com来验证

将如下内容加入/etc/postfix/master.cf

# modify the default submission service to specify a content filter
# and restrict it to local clients and SASL authenticated clients only
#
submission  inet  n     -       n       -       -       smtpd
    -o smtpd_etrn_restrictions=reject
    -o smtpd_sasl_auth_enable=yes
    -o content_filter=dksign:[127.0.0.1]:10027
    -o receive_override_options=no_address_mappings
    -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject

#
# specify the location of the DomainKeys signing filter
#
dksign    unix  -       -       n       -       10      smtp
    -o smtp_send_xforward_command=yes
    -o smtp_discard_ehlo_keywords=8bitmime

#
# service for accepting messages FROM the DomainKeys signing filter
#
127.0.0.1:10028 inet  n  -      n       -       10      smtpd
    -o content_filter=
    -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks
    -o smtpd_helo_restrictions=
    -o smtpd_client_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o mynetworks=127.0.0.0/8
    -o smtpd_authorized_xforward_hosts=127.0.0.0/8

执行命令postfix reload

2.5 测试结果

发送邮件到Gmail. 查看信件是否躺在收件箱里, 查看下邮件标头:

DomainKey-Status: good (test mode)
DomainKey-Signature: a=rsa-sha1; h=Received:Subject:Message-Id:Date:From:To; b=GIvVNdHT2s7DiihL+1Qdm+uHR
2.6 使用Google企业邮箱的设置.
除了Domainkey外,SPF的设置也必不可少,  linuxfocus有篇介绍的文章《SPF简介》
openspf.org设置指导 :http://old.openspf.org/wizard.html?mydomain=&x=21&y=6

 

如何设置 SPF 记录?

通过发件人策略框架 (SPF) 记录,域名所有者可以指定有权代表其域名发送电子邮件的主机,以使发件人:地址难以伪造。 我们强烈建议您为自己的域名发布 SPF 记录,因为采用这些记录后,最终可以帮助防止垃圾邮件的出现。 要设置您所在域的 SPF 记录,请针对 DNS 资源发布以下 TXT 记录: v=spf1 include:aspmx.googlemail.com ~all

发布缺少 include:aspmx.googlemail.com 的 SPF 记录或指定 -all 而不是 ~all 可能会造成收发问题。



参考资料:

http://www.linuxfocus.org/ChineseGB/December2004/article354.shtml

http://www.openspf.org/

http://jason.long.name/dkfilter/

[YAHOO! Anti-Spam|http://antispam.yahoo.com/domainkeys]

[Sendmail + Domain Key 實作|http://phorum.study-area.org/viewtopic.php?t=40579]

[DomainKeys filter for Postfix|http://jason.long.name/dkfilter/]

[DomainKeys filter for Postfix|http://tenyi.spaces.live.com/blog/cns!D13E5C982A38E777!137.entry]

Posted in Mail | No Comments »

Apache+Apusic集成配置负载均衡

March 10th, 2008 by khan.chan

原文地址: http://www.blogjava.net/zhuyuanxiang/archive/2008/03/02/183331.html

[关键词: Apusic, Apache, mod_proxy ]

1. 为何要做负载均衡?
一般是对性能和价格比的需要,为了能够共享应用服务器的计算能力,用户会采购多个CPU的金蝶阿帕斯应用服务器(Kingdee Apusic Application Server,KAAS)中间件产品,并且安装到多台计算机中,然后将多个应用统一部署在这些KAAS之上。
还有就是保护KAAS安全,现在网络攻击太严重了,因此用户想把服务器放在防火墙后面,前端放一个Apache服务器或者硬件的负载均衡器,哪怕前端被攻击,至少可以保证主页内容不会被篡改。
2. 如何用Apache+Apusic实现LB?
实 现前我们需要了解一个概念,LB到底由哪些部分组成,相互之间的关系如何?配置负载均衡需要两个部分组成:负载均衡器和应用服务器集群。负载均衡器不了解 具体业务,只知道有哪些KAAS,然后依据一种分配机制将客户请求分配给相应的KAAS。而KAAS接到用户请求,处理完成后就将结果返回给负载均衡器, 再返回到客户端。但是HTTP请求都是无状态的,如果用户使用Session机制保存了状态,那么下次再访问时可以通过两种方法处理:
⑴ 利用负载均衡器的会话保持(Session Stick)技术,负载均衡器会记录SessionID和服务器IP,然后将下次请求仍然转发到上次处理的服务器上,但是这种方式如果服务器宕机了,就会使客户丢失信息;
⑵ 利用KAAS的集群功能,将一起工作的服务器配置成集群,相互之间利用会话复制(Session Copy)技术,所有服务器上的会话内容完全相同,请求不需要转发给相同的服务器就可以得到正确响应,但是这种方式会给网络带来较大的负载压力,甚至引起网络风暴。
下面分别是这两种方法的配置过程:
⑴ 配置均衡均衡的会话保持
#httpd.conf(Apache的配置文档,如果想了解Apache相关配置参数,请看参考文献)
#载入重要的模块
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule proxy_connect_module modules/mod_proxy_connect.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
#设置负载均衡分配用的服务器
<Proxy balancer://apusicBalance>
BalancerMember http://1.2.3.4:6888/
BalancerMember http://1.2.3.5:6888/
</Proxy>
#设置代理服务器转发方式,stickysession就是会话保持,JSESSIONID就是Apusic的会话ID,nofailover就是说服务器没有做集群
ProxyPass / balancer://apusicBalance stickysession=JSESSIONID nofailover=On
ProxyPassReverse / balancer://apusicBalance

⑵ 利用KAAS的集群功能,只提供了WEB模块Session复制的例子,其它例子请参考Apusic集群管理文档,在apusic.conf增加的配置如下
<!– 将应用服务器增加到集群中 –>
<SERVICE
CLASS=”com.apusic.cluster.ClusterService”
>
<ATTRIBUTE NAME=”ClusterName” VALUE=”ApusicCluster”/>
<ATTRIBUTE NAME=”LoadWeight” VALUE=”100″/>
</SERVICE>
<!– 使WEB容器的Session支持复制 –>
<SERVICE CLASS=”com.apusic.servlet.http.session.SessionService”>
<ATTRIBUTE NAME=”DefaultSessionTimeout” VALUE=”3600″/>
<ATTRIBUTE NAME=”MaxSessionsInCache” VALUE=”1024″/>
<ATTRIBUTE NAME=”SessionInvalidateCheckInterval” VALUE=”60″/>
<ATTRIBUTE NAME=”SessionSwapCheckInterval” VALUE=”30″/>
<ATTRIBUTE NAME=”Distributable” VALUE=”True”/>
<ATTRIBUTE NAME=”Replicable” VALUE=”True”/>
</SERVICE>

3. 使用Apache实现LB还有哪些功能?
⑴ 支持缓冲。mod_cache模块。
Sample httpd.conf
#
# Cache配置的例子
# 下面的Cache默认采用disk作为缓存,如果想调整成mem,必须先将disk用#注释,然后删除mem的#注释。
LoadModule cache_module modules/mod_cache.so
LoadModule disk_cache_module modules/mod_disk_cache.so
#LoadModule mem_cache_module modules/mod_mem_cache.so
<IfModule mod_cache.c>
<IfModule mod_disk_cache.c>
CacheRoot c:/cacheroot
CacheEnable disk /
CacheDirLevels 5
CacheDirLength 3
</IfModule>
<IfModule mod_mem_cache.c>
CacheEnable mem /
MCacheSize 4096
MCacheMaxObjectCount 100
MCacheMinObjectSize 1
MCacheMaxObjectSize 2048
</IfModule>
# 当Apache作为Proxy时,不缓存安全部分的代码。下面的例子实际使用时请改成应用自身的情况
CacheDisable http://security.update.server/update-list/
</IfModule>
⑵ 不同的分配机制。
除了已经知道的循环分配机制,将请求依次派发给每个应用服务器;还可以通过loadfactor来进行权重分配,事先依据每个应用服务器的计算能力,将请求依据权重分发给服务器;还可以通过lbmethod流量的压力进行统计,然后将请求分发给服务器。

⑶ 分别在不同的网络层进行代理,一般可以在四层和七层。
这种方法我没有进行实验,待以后补充。

4. 在应用开发阶段要注意哪些事情来满足LB需要?
开 发阶段需要注意的内容其实很多,主要就是Session复制和资源死锁的问题。以前项目开发,能够把功能实现就算是完工了,但是用户的需求也在增加,特别 是J2EE为分布式计算提供了很好的模型,为开发符合LB标准的程序提供了有利的条件,但是在开发阶段仍然有些工作必须要做,例如:
⑴ 所有保存在Sesion中的类必须要实现Serialable接口,当然还要把Object实现的方法重载,保证你的类被正确序列化了,但是如果没有实现,至少保证系统进行Session复制时不会报错;
⑵ 尽量不要进行长事务操作,尽量不要对数据库进行表级锁,尽量把申请得到的资源进行归还,尽量使用finally保证资源被正确释放,等等…
⑶ 以后继续补充

参考文献:
1. mod_proxy - Apache 2.2 中文版参考手册,
http://doc.chinahtml.com/Manual/ApacheManual/mod/mod_proxy.html
2. mod_cache - Apache 2.2 中文版参考手册
http://doc.chinahtml.com/Manual/ApacheManual/mod/mod_cache.html
3. Apusic集群管理文档
http://infocenter.apusic.com/help/index.jsp?topic=/com.apusic.studio.doc.server/output/eclipse/cluster_management.html

Posted in 集群应用 | No Comments »

缓存no-cache数据-Squid

March 4th, 2008 by khan.chan

原文地址: http://michaelboman.org/wiki/index.php?title=Squid

转载地址: http://enissue.com/archive/183/caching-sharepoint-with-squid.html

使用SquidHTTP加速器, 后端数据加Http header;定义过期或max-age使用起来是非常顺手的.

而对于如Microsoft SharePoint项目,Yupoo API等定义为no-cache的 如何操作呢?

Cache-Control: no-cache, must-revalidate
Pragma: no-cache

michaelboman.org上使用Squid来缓存SharePoint.

http_port 80 accel defaultsite=michaelboman.org
cache_peer 192.168.1.16 parent 80 0 no-query login=PASS originserver connection-auth=on
visible_hostname michaelboman.org
 
client_persistent_connections on
server_persistent_connections on
 
acl all src 0.0.0.0/0.0.0.0
acl static_content urlpath_regex -i .(jpg|gif|png|css|js|axd)
 
http_access allow all
 
cache_effective_user squid
cache_effective_group squid
 
# 不显现Squid 版本
httpd_suppress_version_string on
 
# Remove the Caching Control header on content deemed static
header_access Cache-Control deny static_content
 
# Turn off ICP as we only have 1 proxy
icp_port 0
 
# See http://www.squid-cache.org/Versions/v2/2.6/cfgman/refresh_pattern.html
#http://home.arcor.de/jeffpang/squid/chap07.html#a7
#refresh_pattern <regexp> <min> <percent> <max> <options>
 
# Overriding caching settings if nocache=true option is set
refresh_pattern nocache=true 0 0% 0
 
# Overriding caching settings if edit=yes option is set
refresh_pattern edit=yes 0 0% 0
 
# Cache profile images for 1 minute to 5 hours, but obey the cache settings
#如下时间值是错误的,refresh_pattern min,max是分钟单位
refresh_pattern MyProfile/Profile.Images 60 30% 18000
 
# TESTING: Caching everything like crazy
#refresh_pattern ^http: 600000 100% 700000 override-expire override-lastmod reload-into-ims ignore-reload ignore-no-cache ignore-private ignore-auth
 
# Cache images for 4-5 hours, regardless what the server says
refresh_pattern .jpg 14400 50% 18000 override-expire override-lastmod reload-into-ims ignore-reload ignore-no-cache ignore-private ignore-auth
refresh_pattern .gif 14400 50% 18000 override-expire override-lastmod reload-into-ims ignore-reload ignore-no-cache ignore-private ignore-auth
refresh_pattern .png 14400 50% 18000 override-expire override-lastmod reload-into-ims ignore-reload ignore-no-cache ignore-private ignore-auth
 
# Cache CSS and JavaScript for 4-5 days as well
refresh_pattern .css 14400 50% 18000 override-expire override-lastmod reload-into-ims ignore-reload ignore-no-cache ignore-private ignore-auth
refresh_pattern .js  14400 50% 18000 override-expire override-lastmod reload-into-ims ignore-reload ignore-no-cache ignore-private ignore-auth
refresh_pattern .axd 14400 50% 18000 override-expire override-lastmod reload-into-ims ignore-reload ignore-no-cache ignore-private ignore-auth
 
# Cache all ASPX for 1-2 minutes  (forced)
#refresh_pattern .aspx 60   50% 120   override-expire override-lastmod ignore-no-cache

Posted in 反向代理 | No Comments »

WebSite fairage.com Performance Analysis

January 26th, 2008 by khan.chan

泛世纪网站分析报告

Khan.Chan@enissue.com
http://enissue.com

[关键词: ETag, Expires, 网站性能优化]

Index
!
本文档应 泛世纪站点(www.fairage.com) 秋水之托,对其站点做技术分析,可做技术交流和探讨之用.
本文档遵循CC 2.5 署名-非商业性使用
您可以自由:
复制、发行、展览、表演、放映、广播或通过信息网络传播本作品
惟须遵守下列条件:
署名. 您必须按照作者或者许可人指定的方式对作品进行署名。
非商业性使用. 您不得将本作品用于商业目的。
禁止演绎. 您不得修改、转换或者以本作品为基础进行创作。

对任何再使用或者发行,您都必须向他人清楚地展示本作品使用的许可协议条款。
如果得到著作权人的许可,您可以不受任何这些条件的限制。
• Nothing in this license impairs or restricts the author’s moral rights.
本文档使用黑盒测试方式,即不询问站点方网站架构和应用程序环境信息,不告之测试方式(局限于不伤害站点服务范围),有所差异性属于正常手段之结果.我们 将从Network,Frontend Performance两个角度来分析站点状况,对于具体问题的解决方案在本文档中有所阐述. 从很多案例和资深网站性能分析者研究看来,网站页面响应请求花费的时间20%是使用在HTML上,而更多的花费却是图片,脚本和CSS等对象.
我们发现站点www.fairage.com从页面元素查看,本身站点很适合做反向代理加速的优化操作, 各个元素适量的在客户端浏览器做缓存的优化,那将使站点的响应时间缩短,提高用户
体验; 且对于之后发展的高并发流量都有很大帮助.
!
网络测试环境: 上海移动数据中心线路,1U DELL 1750 RHEL4
从我们的访问使用看来,泛世纪站点使用的网络路由从上海跨越至广东的基本能保持在30ms内, 具体数据如下:
24.786 ms 24.423 ms 32.291 ms 32.514 ms 24.936 ms
设备阻挡了ICMP和渗透扫描,不过建议在操作系统上做更多的版本信息隐藏,如隐蔽Tomcat 5.5.12信息, 在允许情况下,建议将主站内容与BBS和图片数据分割存储,在请求回复时也可在单机状态下设置多hostname的方式来响应更多用户请求.
我们使用Flock浏览器来访问站点,且清空所有cookies和历史记录来模拟第一次访问.
网站首页有12JavaScripts文件, index.js,download.css, category1a.css,allmenu.js,swapimg.js,searchpage.js, okteacher.js, topright.js,xmlhttp.js等都没有做压缩或者合并处理. Download.css14.3K大小,而很多小字节CSS文件建议合成几个大的CSS文件来减少请求数, 遵循HTTP 1.0/1.1KeepAlive和流水线技术,可以适当调节KeepAlive数值.
在网络状态基本良好的情况下,访问站点的速度并非很优良,首页下载使用了49.77s, 当然这里GoogleAnalyticsstar.uusee.com也拖了后腿,可以妥善解决下这个问题.
第二个比较大的问题是每个元素都没有设置过期值!
newyear_logo.jpg为例,这个文件的信息如下:
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
ETag: W/”58991-1175063420000″
Last-Modified: Wed, 28 Mar 2007 06:30:20 GMT
Content-Type: image/jpeg
Content-Length: 58991
Date: Fri, 11 Jan 2008 06:47:57 GMT
Connection: keep-alive
Length: 58,991 (58K) [image/jpeg]
在两次请求后查看记录,发现ETag值是一样的,我猜想网站方应该没有做ETag同步,那确定站点方服务器数只有一台,如果以后流量增大,需要多服务器的群组时, 建议关闭ETag.

highperformancewebsite_img_3.jpg
如上是我查找到的部分未做expires的对象, 再以highperformancewebsite_img_4.jpg做分析,图片最后修改时间是
Tue, 06 Mar 2007 05:47:32 GMT,
直至我请求的Fri Jan 11 15:31:31 CST 2008,查看其他的图片和CSS对象,所有的时间都算是久远, 所以在第一次访问时总共有73HTTP Requests,而在之后的这次访问中是72HTTP Requests.也就是说只缓存了一个对象元素, 没有做缓存而导致HTTP请求数没减少, 这无疑是对性能的很大伤害.

highperformancewebsite_img_5.jpg
这里给出个Yahoo!站点某一元素的信息:
Yahoo
非常的漂亮的让这文件缓存在客户端的浏览器中,定义了过期时间值, 这对用户体验和你的站点包括网络带宽消耗服务器压力都是有很大帮助的.

highperformancewebsite_img_6.jpg

PDF格式下载: highperformancewebsite.pdf
http://enissue.com
admin@enissue.com
khan.chan@enissue.com

网站架构华人社区其宗旨是为系统架构师,系统管理进阶者服务,增进系统架构与技术交流,我们期望大家在这一社区获得最大程度的技术成长与积累。

Posted in 案例分析 | No Comments »

优化你的PHP之43条律言

January 21st, 2008 by khan.chan

khan.chan 采集|enissue.com

原文地址:http://reinholdweber.com/?p=3
英文版权归Reinhold Weber所有,中译文作者yangyang(aka davidkoree)。双语版可用于非商业传播,但须注明英文版作者、版权信息,以及中译文作者。翻译水平有限,请广大PHPer指正。

1. If a method can be static, declare it static. Speed improvement is by a factor of 4. 如果一个方法可静态化,就对它做静态声明。速率可提升至4倍。

2. echo is faster than print. echo 比 print 快。

3. Use echo’s multiple parameters instead of string concatenation. 使用echo的多重参数(译注:指用逗号而不是句点)代替字符串连接。

4. Set the maxvalue for your for-loops before and not in the loop. 在执行for循环之前确定最大循环数,不要每循环一次都计算最大值。

5. Unset your variables to free memory, especially large arrays. 注销那些不用的变量尤其是大数组,以便释放内存。

6. Avoid magic like __get, __set, __autoload 尽量避免使用__get,__set,__autoload。

7. require_once() is expensive require_once()代价昂贵。

8. Use full paths in includes and requires, less time spent on resolving the OS paths. 在包含文件时使用完整路径,解析操作系统路径所需的时间会更少。

9. If you need to find out the time when the script started executing, $_SERVER[’REQUEST_TIME’] is preferred to time() 如果你想知道脚本开始执行(译注:即服务器端收到客户端请求)的时刻,使用$_SERVER[‘REQUEST_TIME’]要好于time()。

10. See if you can use strncasecmp, strpbrk and stripos instead of regex. 检查是否能用strncasecmp,strpbrk,stripos函数代替正则表达式完成相同功能。

11. str_replace is faster than preg_replace, but strtr is faster than str_replace by a factor of 4. str_replace函数比preg_replace函数快,但strtr函数的效率是str_replace函数的四倍。

12. If the function, such as string replacement function, accepts both arrays and single characters as arguments, and if your argument list is not too long, consider writing a few redundant replacement statements, passing one character at a time, instead of one line of code that accepts arrays as search and replace arguments. 如果一个字符串替换函数,可接受数组或字符作为参数,并且参数长度不太长,那么可以考虑额外写一段替换代码,使得每次传递参数是一个字符,而不是只写一行 代码接受数组作为查询和替换的参数。

13. It’s better to use select statements than multi if, else if, statements. 使用选择分支语句(译注:即switch case)好于使用多个if,else if语句。

14. Error suppression with @ is very slow. 用@屏蔽错误消息的做法非常低效。

15. Turn on apache’s mod_deflate 打开apache的mod_deflate模块。

16. Close your database connections when you’re done with them. 数据库连接当使用完毕时应关掉。

17. $row[’id’] is 7 times faster than $row[id]. $row[‘id’]的效率是$row[id]的7倍。

18. Error messages are expensive. 错误消息代价昂贵。

19. Do not use functions inside of for loop, such as for ($x=0; $x < count($array); $x) The count() function gets called each time. 尽量不要在for循环中使用函数,比如for ($x=0; $x < count($array); $x)每循环一次都会调用count()函数。

20. Incrementing a local variable in a method is the fastest. Nearly the same as calling a local variable in a function. 在方法中递增局部变量,速度是最快的。几乎与在函数中调用局部变量的速度相当。

21. Incrementing a global variable is 2 times slow than a local var. 递增一个全局变量要比递增一个局部变量慢2倍。

22. Incrementing an object property (eg. $this->prop++) is 3 times slower than a local variable. 递增一个对象属性(如:$this->prop++)要比递增一个局部变量慢3倍。

23. Incrementing an undefined local variable is 9-10 times slower than a pre-initialized one. 递增一个未预定义的局部变量要比递增一个预定义的局部变量慢9至10倍。

24. Just declaring a global variable without using it in a function also slows things down (by about the same amount as incrementing a local var). PHP probably does a check to see if the global exists. 在一个函数里如果只声明一个全局变量而没在函数中调用它,同样会减慢速度(其程度相当于递增一个局部变量)。PHP很有可能会检查看是否存在全局变量。

25. Method invocation appears to be independent of the number of methods defined in the class because I added 10 more methods to the test class (before and after the test method) with no change in performance. 方法调用看来与类中定义的方法的数量无关,因为我(在测试方法之前和之后都)添加了10个方法,但性能上没有变化。

26. Methods in derived classes run faster than ones defined in the base class. 派生类中的方法运行起来要快于在基类中定义的同样的方法。

27. A function call with one parameter and an empty function body takes about the same time as doing 7-8 $localvar++ operations. A similar method call is of course about 15 $localvar++ operations. 调用带有一个参数的空函数,其花费的时间相当于执行7至8次的局部变量递增操作。类似的方法调用所花费的时间接近于15次的局部变量递增操作。

28. Surrounding your string by ‘ instead of ” will make things interpret a little faster since php looks for variables inside “…” but not inside ‘…’. Of course you can only do this when you don’t need to have variables in the string. 用单引号代替双引号来包含字符串,这样做会更快一些。因为PHP会在双引号包围的字符串中搜寻变量,单引号则不会。当然,只有当你不需要在字符串中包含变 量时才可以这么做。

29. When echoing strings it’s faster to separate them by comma instead of dot. Note: This only works with echo, which is a function that can take several strings as arguments. 输出多个字符串时,用逗号代替句点来分隔字符串,速度更快。注意:只有echo能这么做,它是一种可以把多个字符串当作参数的“函数”(译注:PHP手册 中说echo是语言结构,不是真正的函数,故把函数加上了双引号)。

30. A PHP script will be served at least 2-10 times slower than a static HTML page by Apache. Try to use more static HTML pages and fewer scripts. Apache解析一个PHP脚本的时间要比解析一个静态HTML页面慢2至10倍。尽量多用静态HTML页面,少用脚本。

31. Your PHP scripts are recompiled every time unless the scripts are cached. Install a PHP caching product to typically increase performance by 25-100% by removing compile times. 除非脚本可以缓存,否则每次调用时都会重新编译一次。引入一套PHP缓存机制通常可以提升25%至100%的性能,以免除编译开销。

32. Cache as much as possible. Use memcached - memcached is a high-performance memory object caching system intended to speed up dynamic web applications by alleviating database load. OP code caches are useful so that your script does not have to be compiled on every request. 尽量做缓存,可使用memcached。memcached是一款高性能的内存对象缓存系统,可用来加速动态Web应用程序,减轻数据库负载。对运算码 (OP code)的缓存很有用,使得脚本不必为每个请求做重新编译。

33. When working with strings and you need to check that the string is either of a certain length you’d understandably would want to use the strlen() function. This function is pretty quick since it’s operation does not perform any calculation but merely return the already known length of a string available in the zval structure (internal C struct used to store variables in PHP). However because strlen() is a function it is still somewhat slow because the function call requires several operations such as lowercase & hashtable lookup followed by the execution of said function. In some instance you can improve the speed of your code by using an isset() trick. 当操作字符串并需要检验其长度是否满足某种要求时,你想当然地会使用strlen()函数。此函数执行起来相当快,因为它不做任何计算,只返回在zval 结构(C的内置数据结构,用于存储PHP变量)中存储的已知字符串长度。但是,由于strlen()是函数,多多少少会有些慢,因为函数调用会经过诸多步 骤,如字母小写化(译注:指函数名小写化,PHP不区分函数名大小写)、哈希查找,会跟随被调用的函数一起执行。在某些情况下,你可以使用isset() 技巧加速执行你的代码。

Ex.(举例如下)
if (strlen($foo) < 5) { echo “Foo is too short”; }
vs.(与下面的技巧做比较)
if (!isset($foo{5})) { echo “Foo is too short”; }

Calling isset() happens to be faster then strlen() because unlike strlen(), isset() is a language construct and not a function meaning that it’s execution does not require function lookups and lowercase. This means you have virtually no overhead on top of the actual code that determines the string’s length. 调用isset()恰巧比strlen()快,因为与后者不同的是,isset()作为一种语言结构,意味着它的执行不需要函数查找和字母小写化。也就是 说,实际上在检验字符串长度的顶层代码中你没有花太多开销。

34. When incrementing or decrementing the value of the variable $i++ happens to be a tad slower then ++$i. This is something PHP specific and does not apply to other languages, so don’t go modifying your C or Java code thinking it’ll suddenly become faster, it won’t. ++$i happens to be faster in PHP because instead of 4 opcodes used for $i++ you only need 3. Post incrementation actually causes in the creation of a temporary var that is then incremented. While pre-incrementation increases the original value directly. This is one of the optimization that opcode optimized like Zend’s PHP optimizer. It is still a good idea to keep in mind since not all opcode optimizers perform this optimization and there are plenty of ISPs and servers running without an opcode optimizer. 当执行变量$i的递增或递减时,$i++会比++$i慢一些。这种差异是PHP特有的,并不适用于其他语言,所以请不要修改你的C或Java代码并指望它 们能立即变快,没用的。++$i更快是因为它只需要3条指令(opcodes),$i++则需要4条指令。后置递增实际上会产生一个临时变量,这个临时变 量随后被递增。而前置递增直接在原值上递增。这是最优化处理的一种,正如Zend的PHP优化器所作的那样。牢记这个优化处理不失为一个好主意,因为并不 是所有的指令优化器都会做同样的优化处理,并且存在大量没有装配指令优化器的互联网服务提供商(ISPs)和服务器。

35. Not everything has to be OOP, often it is too much overhead, each method and object call consumes a lot of memory. 并不是事必面向对象(OOP),面向对象往往开销很大,每个方法和对象调用都会消耗很多内存。

36. Do not implement every data structure as a class, arrays are useful, too. 并非要用类实现所有的数据结构,数组也很有用。

37. Don’t split methods too much, think, which code you will really re-use. 不要把方法细分得过多,仔细想想你真正打算重用的是哪些代码?

38. You can always split the code of a method later, when needed. 当你需要时,你总能把代码分解成方法。

39. Make use of the countless predefined functions. 尽量采用大量的PHP内置函数。

40. If you have very time consuming functions in your code, consider writing them as C extensions. 如果在代码中存在大量耗时的函数,你可以考虑用C扩展的方式实现它们。

41. Profile your code. A profiler shows you, which parts of your code consumes how many time. The Xdebug debugger already contains a profiler. Profiling shows you the bottlenecks in overview. 评估检验(profile)你的代码。检验器会告诉你,代码的哪些部分消耗了多少时间。Xdebug调试器包含了检验程序,评估检验总体上可以显示出代码 的瓶颈。

42. mod_gzip which is available as an Apache module compresses your data on the fly and can reduce the data to transfer up to 80%. mod_gzip可作为Apache模块,用来即时压缩你的数据,并可让数据传输量降低80%。

43. Excellent Article (http://phplens.com/lens/php-book/optimizing-debugging-php.php)about optimizing php by John Lim 另一篇优化PHP的精彩文章,由John Lim撰写。

Posted in WebPerformance | 5 Comments »

GlusterFS 分析报告

January 15th, 2008 by khan.chan

关键词: GlusterFS, 分布式文件系统

康国庆

引言
GlusterFS 是一个高层次的分布式文件系统解决方案。通过增加一个逻辑层,对上层使用者掩盖了下面的实现,使用者不用了解也不需知道,文件的存储形式、分布。内部实现是整合了许多存储块(server)通过Infiniband RDMA 或者Tcp/Ip 方式互联的一
个并行的网络文件系统,这样的许多存储块可以通过许多廉价的x86 主机,通过网络搭建起来。其相对于传统NAS 、SAN、Raid 的优点就是:
1.容量可以按比例的扩展,且性能却不会因此而降低。
2.廉价且使用简单,完全抽象在已有的文件系统之上。

3.扩展和容错设计的比较合理,复杂度较低。扩展使用translator 方式,扩展调度使用scheduling 接口,容错交给了本地的文件系统来处理。
4.适应性强,部署方便,对环境依赖低,使用,调试和维护便利。
支持主流的linux 系统发行版,包括fc,ubuntu,debian,suse 等,并已有若干成功应用。
整体逻辑结构分析

GlusterFS,整体来看分客户和服务端两部分,当然这是相对的。客户端是对于提供数据中心整体来说的,它对外提供文件服务,目录服务,两个文件系统最重要的两个服务。(注1:文件复制和共享的问题不知GlusterFS 是怎么考虑的。客户机拥有一个卷管理器,和子卷的调度程序,在客户机中有的子卷映像和服务器主机
是相对的,1 对1。相当于一个卷集包含了若干逻辑卷,逻辑卷的物理位置是在服务器主机上的,该实现与NFS 是有很大区别的。服务器主机,上面拥有与客户机相应的通信接口,接口之间使用GlusterFs protocol 来通信,服务器主机还应有自己的文件系统来提供文件服务和目录服务,GlusterFS 是构建在
其上的。当然客户和服务主机都有相应的配置文件,物理连接是通过InfiniBand、Myrinet 或者Gbit 以太网连接。下图为个人理解图:
glusterfs_3.jpg
组件构成分析

GlusterFS,含有以下组件:
1.客户与服务器组件。这部分是复杂双方传输一个总的接口,服务器组件负责把自己的子卷发布出来,客户组件负责挂载GlusterFS 到内核的VFS 上。
2.翻译器模式,一种来自 的设计机制,( hurd 是借鉴IBM vms 系统设计的, 内核只有最简单的功能,其上层设计了模拟器可以模拟很多操作系统)该设计可以扩展

glusterfs_6.jpgGlusterFS 的功能,包括调试器,性能
调优的工具,加密验证等都是使用的该模式。xlators 文件夹下的都是翻译器的实现。
3.传输模块,protocol translators 文件夹是其实现。
4.调度模块,Scheduler 文件夹下面有4 种调度器实现,其作用是对子卷做负载均衡。4 种调度器实现了unify 翻译器。分别为A)Adaptive Least Usage (ALU) 利用它的一种评价方式,把一些要素如磁盘利用率、文件访问频率(读、写分开)、响应速度等综合起来考虑,

做出的一种自适应的调度方式。其实现是4 种调度中最复杂的。
B)Non-Uniform Filesystem Scheduler 这个有点象NUMA ,一种结合SMP 和Mpp 以及cluster 优点的内存管理方式,它的一个特点就是在创建文件时优先在本地创建文
件。C)Random scheduler 随机调度器,使用随机数发生器,选择子卷。D)Round-Robin (RR) scheduler 螺旋线调度算法,它会将数据包均匀的分发给各台
服务器它把所有的服务器放在相等的地位上,而不会实际的去考虑各台服务器的差异, 如负载,响应等等,如有4 台服务器,调度序列可能就是ABCDABCDABCDABCD。。。4 种基本模块构成了GlusterFS,另外还提供了一些扩展。上图是源码解开后的结构。
源代码组成分析

定量分析:37 个头文件,55 个c 源码文件,共有代码59460 行( 包括源码中的空行。文件最多的是libglusterfs 文件夹,包含了36 个文件。
3.1 启动过程

glusterfs_ctx_t (重要)GlusterFS 的环境类,里面包含日志文件,日志级别,定时器,poll 类型等等,使用dict 实现。服务端和客户端可以使用守护进程方式(deamon 精灵),也可以作为应用程序来启动,
下面剖析了两个部分的main 函数。 部分( )初始化环境ctx,初始化调用池?初始化链表,解析命令行参
数。//main 部分

int32_t
main (int32_t argc, char *argv[])
{
FILE *fp;
int32_t pidfd;
glusterfs_ctx_t ctx = {
.logfile = DATADIR “/log/glusterfs/glusterfsd.log”,
.loglevel = GF_LOG_ERROR,
.poll_type = SYS_POLL_TYPE_MAX,
};
call_pool_t *pool;

pool = ctx.pool = calloc (1, sizeof (*pool));
LOCK_INIT (&pool->lock);
INIT_LIST_HEAD (&pool->all_frames);

argp_parse (&argp, argc, argv, 0, 0, &ctx);

设定进程pid,设定日志级别,文件路径。

pidfd = pidfile_lock (pidfile);

if (gf_log_init (ctx.logfile) < 0){
return 1;
}
gf_log_set_loglevel (ctx.loglevel);

接下来设定系统资源限制,一般默认是1024 ,比如打开文件数,此处设定为65535. 软限制和硬限制设为相等。
{
struct rlimit lim;
lim.rlim_cur = RLIM_INFINITY;
lim.rlim_max = RLIM_INFINITY;

if (setrlimit (RLIMIT_CORE, &lim) < 0) {
gf_log (”glusterfsd”,
GF_LOG_ERROR,
“main: failed to set RLIMIT_CORE, error string is %s”,
strerror (errno));
}

lim.rlim_cur = 65535; //RLIM_INFINITY;
lim.rlim_max = 65535; //RLIM_INFINITY;

if (setrlimit (RLIMIT_NOFILE, &lim) < 0) {
gf_log (”glusterfsd”,
GF_LOG_ERROR,
“main: failed to set RLIMIT_NOFILE, error string is %s”,
strerror (errno));
}
}

接下来读取卷配置文件specfile 串,程序设定的是CONFDIR “/glusterfs-server.vol”, 成功了如何如何….. 失败了如何如何……

if (specfile) {
fp = fopen (specfile, “r”);
if (!fp) {
gf_log (”glusterfsd”,
GF_LOG_ERROR,
“FATAL: could not open specfile: ‘%s’”,
specfile);
exit (1);
}
} else {
gf_log (”glusterfsd”,
GF_LOG_DEBUG,
“main: specfile not provided as command line arg”);
argp_help (&argp, stderr, ARGP_HELP_USAGE, argv[0]);
exit (0);
}

然后是判断环境ctx 的foreground 有值与否,没有值,就清空命令行的参数值,然后把argv[0]填充成[glusterfsd] ,生成守候进程,更新前面锁定的pidfile 。

if (!ctx.foreground) {
int i;
for (i=0;i<argc;i++)
memset (argv[i], ‘ ‘, strlen (argv[i]));
sprintf (argv[0], “[glusterfsd]”);
daemon (0, 0);
pidfile_update (pidfd);
}

初始化定时器, 通过specfile 串对应文件构造出树来, 关闭该文件, 忽略管道信号
SIGPIPE 设置软中断处理函数为glusterfsd_cleanup_and_exit 这个清理函数。

gf_timer_registry_init (&ctx);

xlator_tree_node = get_xlator_graph (&ctx, fp);
if (!xlator_tree_node) {
gf_log (”glusterfsd”,
GF_LOG_ERROR,
“FATAL: could not create node graph”);
exit (1);
}
fclose (fp);

/* Ignore SIGPIPE */
signal (SIGPIPE, SIG_IGN);

#if HAVE_BACKTRACE
/* Handle SIGABORT and SIGSEGV */
signal (SIGSEGV, gf_print_trace);
signal (SIGABRT, gf_print_trace);
#endif /* HAVE_BACKTRACE */

signal (SIGTERM, glusterfsd_cleanup_and_exit);最后进入循环, 判断函数是transport.c 中的poll_iteration 函数。循环完毕关掉pidfile 。

while (!poll_iteration (&ctx));

close (pidfd);

观察poll_iteration(&ctx )的实现,发现默认如果不设置ctx 的异步进程通信模式的话,默认是使用epoll 的,看代码://transport.c int32_t

int32_t
poll_iteration (glusterfs_ctx_t *ctx)
{
int32_t ret = 0;

#ifdef HAVE_SYS_EPOLL_H
switch (ctx->poll_type)
{
case SYS_POLL_TYPE_EPOLL:
ret = sys_epoll_iteration (ctx);
break;

case SYS_POLL_TYPE_POLL:
ret = sys_poll_iteration (ctx);
break;

default:
ctx->poll_type = SYS_POLL_TYPE_EPOLL;
ret = sys_epoll_iteration (ctx);
if (ret == -1 && errno == ENOSYS) {
ctx->poll_type = SYS_POLL_TYPE_POLL;
ret = sys_poll_iteration (ctx);
}
break;
}
#else
ret = sys_poll_iteration (ctx);
#endif

return ret;
}
g
关于poll 和epoll 相关函数的实现都在poll.c 和epoll.c 里面。poll 、epoll 、select 是网络编程和进程间通讯的三种模式。select 在BSD Unix 中引入, 而poll 是System V 的解决方案. epoll 调用添加在2.5.45, 作为使查询函数扩展到几千个文件描述符的方法。具体他们的区别可以查man 手册页。
Client 客户端启动过程:(glusterfs-fuse/glusterfs.c) 初始化glusterfs_ctx 环境中的日志文件位置,日志级别和poll 类型,这个和server 端一样,另外声明了xlator 的图(树型),配置文件的指针,传输类型指针,还有就是系统资源限制,和调用池。

};

同样需要设定系统资源限制的值,此处还可以设定debug 模式来使用mtrace()

#endif

#endif

初始化环境的调用池,解析参数等等这些和server 是一样的。

此处与server 不同,但无非是一些判断和检测, 如测试ctx 的日志文件设置没, 设置glusertfs 的全局日志级别为环境的日志级别。

ctx.logfile);

}

针对解析的参数设定mount_point ,此处为判断设定与否。下面是设定端口号,和配置文件的地方等等,都是做检测用的。

}

file=VOLUME-SPEC-FILE\n”);

}

{

specified\n”);

}

}

下面是通过配置文件来设定ctx,接下来就是一些中断屏蔽,中断处理函数的处理等等, 这个和server 是一样的。

}

接下来是当一切成员都初始化完毕时,此刻开始进行glusterfs 的挂载, 下面和server 一样生成守护进程。然后注册定时器, 生成xlator 树并赋值给环境ctx 中的graph 成员, 初始化FUSE 的图,进入循环。后面的过程和server 类似,少许不同。所有的初始化过程都和ctx 环境有关,从携带变量,赋值等等,都是操作的ctx 。

}

}

}

}

3.2 相关代码分析
. . 传输协议代码

传输协议代码中体现了3 种可以使用的方式:
· ib-verbs使用Infiniband verbs 层为RDMA(Remote Direct Memory Access) 通信. 这是最快的接口(1-4 ms). · ib-sdp: 使用Infiniband SDP (sockets direct protocol) 为RDMA 通信(70-90ms). · tcp:使用普通TCP/IP 或IPoIB 内部连接. 假如有4 节点的集群每台主机的NIC 带宽1G byte/s ,那么组合起来的带宽也有4G byte/s。
下面是tcp 部分的分析:
共4 个文件tcp.h 是接口定义文件,里面定义了几个tcp 操作的函数,接收,关闭连接等等,还定义了等待队列的结构体wait_queue 和一个tcp 状态的结构体tcp_private 。Tcp.c 是头文件里面定义的函数的实现。
Tcp-client.c: 里面有关于建立连接的方法,tcp 客户端确认提交的方法,初始化传输和结束等方法。Tcp-server.c 里面也有相应的方法。
3 种方法都是按照transport.h 定义的接口来实现的。这里用到了面向对象的思想和状态模式,transport.c 里面的相关方法对其多种“ 子类做了”dispatch 。例如:
//transport.c
int32_t

{

}

该模块通过transport_op 这个结构体实现了“ 多态”,该结构体在transport.h 有接口定义(都是函数指针), 而在每个连接方式的实现里面都声明一个实例,并按自己的方式初始化。
//transport.h

struct peer_info_t {

//连接属性结构体含有套接字信息
struct sockaddr_in sockaddr;

};

struct transport { //传输interface
struct transport_ops *ops; //操作功能结构体指针,相当于类函数

void *xl_private; //
pthread_mutex_t lock; //线程锁
int32_t refcount; //引用计数
xlator_t *xl;
void *dnscache; //dns 缓冲?作用不太明了
data_t *buf; //数据缓冲

dict_t *options,

event_notify_fn_t notify);

struct peer_info_t peerinfo; //包含了前面定义的套接字信息

event_notify_fn_t notify; //消息
};

int32_t (*flush) (transport_t *this);

int32_t (*recieve) (transport_t *this, char *buf, int32_t len);

int32_t (*submit) (transport_t *this, char *buf, int32_t len);

int32_t (*writev) (transport_t *this,

};

在真正的实现里面会初始化,传输类的成员函数的,如Tcp-server.c
//Tcp-server.c

多态

};

. . 调度器剖析
调度器是给unify 的接口用的,在集群文件系统中,分数据块时绑定一个相应的调度器, 来进行数据的分布式存取。有四种调度器,前面有提到了。调度操作接口比较简单,只要实现下列的接口函数的类就是一个调度器。
//scheduler.h

struct sched_ops { // 包括初始化,清理费料,更新,调度器逻辑,通知。int32_t (*init) (xlator_t *this); void (*fini) (xlator_t *this); void (*update) (xlator_t *this); xlator_t *(*schedule) (xlator_t *this, int32_t size); void (*notify) (xlator_t *xl, int32_t event, void *data);
};

该文件还定义了一个获得调度器的方法get_scheduler,具体实现在scheduler.c 里面,被
unify.c 中调用(后面提unify.c).
RR 调度器是最简单的,和下面要写的xlator 关系很密切。结构体如下:
//rr.h struct rr_sched_struct { //rr 调度需要的一些属性如刷新间隔,剩余磁盘空间,可用性等等。。
xlator_t *xl;
struct timeval last_stat_fetch;
int64_t free_disk;
int32_t refresh_interval;
unsigned char eligible;

};

struct rr_struct {

//rr 结构体
struct rr_sched_struct *array; //包含rr 调度结构体的一个数组
struct timeval last_stat_fetch;

int32_t refresh_interval;

//刷新时间间隔
int64_t min_free_disk;

//最小的剩余空间
pthread_mutex_t rr_mutex; //线程锁
int32_t child_count;

//节点计数
int32_t sched_index;

//调度索引};
与接口的联系方式和transport 模块类似,实现了sched_ops 定义的操作。
//rr.c
struct sched_ops sched = {

.init

= rr_init,
.fini

= rr_fini,
.update

= rr_update,
.schedule = rr_schedule,
.notify

= rr_notify
};

. . 与
Xlator 是一个有前驱和后继及父指针的节点类,其组成了xlator_list 链表,另外系统使用它组成树结构来使用,schedulor 在初始化时是会遍历xlator_list 的每个节点的。Xlator 里面还包括了相关xlator 的操作符xlator_fops、xlator_mops,和构造函数与析构函数,以及一些必要的数据,节点表指针,消息,glusterfs 环境,配置选项字典(option)等等。
//xlator.h

struct _xlator {
char *name;
char *type;
xlator_t *next, *prev;
xlator_t *parent;
xlator_list_t *children;

struct xlator_fops *fops;
struct xlator_mops *mops;

void (*fini) (xlator_t *this);
int32_t (*init) (xlator_t *this);
event_notify_fn_t notify;

dict_t *options;
glusterfs_ctx_t *ctx;
inode_table_t *itable;
char ready;
void *private;

};

typedef struct xlator_list {
xlator_t *xlator;
struct xlator_list *next;

} xlator_list_t;

Xlator_list 是一个关于xlator 的前向链表。
. . 与
Translators 思路来自GNU/hurd 微内核的虚拟文件系统设计部分,一个translator 是一个用于目标服务器与hurd 分布式文件系统之间的普通程序。作用是对外部的文件系统做操作时,转换成目标文件系统适当的调用。Translators 不需要特别的权限运行。在传统的unix 内核设计上,文件系统或虚拟文件系统都是在内核里面实现的,内核有绝对的机器访问权,在hurd 上,使用的微内核设计,所以translator 使用的协议相关操作
不需要特别权限来执行,在用户空间上执行。
Translator 可以附着在节点上,每个tanslator 都是针对相应的功能设计的,比如rot-13 是加密用的,Trace 是追踪,调试用的,performance 文件夹下的4 个translator 是用来性能调优用的。
和translator 有直接关系的是这么几个文件,xlator.c 、xlator.h 、default.h、,default.c 四个文件,后两个比较容易解释,就是其他translator 在定义fop 和mop 时如果自己没有设定实现的话,便会使用默认实现,其实现就在这两个文件里面。也就是说这两个文件里面保含了所有的操作的定义。前两个文件在上节已经说过了。
下图是glusterfs 使用的相关translator 所处的位置。相对于客户端来说,服务端的任务真的是很简单的,大部分translator 是工作在客户端的,比如,性能调优,调度器,合并器等等,而相反,服务端只需运行起来glusterfsd 就可以了。要设计一个translator 也比较容易,除了需要一个初始化函数

int32_t init (xlator_t *this)

和收尾的函数
void

fini (xlator_t *this)

还需要对xlator.h 中的xlator_fops 、xlator_mops 两个结构体中,需要翻译的命令自己定义
//xlator_fops的定义,里面包括要求翻译的调用表,fops表示文件操作struct xlator_fops {

…………

};

结构体中每个属性都是个指向函数的指针,例:

xlator_mops 的定义,里面包括要求翻译的调用表,mops 表示管理操作

};

前面是需翻译的命令,翻译后的命令例子如下,以rot-13 为例:
//rot-13.c 下面的赋值表示要翻译的两个调用, 管理操作以默认不做更改//(default.h,default.c)

};

同样你把自己需要翻译过来的调用自己实现例如下:

这样扩展一个translator 就完成了。translator 操作是异步的,这样可以减少网络上调用的延时造成性能下降。它使用STACK_WIND 和’STACK_UNWIND 维护一个用户空间的调用栈。在桩文件call-stub.h 文件中, 里面有call_stub_t 结构体的定义,结构体里面含有一个联合,另外头文件还有相关调用的桩,call-stub.c 里面是头文件接口的实现。
// call-stub.h

象?)。

// call-stub.c 头文件中示例的实现

{

}

. .
Glusterfs 使用的协议是比较简单的,协议的定义可以在其官方网站有简短的描述。(来自代码注释protocol.h)
All value in bytes. ‘\n’ is field seperator.
Field:<field_length>

==================
“Block Start\n”:12
callid:16
Type:8
Op:8
Name:32
BlockSize:32
Block:<BlockSize>
“Block End\n”:10
==================

起始头12 个字节,调用id 16 个字节,操作类型8 个字节,操作指令8 个字节,操作名32 个字节,数据块大小32 个字节,然后是数据块,然后是包尾10 个字节。操作类型有四种:分别是请求和回应、对应当fop(文件操作),mop(管理操作)

GF_OP_TYPE_FOP_REQUEST,
GF_OP_TYPE_MOP_REQUEST,
GF_OP_TYPE_FOP_REPLY,
GF_OP_TYPE_MOP_REPLY

操作指令定义在glusterfs.h 里面两个枚举类型,一个fop ,一个mop 。服务端执行的是响应请求, 所以收到的包中操作类型皆是request 类型的fop 或者mop ,然后将之交给一个解释器函数,函数负责分析是fop 还是mop ,然后转换成local 系统的执行序列。这其中还包括一些传输的错误处理,参数不正确等等。该解释器通过调用相应的本地函数,处理完后返回的也是一个完整的协议数据包。该函数在server-protocol.c 文件中,声明如下: static int32_t server_protocol_interpret (transport_t *trans,
gf_block_t *blk) 此外服务端还需要维护一个响应队列。客户端就比较繁琐了, 它需要负责连接,保持连接,握手等动作,另外它还和服务端一样,也有一个翻译器,负责解释收到的协议包的处理。由于客户端对其上层应用来说还得提供文件服务, 所以它所需要提供的xlator_fops 对象的成员函数比服务端提供的更多一些。

该图是服务端与客户端建立连接之后,客户端请求一个命令,或传输一部分数据所需的过程示意图,最中间的部分是在网络中传输的协议包。顶部是客户端请求的指令,该指令一般是给更高层提供服务的。最下层是服务端处理请求的命令或数据给local 调用处理。
3.3 相关的实用工具类

Util 类:dict 、stack 、list ,自己实现的容器和必要的操作。Stack 用的是list 实现的。hashFn : 实现的一个最快速hash 算法, uint32_t SuperFastHash (const char * data, int32_t len) 这个方法,来自网上一个实现。Lock :加锁,解锁的类。Logging :日志类,到文件的。rot-13 :简单的一个加密类,没有错误检查的。Timer :定时器,记时器
3.4 性能,优化的部分代码

整体上,增加性能的优化方式就是利用缓冲, 加之考虑其业务需求, 比如频繁读写小文件, 或是大量操作是读文件而很少写等等。利用其业务特点, 适当的使用优化方式,源代码里面提供了4 种优化器,都是利用了translator 模式实现的。
Readhead:预读技术, 这个在操作系统中内存技术,外存技术和catch 技术中用到比较多了。大概的意思就是使用临近数据被访问的可能性较大的原理, 做的预读的优化。
Writebehind:后写技术,就是当需要回写硬盘时,先做一个缓冲区,然后等缓冲区满了,一次性写回硬盘,这样减少了使用系统调用,网络等开销。
Io-cache:这个是利用多个服务端中多余内存来做缓冲用的,网站上有个性能测试,64 个服务端主机,每主机有8GB mem ,使用io-catche 后,每个主机使用了6GB mem 作为io 缓冲,共有64×6GB =384GB 的数据缓冲区,这样可以大大减少外存的访问,提高了数据访问速度。
Io-threads:io 线程化,AIO 添加了异步读写功能,使用这个translator ,可以利用系统的idle 进程堵塞时间来处理新到来的请求。当进入内核调用时系统会锁住资源,如cpu mem 等等,这样就不能利用其做其他工作了,该translator 可以更好的改进此模型,是之更有效率。看其road map 是1.3 版本新加进来的特性。
在cluster 文件夹下面有两个与性能有关的三个translator 。分片技术strip:这个大意就是象RAID0 那样,可以加速保存和读取,但风险加大,所以在Stripe.c 文件的注释中,提示最好和下面的translator 一起使用, 以保证安全。重复技术afr:这个就是象RAID1 那样,保存时写数据做双份。而且可以对相应的类型的文件做不同的设置。Unify:组合了多个存储块到一个巨大的存储空间里面, 前面介绍translator 时有写unify 在整个系统中所处的位置。当然对单个主机中文件系统的优化也是需要的,比如对ext3,reiserfs 的参数优化。
全局看整个系统

此处写的是个人体验,不一定都正确,供参考。Glusterfs 是一个存储空间和访问效率都可以线性增加的一个分布式文件系统,网上资料除了gluster.org 以外,几乎没有什么有关的介绍了。通过对源码的审阅,个人感觉,比较主要的是把程序的整体结构理清,扩展方式弄明白在向下看具体的实现是比较好的。该系统扩充的方式是使用了translator 的模式,具体我还参考了《现代操作系统》中分布式文件系统章节和GNU/HURD 中解释translator 的部分,后者主要是在gnu 的网站上。
数据结构上讲,整个文件系统中节点构成了一棵树,而且每个节点的操作是通过某个translator 来工作的,一个节点可以附着很多的translator。所有的translator 都要实现xlator 结构体和相关的xlator_fops、xlator_mops 两个“成员函数的结构体”, 从xlator“继承”下来的操作如果不自己定义, 那么就会使用默认的设置,这个在default.c 里面定义。当然自己定义的操作并赋值,这个过程有些象子类覆盖父类的操作,平行来看也就是多态。当然这是从面向对象角度来看的,该系统很多地方都使用了面向对象的思想来设计的,这个和linux 2.6 以后的内核模块设计是异曲同工的。
那么一般可以这样识别一个用c 实现的Class 关键字的类:例(对源文件有些修改):
struct A { char *name; char *type; //成员
struct A *this;
struct xlator_fops *fops;
struct xlator_mops *mops;
void (*fini) (struct A *this);

//this 指针//成员操作结构体1 //成员操作结构体2 // 析构函数,垃圾清理
int32_t (*init) (struct A *this);// 构造函数,初始化
event_notify_fn_t notify; // 成员。。

dict_t *options;
glusterfs_ctx_t *ctx;
inode_table_t *itable;
char ready;
void *private;

};

1. 一个struct 定义里面包含一个指针该指针的类型是该struct 定义的类型。
2. 上面的struct 内部成员中含有其他结构体的指针,象xlator_fops 就是这里提到的其他结构体的指针, 该结构体里面全部都是指向函数的指针, 也就是成员函数了。当然此处也可以把xlator_fops 里面的成员都释放到struct A 里面,但是这样这个struct 就显得有些臃肿了,,毕竟成员函数还是不少的。上面这个例子还有两个只有类才具备的析构函数,和构造函数。glusterfs_ctx 控制了全局的信息, 很多地方传输都是使用它来传递的, 一个典型的环境类。初始化些东西也是针对它来做的。
Redhat GFS 和Glusterfs 的目的类似,都是以全局在一个命名空间下而通过访问其他节点获取数据的。此处没有性能比较。Lustre 也是一个开源基于GNU lisence 的集群文件系统,网站资源比较丰富,开发者的资源也比较多,中文资料也不少,sun 公司收购了clusterfs 公司,拥有了此技术。下面地址显示的是lustre 与glusterfs 做相当命令所需时间的比较:
http://www.gluster.org/docs/index.php/GlusterFS_1.3.pre2-
VERGACION_vs_Lustre-1.4.9.1_Various_Benchmarks

下面的地址是NFS 与glusterfs 性能的测试对比:
http://www.gluster.org/docs/index.php/GlusterFS_1.2.1-
BENKI_Aggregated_I/O_vs_NFSv4_Benchmark

附言

下载的源码为1.3.0 版,使用cygwin configure 失败,对FUSE 配置也失败。Window 下再没尝试。
另外时间较短, 自己c 、api 方面经验不足, 有些地方还不是很确定, 比如: translator 到底能不能到处附着在节点上。后来看到性能测试时, client 和server 还可以多对多…这个也是以前没想到的。很多前面推测出的结论,后两天又由于深入的学习而推倒。这几天我收获比较大,人的认识过程就是这样,肯定到否定再到肯定。
2007-11-25 西安

glusterfs_analysis_report.pdf下载

 

 

Posted in 集群应用 | No Comments »

如何提高网站的访问速度 - 从30秒到3秒的飞跃

January 3rd, 2008 by khan.chan

原文地址: http://blogger.org.cn/blog/more.asp?name=lhwork&id=23281

原文作者: lhwork

2006年10月份,我开始对web开发产生了兴趣,并决定自己也尝试开发一个网站。在此之前,我做过3年的java application的开发,对web开发应该算一无所知。在比较了java,php,ror,和python后,我选择了基于python的web框 架 - django 。到目前为止,我还认为这是一个明智的决定。Django高效的开发效率让我仅仅用一个月的业余时间,就基本完成了网站的开发。这是一个网络书签的网站, 我加上了一些有意思的特性,让网站显得有些与众不同。

我购买了域名和Dreamhost 的主机空间。Dreamhost支持django,并且第一年的费用只有180元人民币。2006年11月份,http: //www.hpbookmarks.com 上线了。网友们发来了善意的评论,“很有创意”,“点意思”,“一些feature很不错”。同时,还有一个非常一致的意见就是,“访问速度太慢了”。其 实,当时的情况不只是访问速度慢,而且是相当不稳定。很多时候是几个小时网站无法访问。当时,我并没有在意,因为我有两个自以为“合理”的解释。第一,我 用的是国外最便宜的虚拟主机,国内访问慢是很正常的。第二,django的还处于0.95的状态,效率和稳定性方面有问题也正常。

但是, 我慢慢发现上面的解释不过是自己骗自己的借口。很多用dreamhost的网站,访问起来也很快。而且django也已经被成功应用在许多大型的网站。我 开始认真考虑提高网站速度的问题了。毕竟,速度慢的网站很可能在第一次就失去的用户,他们可以永远不会再来了。终于,我进行了下面一步一步的优化工作,并 且取得了一个看起来还不错的结果。

第一步,用Ajax提高用户体验

由于我的网站上链接字体的大小是根据点击次数决定,所 以每次点击都要提交到服务器端并记录次数,再在客户端打开网站链接。这在localhost测试的时候没有发现问题,但是部署到服务器上,会感到明显的等 待。解决办法就是用Ajax。用户点击网站链接后就直接打开,再通过Ajax将点击的事件提交到服务器端记录。这样用户感觉不到任何延时。

第二步,将逻辑移到客户端的javascript中

在 开始的时候,“网站标签高亮”和“手气不错”的功能都是提交到服务器端操作,然后返回结果的。后来,我发现其实很多逻辑是可以移到客户端,由 javascript来实现的。Javascript非常强大,可以完成很多复杂的逻辑。将逻辑移到客户端的javascript中,可以很有效的减少和 服务器通讯的次数,获得更好的访问速度。

第三步,解决进程的<defunct>

由于采用的是fastCGI 的方式,我配置了django.fcgi。可是,我发现系统进程中,有大量的django.fcgi进程被标记为< defunct>(失去功能)。这些<defunct>进程会导致服务器有时无法正常访问。我开始尝试用命令来kill掉这些进程,但 是很快发现这无法从根本上解决问题。后来,我看到一个老外在blog上提到一个解决方案,将django.fcgi改名为dispatch.fcgi。原 来,dispatch.fcgi是一个dreamhost的系统进程,它的健壮性是可以得到保障的。果然,我将django.fcgi改名为 dispatch.fcgi后,<defunct>的现象再没有出现。

第四步,优化SQL语句

SQL语句的 执行通常也是一个很花费时间的操作。经过检查,我发现我的一条SQL语句,是一个嵌套三层的子表查询。而这条SQL还必须是一个Raw SQL,即不能采用django的OR Maping。这意味着不能被cache缓存,每次都是真刀真枪的执行。更失败的是,经过我的分析,这条SQL完全可以不执行。这是一次设计上的失误,标
准的over design(过渡设计)。当时,我是想通过数据库得到一个最精确的统计值。后来发现,这个值完全可以用一个近似的常量代替。优化SQL,尤其是避免不必要的SQL执行,带来的效果是非常明显的。

第五步,尽量减少页面大小

随 着添加网站越来越多,有一天我发现django生成的首页已经达到了80k。我很清楚这是一个非常不能被接受的数字。我开始检查页面,很快发现了线索。第 一,因为偷懒,页面中很多layout是用空格( )实现的。第二,因为为了增加代码可读行,调试方便,每行生成的页面都增加换行符 (\n)。第三,最糟糕的是,大量的用了
inline css。就是将css style直接嵌入标记块中。于是,我立即动手,用css的align解决layout,去掉\n,将inline css抽象到独立的css文件中。这样下来,在不更改任何内容的情况下,80k变成了57k。

第六步,用gzip进行页面压缩

当 我兴高采烈的把页面优化结果贴到了smth bbs上,却被直接泼了盆凉水。原来百分之二十几的优化结果,实在太一般了。ylsdd给我了一个很重要的线索,deflate。原来apache的 deflate模块可以把文件进行gzip压缩,压缩后的文件传到浏览器后再被解压。主流的浏览器都支持这种gzip的解压操作。于是,我在apache 的配置文
件中加入了Add OutputFilter DEFAULT html css js的语句。经过测试,css,js这些文本文件的压缩后都只有原来尺寸的25%。这里,和大家分享一个网站http: //www.port80software.com/products/httpzip/compresscheck 它的作用是检测你的网站是否被压缩,以及压缩比率等。

第七步,回归静态页面

新的问题又来了。原来deflate只支持静 态文件的压缩。而我的首页是django动态生成的,deflate模块没有进行压缩。我突然想到,网站的首页为什么不能是静态页面呢?于是,我增加了一 个runtime的api,这个api提供的是和原来一样由django动态生成的页面。我又写了一个 python的程序,通过urllib2模块下载这个动态生成的页面,并保存为index.html。我将网站的root映射到index.html这个 静态页面。最后,通过linux crontab定义一个行为,每五分钟执行一下这个python程序,生成新的index.html。值得一提的是,由于网络原因,python程序不一 定每次都能准确完整的下载动态生成的页面。所以我们必须再进行一个校验算法。当页面大小要超过一定数字,页面中出现某个校验字符串的情况下,才保存 index.html。这样,每次用户提交的访问,不是由服务器端动态生成页面,极大的节省了服务器端的开销。而静态页面又可以有效的被deflate压 缩。最后结果,首页被压缩为13k,为原来的22%。唯一的区别就是,新提交和推荐的网站不能立即出现在首页。但是我认为,这应该是可以被接受的。

至此,网站的优化工作基本完成。网站的访问速度从原来30秒以上,缩短到3秒左右,应该说算是一个飞跃。虽然,3秒的速度也不是非常快,但是,考虑到虚拟主机等客观原因,这个结果我还是满意的。原来感觉我的网站很慢的朋友们,也可以再试试。

以 上的优化方案出自我的个人经验,并不一定适合所有网站。但是,它告诉我们一个事实。影响网站访问速度的不仅仅是服务器配置,网络带宽。也许,你糟糕的设 计,低效率的方案也是致命的因素。应当注意的是,优化工作也不能匆匆上手。一定要仔细研究,具体情况具体分析,得到统计数据,找到真正的问题所在,再开始 优化。相信自己,提高网站的访问速度并不是不可能。毕竟,Nothing is Impossible。祝大家成功。

Posted in 案例分析 | 2 Comments »

Apache 2 mod_deflate 基准

December 26th, 2007 by khan.chan

导言

Apache 1.3.x版本中我们可以使用mod_gzip来压缩对象,Apache2的版本中使用mod_deflate来代替mod_gzip, 允许WebServer对输出内容进行压缩而节省传输到客户端的带宽消耗.

HTTP 压缩

HTTP 1.1协议中一个新的HTTP编码特征为Accept-encoding. 参考资料: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html

这个新特征允许浏览器让WebServer知晓是否支持压缩数据的传导.

取舍

压缩数据对象无疑会消耗CPU,所以带宽与服务器的CPU使用都是需要取舍考虑的,如果带宽足够的话,也许你就不会再继续看本文了 J

正题

对于你的Apache httpd.conf文件添加如下代码:

AddOutputFilterByType DEFLATE text/html text/plain text/xml
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html
Header append Vary User-Agent env=!dont-vary

  • AddOutputFilterByType …, 这行指定了text/html,text/plaintext/xml能被压缩,也防止mod_deflate压缩图片,文档和多媒体文件.
  • BrowserMatch …, 对于一些不能正确处理压缩内容的浏览器做排除匹配.
  • Header append …, 确保代理不会错误处理压缩内容.

接下来我们定制下Log格式来记录压缩行为.

DeflateFilterNote deflate_ratio
LogFormat “%v %h %l %u %t \”%r\” %>s %b mod_deflate: %{deflate_ratio}n pct.” vhost_with_deflate_info
CustomLog logs/deflate_access_log vhost_with_deflate_info

  • DeflateFilterNote …, DeflateFilterNote指令将指定压缩率标记在日志中.这里标记为deflate_ratio.
  • LogFormat …,日志格式.
  • CustomLog …, 日志记录位置和压缩统计信息记录.

我们可以试下效果并查看下日志,如下:

HTTP/1.1″ 200 8125 mod_deflate: 34 pct.
HTTP/1.1″ 200 5665 mod_deflate: 35 pct.
HTTP/1.1″ 200 24477 mod_deflate: - pct.

在第一条里可以看到8125Bytes的数据传输到客户端,(100-34)=66%的流量被节省.

 

原文地址: http://enissue.com/archive/162

作者: khan.chan {at} enissue.com

参考文章:http://httpd.apache.org/docs/2.0/mod/mod_deflate.html

 

Posted in Benchmark | No Comments »

大型高负载网站架构 的感想

December 19th, 2007 by khan.chan

昨日认识了位健谈的IT人,原在verycd作系统管理的叶宁(这哥们现飘泊去见首都人民了).

以前sweater给我介绍的他写的《大型高负载网站架构和应用初探》, 他总结了很多信息和数据,感谢叶宁的辛劳成果. 这里我也想就此PPT写些自己的感想.

老牌的互联网公司,如Yahoo! ,网易等因成长和壮大的时间曲线较长,所以你可以看到多数采取的方式是以频道或项目来分割开, 避免站点臃肿到不可控制. 基本的架构方式为:

前层的反向代理加速—Web Server—Cache Server—DB

而负载均衡的方式也使用了如DNS轮循或部分硬件负载均衡设备来承担大流量.

叶宁在ppt第一部分提到了性能更好的 Web服务器, 世面上Web Server“品种繁多”,挑选适合自己的尤其重要. 一般的Web Server多是在User Layer, 但在RedHat 9.0时我也使用了TUX(kernel Layer层面上Web Server). 页高速缓冲存储器通过DMA方式将置换数据直接传导网络,实现了零拷贝的操作. 对小字节数据块静态操作,有着非常优良的体验; but, 如果崩溃的话,呵呵 这后果也是….

注: DMA的方式因为减少了CPU的参与, 而且DMA池应用于Cache System也是可行的,RedHat为TUX也在往能使用减少User Layer参与的情况下, 尽量让其在User Layer 守护, 我想这个思路是可以应用与除了web server以外的应用上.

除了一些商业的Web Server性能不错的外, 有些做Web 加速和代理的软件我们也放在一起介绍,

Lighttpd, Nginx,HAproxy.

这三个应该是现在应用最多的前层软件, Lighttpd使用比较著名的例子如Flickr, tudou.com

Nginx 业内站点如sina, 51.com 等都是应用它的.

ppt第二部分提到的是缓存系统. 我在这里也说下自己的意见:

Linux在kernel 2.5后就将/dev/shm与tmpfs使用了同个struct 结构. 在以前很多文章中我都看到要用shm来做cache存储的地方,但问题是没有仔细考虑过机器的内存大小,使用内存情况,且也没有能限制shm被做为 cache使用的大小限制! 我更推荐使用memcached和对PHP优化上.

最后提下负载均衡环境, 在提供Web应用的站点中使用的方式是和¥挂钩的, 如LVS, HAProxy, 加上Linux-HA项目, 完全能撑起千万的流量.

另一种就是使用硬件设备, 如F5 BigIP, NetSclaer设备.

当然Web 2.0的公司提倡DIY的精神也未尝不可,一台1U的IA32硬件设备, 板子需要是为fw,switch设计的, 使用FreeBSD操作系统, 主要是FS和slab的改写会是技术活 .

后继 ing

Posted in 案例分析 | No Comments »

在UNIX下改善运行Apache与PHP的速度

December 13th, 2007 by khan.chan

作者: johnlim

译者: khan.chan khan.chan {at} enissue.com

原文地址: http://phplens.com/phpeverywhere/tuning-apache-php

Last modified: 24 Mar 2006.

作者描写了自己总结的优化ApacheLinux下跑PHP