Subscribe to RSS Subscribe to Comments

Steve’s Blog

BIND 配置DNS时要注意的几个问题。

现在很多服务器都是用的域名注册商提供的域名解析服务,但还是会有人自己建一个域名服务器,我就是其中之一。因为我是在万网注册的,但是万网只给我10个解析,这肯定不够。

自己建域名服务器,简单说一下吧,先在万网花10元建一个域名服务器,等完成之后(几天时间)把自己的域名的DNS服务器改成自己新建的。以前万网可以允许主和辅DNS服务器相同,但现在不行了,那就简单的把主DNS服务器设成自己的,万网的设成辅的,就可以了。

 在自己建域名服务器的时候,要注意几个东西,一个就是BIND在默认情况下是允许递归查询的,这个是什么意思?简单的说,就是别人可以向你这台服务器查询别的域名,这样的话,DNS服务器就有很大的风险,比如很容易被DDOS等等。 那么简单的加一条recursion no;到你的options里面,就可以关掉了。这样,你的DNS服务器就只会对你自己的域名查询做出回应。

 第二就是在做域名解析的时候,很多人都只解析xxx.example.com这样的域名,但是都没有做example.com这个名字的解析。最好也做一个这样的解析,同时把www做为example.com这个名字的CNAME,这样的结果就是在浏览器里输入example.com和http://www.example.com/是一样的。不过,如果要让apache能这么做的话,vhost里面http://www.example.com/就要设为ServerName,然后再加一个ServerAlias 就象这样

  ServerAdmin email@email.com
  DocumentRoot /var/www/localhost/host
  ServerName www.example.com
  ServerAlias example.com
  CustomLog logs/www-access_log common

第三,在做MX的时候,大家都知道要做PTR,可是这个可以明确的告诉你,做了也没用。PTR不是由你这个DNS管的,就算你做了PTR,别人一样查不到你的MX的PTR。因为PTR这个反向查询的话,是要在APNIC做的。但是象新浪这样的网站,他们好象不允许没有PTR的mail server发邮件给他们,这个时候你只能和他们联系,让他们把你的服器IP地址加到他们的白名单里面。

 最后,在LINUX下面肯定都有做amavis+spam的处理,那么在spamassassin里面有一个SPF,这个也可以加在你自己的BIND里面。如何设置这个SPF呢?去这里http://www.openspf.org/ 会有一个wizard,给你把你域名下面的mailserver的SPF设置好。这样,你的域名就算真正完成了。

基于tc/connmark的流量控制 - 续

上一篇里面我讲了很多TC的应用, 但是iptables的基本上没有说太多,我后来仔细看了看,那个TCPMSS的rule,如果加在mangle表里面,是没用的。还是要放在filter里面的FORWARD。

另外,对于如何区分LAN还是internet的数据,应该用filter里面的FORWARD着手,因为所有的数据包,一来到iptables,都是直接先进入filter表中,而对于NAT的网关来说,如果要抓到LAN的数据,就不能简单的在INPUT里面作文章。所以要区分LAN的数据和Internet的数据,可以在FORWARD里面用CONNLIMIT的target,直接对internet的数据加一个mark,然后再到mangle表中match connlimit的mark,再处理。

但是之前我说又必须要在FORWARD里面加上那个TCPMSS的target,这两个都是JUMP,只可能有一个rule被处理到。那怎么办? 再看看我前一篇文章里面写到,TCPMSS只是把MSS值减40,那我们再看一下,ethx的MTU是1500,ppp0的MTU是1492,所以不可能是对ppp0上来的数据包减40(ppp0来的包肯定是下载),那么这个TCPMSS应该是对应于eth0上来的发送的数据包(上传,请求等),那么我们就只要在TCPMSS上加上一个-i eth0,在CONNLIMIT里加上-i ppp0,这样,就能做到既为下载的数据包mark,又能保证最大程度的连接性。所以,处理完之后,filter表只有如下的几个rule:

# Generated by iptables-save v1.3.8 on Wed Nov 21 13:33:42 2007
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A FORWARD -i eth0 -p tcp -m tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu
-A FORWARD -i ppp0 -j CONNMARK --set-mark 1000 #对所有下载包全设置connmark = 1000
COMMIT
# Completed on Wed Nov 21 13:33:42 2007

对于nat表来说,MASQUERADE肯定是要加的,其它就是自己定义了。

对于mangle表,我们最好能新建一个chain,让所有被connmark mark过的数据包全都转到这个新的chain中来处理,这样,也就是对于LAN的文件服务的数据完全不处理(还记得HTB的结构是层级的么,如果没有匹配的class,那么这个数据包会被放在HTB的root里处理,当然就是没有限速了)。 mangle的规则定义也就相对比较简单了:

# Generated by iptables-save v1.3.8 on Wed Nov 21 13:33:42 2007
*mangle
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:WANTC - [0:0]
-A POSTROUTING -m connmark --mark 1000 -j WANTC # connmark为1000的,是下载数据,转到新建的WANTC中去
-A WANTC -j MARK --set-mark 0x3
-A WANTC -d 192.168.1.1 -j MARK --set-mark 0x5
COMMIT
# Completed on Wed Nov 21 13:33:42 2007

上面的WANTC就是我新建的chain,先给所有的包一个class,然后可以再加一些其它的规则。 现在,就能比较完美的实现用tc和iptables的流量控制了。

 为什么我没有讲connlimit? 因为我发现在mangle表中,connlimit的表现好象不是我所期望的。另外,不知道connlimit match的ip地址到底是以什么样的规则运行的,是不是以-s/d的方式指定。所以我没有讲connlimit

Postfix最简单的anti-spam

是人都知道,我们做一个邮件服务器,肯定要做anti-spam的,那么,看了网上那么多anti-spam的教程,howto,都要用到spamassassin+amavisd-new,可是很多里面都没有或者很少提到postfix也可以做简单的anti-spam,而且它的这套,能做的更好。

ChinaVFX有自己的邮件服务器,我公司的域名也是在这边做的邮件服务器。现在的spamer都已经很强了,只要你在随便什么地方一公布邮箱,马上就会有狂多的垃圾邮件了。 我的两个邮箱,平均每天收到最少30封垃圾邮件。加起来,每天有近100封垃圾邮件发给我。。

每个邮件都有一个header(是人都知!!),在这里面,记录了这个邮件经过哪几个relay服务器,从什么client地址发出的,如果我们仔细看看,就能发现其中的问题。

现在每个邮件服务商都有自己的规则,所以通过用这些Free的,大众化的邮件服务商的邮箱来发送SPAM,已经很少了;但是自己做个邮件服务器却很简单,可以说,都不用专门的邮件服务器软件就可以疯狂群发邮件。 那么对于这种情况,可以想象,他们不可能做一个有效的域名,再去做MX,再去做一堆邮件服务器所需要的域名工作。因为他们负担不起,一旦这么搞法,这个域名最多能用几天,然后就会被全世界列入blocklist,所以他们都是用的动态IP地址。

那么动态IP地址的问题在哪?名字就说明问题了,第一,不可能有PTR记录,也就是说不能反向查找域名,第二,很有可能用动态IP地址也没有一个有效的域名,当然也不可能有一个有效的主机名。如果各位自己也有做过邮件服务器的话,可以看看自己邮箱里的SPAM,我的邮箱里的SPAM,有90%都是这种没有主机名,没有PTR的邮件服务器发过来的。

那怎么办呢?看看我的postfix的restriction

# enable some restrictions
smtpd_helo_required = yes
smtpd_delay_reject = yes
smtpd_reject_unlisted_sender = yes
smtpd_reject_unlisted_recipient = yes
smtpd_etrn_restrictions = permit_mynetworks, reject
smtpd_recipient_restrictions =
	permit_sasl_authenticated,
	permit_mynetworks,
	reject_unauth_destination,
	reject_unlisted_recipient,
	reject_unauth_pipelining,
	reject_non_fqdn_recipient,
	reject_unknown_recipient_domain,
	permit
smtpd_sender_restrictions =
	permit_sasl_authenticated,
	permit_mynetworks,
	reject_unknown_sender_domain,
	reject_non_fqdn_sender,
	permit
smtpd_helo_restrictions =
	permit_sasl_authenticated,
	permit_mynetworks,
	reject_invalid_hostname,
	reject_unknown_hostname,
	reject_unknown_client_hostname,
	reject_unknown_reverse_client_hostname,
	reject_non_fqdn_hostname,
	permit
smtpd_data_restrictions =
	reject_unauth_pipelining,
	permit

下面来一行行的看看,第一行,helo,这个对于服务器来说是必须的,每个连接过来的人必须先helo,第二行,smtpd_delay_reject,这个一定要注意,它的意思是,如果指定为no,那么helo_restrictions就会在客户端发送helo命令时运行,那么在这时,就算你要sasl_auth,也是不太可能被permit,现在把它改成yes,就是让helo的限制检查推后到data开始。所以这条指令是最重要的一条了。

下面的smtpd_etrn_restrictions这个要不要都不是太重要,除非你的邮件服务器不是一直连在internet上的,这种情况可能会需要。

后面的就很明显了,作用最大的,还是在smtpd_helo_restrictions里面,这里面,我们允许sasl认证过的客户端,reject掉无效主机名的,未知主机名的(没有DNS)最重要的,如果这个客户端没在DNS里面没有PTR就REJECT掉,新浪的邮箱也是这么做的。

还有一个要说明的,就是permit_mynetworks,不重要么,好象是的,但可以肯定的说,肯定重要,因为对于自己的服务器发送的local信件,象cron的出错,等等,还有用PHP等等方式发出的邮件,都是来自于localhost,localhost显然不是一个FQDN的主机名,所以一定要加这个permit_mynetworks,另外,mynetwork,如果不指定,可能不一定会是你想要的结果,所以最好在main.cf里面的mynetworks指定为127.0.0.0/8。 注意main.cf里面mynetworks_style和mynetworks只需要指定一个,不需要两个同时指定。

好了,自从有了这些个restriction,我的邮箱干净多了。垃圾邮件从每天的几十封变成了现在的每天只有最多5封(当然,不排除有些白痴还是在用有效的域名在发SPAM)。哈哈,世界清静了!

基于tc/connlimit的流量控制

很久很久没有写什么东西了,太忙了,根本没有时间空下来写点什么。这几天算是有点空了,大家手上的事情也不多了,所以上班的时候也比较空,不少公司的人在上班时间疯狂下载。。。 搞得我连ssh都不能正常操作。。。

 所以上网看了不少关于流量控制的东西,终于能在我这边应用一下了,原来我这边是用的l7-filter做的,但后来发现这东西是很好,但也有不足,从l7-filter出现以来,他的一些filter都已经失效了,象讯雷的,和verycd版的emule都不能用了。所以l7-filter的作用就不那么明显了。那怎么办呢?用ipp2p?这个更老。。。

 如果你仔细想想,象讯雷、BT,和电驴都是P2P的软件,P2P的软件有一个共性,那就是需要很多个并发连接。那是不可以从这方面出发呢? 答案是肯定的。

先来看看怎么控制,很简单,还是用iproute2里面的tc来控制。上网找了很多资料,我用的是gentoo,所以这里一篇howto讲的很详细(http://forums.gentoo.org/viewtopic-t-225863-postdays-0-postorder-asc-highlight-bandwidth+control-start-0.html),这里面主要用的是HTB (Hierarchical Token Bucket) qdisc,这个比CBQ相比,更易懂,更高效,而且HTB能更精确的保证流量。

有关HTB的介绍,可以看这里 http://luxik.cdi.cz/~devik/qos/htb/manual/userg.htm

上面两个文章看过之后,那么下面就是一些简单的问题了

 /sbin/tc qdisc del dev ppp0 root
 /sbin/tc qdisc add dev ppp0 root handle 1: htb default 20
 # Set total bandwidth to 80K, adsl has only about 60K
 /sbin/tc class add dev ppp0 parent 1: classid 1:1 htb rate 80kbps
 /sbin/tc class add dev ppp0 parent 1:1 classid 1:10 htb rate 80kbps prio 1
 /sbin/tc class add dev ppp0 parent 1:1 classid 1:20 htb rate 30kbps ceil 60kbps prio 2
 /sbin/tc class add dev ppp0 parent 1:1 classid 1:30 htb rate 10kbps ceil 20kbps prio 3
 /sbin/tc qdisc add dev ppp0 parent 1:10 handle 10: sfq perturb 10
 /sbin/tc qdisc add dev ppp0 parent 1:20 handle 20: sfq perturb 10
 /sbin/tc qdisc add dev ppp0 parent 1:30 handle 30: sfq perturb 10
 /sbin/tc filter add dev ppp0 protocol ip parent 1:0 handle 101 fw flowid 1:10
 /sbin/tc filter add dev ppp0 protocol ip parent 1:0 handle 102 fw flowid 1:20
 /sbin/tc filter add dev ppp0 protocol ip parent 1:0 handle 103 fw flowid 1:30

在这里,我用的if是ppp0,HTB是一个层级的,最上层的是一个总流量,然后下面的每个子类的都量都必须小于等于总流量。我把它分成三类,上面第二个文章里说到不同的class是可以借用代宽的。这也就是为什么我在1:20和1:30后面指定了ceil,这也就是说明,如果只有这一个class在使用代宽的情况下,1:20可以使用到60K,但如果1:10和1:30都在用带宽,那么1:20只能到30K。然后网卡根据每个包的mark把他们放在不同的class里。这个mark,就是iptables的工作了。 另外一个要注意的,就是速度的单位, 看看man tc可以知道,这里面的单位可以有几种方式,表示速度的单位

  • kbps   Kilobytes per second
  • mbps   Megabytes per second
  • kbit   Kilobits per second
  • mbit   Megabits per second

表示量的单位:

  • kb or k             Kilobytes
  • mb or m          Megabytes
  • mbit                 Megabits
  • kbit                   Kilobits

那么上面我是用的ppp0来做流量,这里就会有问题了,实际上,我们做流量的时候,我们可以控制的是从我们这里发出数据包的速度,而无法控制别人用多少的速度发给我。就如gentoo的那个howto里提到的,其实理论上也是可以控制的,因为我们都是用TCP/IP协议,在TCP/IP协议里面,用一个ACK packet 来回应发送数据的主机,也就是服务器,然后服务器确认你已经收到他上一个包的时候,才会再发送第二个包,那么理论上讲,可以控制这个ACK,但是就象那howto里说的,这个只是理论。。 那我们再换一个角度去考虑,我们能控制从我这台机器发出数据包的速度,那么对于NAT的网关和LAN来说,不也正是这样的么,上面的一堆tc能控制从我们NAT网站到Internet的数据速度,也就是上传速度。那么我们再反过来限制一下从NAT到我们LAN的速度,不就可以达到控制下载速度了么。是的,表面上看是这样,但是你还是无法控制Internet上进来的数据速度,这样做,只是白白浪费了带宽(不过,电信那么黑,反正也是浪费他的带宽)。

 那么现在要看的就是如何控制下载速度。下载速度控制,还有另一个问题,就是我服务器有两个网卡,一个是接LAN,一个是接ADSL,服务器还同时充当文件服务器的作用,如果我在LAN上限制流量,那么我的文件服务器不就变成了一个废物了么? 再一想,也简单,上面不是说了么流量是根据每个包的mark来决定的,那么有iptables,可以很简单的区分什么包是从internet上来的,什么包是来自我服务器的。所以,下载速度的限制,就要做在连LAN的那个网卡上。

 /sbin/tc qdisc del dev eth0 root
 /sbin/tc qdisc add dev eth0 root handle 1: htb default 20
 # Total traffic set to 1000M
 /sbin/tc class add dev eth0 parent 1: classid 1:1 htb rate 1000mbps
 /sbin/tc class add dev eth0 parent 1:1 classid 1:10 htb rate 256kbps prio 1
 /sbin/tc class add dev eth0 parent 1:1 classid 1:20 htb rate 60kbps ceil 80kbps prio 2
 /sbin/tc class add dev eth0 parent 1:1 classid 1:30 htb rate 30kbps ceil 60kbps prio 3
 /sbin/tc class add dev eth0 parent 1:1 classid 1:40 htb rate 10kbps ceil 15kbps prio 4
 # Internal traffic set to 1000M
 /sbin/tc class add dev eth0 parent 1:1 classid 1:50 htb rate 1000mbps prio 5
 /sbin/tc qdisc add dev eth0 parent 1:10 handle 10: sfq perturb 10
 /sbin/tc qdisc add dev eth0 parent 1:20 handle 20: sfq perturb 10
 /sbin/tc qdisc add dev eth0 parent 1:30 handle 30: sfq perturb 10
 /sbin/tc qdisc add dev eth0 parent 1:40 handle 40: sfq perturb 10
 /sbin/tc qdisc add dev eth0 parent 1:50 handle 50: sfq perturb 10
 # Send packet to different queue according to it's mark number
 /sbin/tc filter add dev eth0 protocol ip parent 1:0 handle 1 fw flowid 1:10
 /sbin/tc filter add dev eth0 protocol ip parent 1:0 handle 2 fw flowid 1:20
 /sbin/tc filter add dev eth0 protocol ip parent 1:0 handle 3 fw flowid 1:30
 /sbin/tc filter add dev eth0 protocol ip parent 1:0 handle 4 fw flowid 1:40
 /sbin/tc filter add dev eth0 protocol ip parent 1:0 handle 5 fw flowid 1:50

这里,我把lan的总流量设定成1000M,另外再加一个class就是1000M的流量。其它几个都是控制下载流量的。

 下面就是iptables的工作了。在内核版本2.6.23以前,connlimit都是一个iptables的P-O-M,从2.6.23开始,它正始进入内核。所以我们的iptables在编译时就要使用内核的extention才行。不知道别人的系统启动好后eth0的mtu是多少,但是正常情况下,adsl的mtu是1460,而我其它的网卡的mtu是1500,这样会有一些问题,会让一些网站不能正常访问, 所以必须要在iptables里面加上-A FORWARD -p tcp -m tcp –tcp-flags SYN,RST SYN -j TCPMSS –clamp-mss-to-pmtu 这个东西是干什么用的?自已经去man吧,简单的讲,他把一个TCP SYN包的MSS值减40,所以它只能工作在mangle表中。这样就没有问题了。另外,下面的所有的东西都是在mangle表中进行的。

对于上传,很好控制,把所有来自LAN的IP包全都mark成102,也就是对应最上面的速度30K,峰值速度60K。然后可以随时进行机动处理,把它们mark成101或者最慢的103

对于下载,要区分文件服务和Internet,这个很简单,对于所有不是来自于192.168.0.1(服务器IP)的,全部mark成2(60K,峰值80K),所有来自于192.168.0.1的,全部mark成5(最大速度)。

其它的,可以用connlimit来进行限制,连接数大于20的给30K,连接数大于40的,只给10K,这样的话,只要用BT,讯雷或者什么什么P2P的软件,连接数会很容易的超过20,我前几天看到一个连接数为2000多个,其中一半是讯雷的。

另外,iptables还有一个hashlimit,这个是统计所有的包速率,也包含LAN到Server的文件服务,这个不同于connlimit,connlimit只计算tcp连接,另外,在网上能找到的文章里提到的iplimit,老早就被hashlimit代替了。

 最后忘了说了,控制上传的chain是FORWARD,控制下载的是POSTROUTING。

可爱而搞笑的 Microsoft 到底是Vista SP1还是Windows Server 2008

前天收到MS的一封信,说是在connect上面WDK的beta出新版本了, 这个beta是为Vista SP1准备的, 我看了一下,对Beta的SDK没什么兴趣,就没管它,今天又收到一封信,说是对不起,上封信中的内容说错了,不是Vista SP1,而是Windows Server 2008的SDK出了新版本了. 哈哈,太搞笑了吧, 到底是Vista SP1还是Windows Server 2008,我也没兴趣了, 因为它是测试版的SDK.

下面看看原信吧: 第一封

Hello WDK Beta Users-

A new beta WDK build is now available for download on Connect. This WDK beta release to Connect coincides with the recent OS beta release for Vista SP1 Preview. Please take a few moments to install the new build and test as applicable for your needs. As always, please file bugs as quickly and detailed as possible.

Thank you for your continued support!

WDK Team

 第二封:

Apologies All, We made a typo in the last notification that went that referenced the wrong OS. We didn’t mean to imply or cause any confusion on the availability of the Service Pack for Vista. The WDK build available now on connect is the latest for Windows Server 2008.

Thanks,
The WDK Beta team

可爱的Microsoft,不要再搞这种情况了.

统计LAN中每IP流量的好工具bandwidthd

因为公司的服务器也当网关,而且公司的网络连接不算快的,只是1M的ADSL,常常被人用来迅雷, 讯雷这种吃肉不吐骨头的烂东西,一开就是所有网络资源被占用。 虽然有ipp2p来过滤P2P的东西,但是这个ipp2p对verycd的电驴不起什么作用。 简单的netstat-nat又不能显示流量, google了一堆文章基本没什么用。

最后在一个网站看到了bandwidthd这个东西,虽然功能不强,但是统计LAN中每个IP的流量这个功能就足够了。 于是去他的主页看了看 http://bandwidthd.sf.net 发现居然两年前就已经停止开发了。 下下来compile之后发现几个问题

  1. bandwidthd.conf是放在程序里面的,不能指定。
  2. pid文件也是不能指定
  3. 输出目录只能在bandwidthd所在目录下的htdocs里面

这三个问题就已经让我的gentoo很不爽了,居然portage里面没有这个东西。于是自己写了个ebuild, 改了改程序,让他支持几个命令行参数。这样就可以在命令行里指定pidfile的位置,conf文件的位置和输出目录。 改完之后,成功运行,于是就发了一个bug到gentoo, 地址在这里 http://bugs.gentoo.org/show_bug.cgi?id=184002

这个东西已经很老了,而且它的流量是针对整个interface/lan的,不能只列出nat的流量,能有多个subnet,但只能用一个interface。 不过还是不错。

另一个问题就是现在的内核2.6.18以后,可以选择不用/proc/net/ip_conntrack,这样的话,就必须用netfilter新出的libnetfilter_conntrack, 这个是userspace connection track访问的库。 等过一段时间看看有没有空,自己重新写一个基于这个libnetfilter_conntrack的新netstat-nat。

Visual Studio 2005 SP1之后的一些FIX

今天在家玩Live beta里面的Live Mail,这个应该是Outlook Express的替代产品,里面有NNTP的支持,所以就加了几个常去看的新闻组。 里面提到了VS2005 SP1里面的几个问题,其中一个是我在VS2005 SP1 beta里面提到的问题,SP1好象不能编译某些模板类。但这个bug最终还是没有处理,SP1也一样存在。

在有一个邮件里提到了MS已经针对这个问题做了一个hotfix,我就找到了这个地址:

https://connect.microsoft.com/VisualStudio/content/content.aspx?ContentID=3705&wa=wsignin1.0

这里面列出了不少东西,但正如这页的红色字部分提到的,下面列出的hotfix没有经过完整的regression test, 而且这些hotfix只针对某一个问题。

我仔细看了看。里面有几个是VS2005 SP1之后的hotfix,其中一个是针对我以前提高的模板类的问题的(http://support.microsoft.com/kb/930198)。另外还有两个:

  1. FIX: Visual C++ .NET 2005 SP1 C runtime daylight saving time 2007 update for the TZ environment variable (http://support.microsoft.com/kb/932391)
  2. An Add-in does not load when you start Visual Studio 2005 IDE( http://support.microsoft.com/kb/933054)

另外还有一个是针对VB的,对我没用。 有兴趣的可以去看看。最后要说的就是这几个都会更新相关的Merge Module。

Samba 及 Linux 的网络性能调整

不知道有多少公司的内部打印及文件服务器是用的Linux,我想肯定不会太多,因为Windows实现起来更方便,更快速,当然,Windows也是更Danger。 因为Windows有太多不确定性的东西,Virus,木马,等等。

但Linux也不见得太安全,不过因为我对Linux非常熟了,而且在Windows下面不了解,所以公司的文件服务器和打印服务器都是在一台机器上面实现的, 同时这台服务器还管理公司网络的DHCP和DNS服务。

年初的时候,因为binutils的新版本的一些Optimize,所以把服务器的Linux重新做了,也换成了selinux,不过Gentoo下的selinux真是想让人死。。 x86下面没什么问题,amd64下面,如果你是用multilib的,那我不清楚,我只用amd64的ABI,没有multilib, 在这种情况下,Gentoo的selinux会有不少问题。根本不可能用enforce模式。

就这样用了近半年,网络性能一直很差,从服务器上Copy文件或者反过来,性能都很差, 看看Windows的Task Manager里面的Networking,LAN的使用率最多也只能在40%左右,这个也太低了点吧?想办法解决一下。

先google了一下,发现IPv4可以有一些调整. 主要是下面几个参数:

  • /proc/sys/net/core/rmem_default 定义默认的接收窗口大小;对于更大的 BDP 来说,这个大小也应该更大。
  • /proc/sys/net/core/rmem_max 定义接收窗口的最大大小;对于更大的 BDP 来说,这个大小也应该更大。
  • /proc/sys/net/core/wmem_default 定义默认的发送窗口大小;对于更大的 BDP 来说,这个大小也应该更大。
  • /proc/sys/net/core/wmem_max 定义发送窗口的最大大小;对于更大的 BDP 来说,这个大小也应该更大。
  • /proc/sys/net/ipv4/tcp_window_scaling  启用 RFC 1323 定义的 window scaling;要支持超过 64KB 的窗口,必须启用该值。
  • /proc/sys/net/ipv4/tcp_sack 启用有选择的应答(Selective Acknowledgment),这可以通过有选择地应答乱序接收到的报文来提高性能(这样可以让发送者只发送丢失的报文段);(对于广域网通信来说)这个选项应该启用,但是这会增加对 CPU 的占用。
  • /proc/sys/net/ipv4/tcp_fack 启用转发应答(Forward Acknowledgment),这可以进行有选择应答(SACK)从而减少拥塞情况的发生;这个选项也应该启用。
  • /proc/sys/net/ipv4/tcp_timestamps 以一种比重发超时更精确的方法(请参阅 RFC 1323)来启用对 RTT 的计算;为了实现更好的性能应该启用这个选项。
  • /proc/sys/net/ipv4/tcp_mem 确定 TCP 栈应该如何反映内存使用;每个值的单位都是内存页(通常是 4KB)。第一个值是内存使用的下限。第二个值是内存压力模式开始对缓冲区使用应用压力的上限。第三个值是内存上限。在这个层次上可以将报文丢弃,从而减少对内存的使用。对于较大的 BDP 可以增大这些值(但是要记住,其单位是内存页,而不是字节)。
  • /proc/sys/net/ipv4/tcp_wmem 为自动调优定义每个 socket 使用的内存。第一个值是为 socket 的发送缓冲区分配的最少字节数。第二个值是默认值(该值会被 wmem_default 覆盖),缓冲区在系统负载不重的情况下可以增长到这个值。第三个值是发送缓冲区空间的最大字节数(该值会被 wmem_max 覆盖)。
  • /proc/sys/net/ipv4/tcp_rmem 与 tcp_wmem 类似,不过它表示的是为自动调优所使用的接收缓冲区的值。
  • /proc/sys/net/ipv4/tcp_low_latency 允许 TCP/IP 栈适应在高吞吐量情况下低延时的情况;这个选项应该禁用。
  • /proc/sys/net/ipv4/tcp_westwood 启用发送者端的拥塞控制算法,它可以维护对吞吐量的评估,并试图对带宽的整体利用情况进行优化;对于 WAN 通信来说应该启用这个选项。
  • /proc/sys/net/ipv4/tcp_bic 为快速长距离网络启用 Binary Increase Congestion;这样可以更好地利用以 GB 速度进行操作的链接;对于 WAN 通信应该启用这个选项。

(以上内容引自http://www.ibm.com/developerworks/cn/linux/l-hisock.html

看了上面的内容,我对自己的服务器调整了一下,加了下面几个参数到sysctl.conf里面

net.core.rmem_max = 8388608
net.core.wmem_max = 8388608
net.ipv4.tcp_rmem = 8192 4194304 8388608
net.ipv4.tcp_wmem = 4096 2097152 8388608

再去试了一下Copy文件,速度果然有5%-8%的提高,那么,下面的问题,就是samba的性能了。去samba的网站看看它的howto, 里面提高几个问题,一个是samba如何log,这个会对性能有一定的影响,但不大,最多也就是百分之几的差别。仔细看了howto以后,我设了如下几个参数:

# Some other performace tuning options
# disable links and symbol links
    follow symlinks = no
    wide links = no
# enable some read/write tuning
    use sendfile = yes
    read raw = yes
    write raw = yes
    aio read size = 16384
    aio write size = 16384
    write cache size = 262144
    max xmit = 65536
    large readwrite = yes
    getwd cache = yes
# disable locking, because only 2 share can be written.
    strict locking = no
    fake oplocks = yes
    oplocks = no

来说明一下,

  • follow symlinks ,对我来说不需要,Windows好象不支持这种符号连接,我也不需要在共享里有这个东西,所以关闭
  • wide links,同上,不需要
  • aio read/write size,是和异步读写有关的,设大一点比较好
  • oplocks和locking这两个决定samba是否对文件进行lock测试,如果一个用户以write方式打开了这个文件,那么其它用户就不能再以write打开,这两个东西很费内存,也同样会降低samba的性能。 另外,我有很多个share,其中只有两个是所有人都可以写的,其它的都只有一个用户能写,对于后者,不需要打开这个,因为共享设计就是别人无法写。 那么就在global里面把这几个和lock有关的参数关掉,在那两个所有人可以写的share再打开。
  • 所有基它的参数,都是samba里面提到的performance tunning的参数我就不一个一个的说了,要了解它的可以去 http://us1.samba.org/samba/docs/man/Samba-HOWTO-Collection/ 看看

好了,samba也调整了,再试一下copy文件。WOW~~ 网络占用率达到了80%,提高了一倍!! 当然,所有的有数值的参数,都是需要对应实际应用来调整的,我在这里只是简单的设了一下,性能就提高一倍多,相信仔细调整后网络占用率应该能达到与Windows相当的水平,也就是80%~90%

NOD32的IMON Internet Monitor问题太多了

已经快一年了,我发现公司的XSI再也不能用Distribute Render,这个让我们的渲染测试工作浪费了很多的时间,试了很多种方法,也解决不了。

今天还是需要,所以我再试试。把Port加到Windows的Firewall里面,Master和Slave都加上,不行,ray3死在那里等Slave的回应。 然后把两台的Firewall全关掉,再试,一样不能Render,我看了Windows的Event Log,里面写的很清楚,ray3-server已为master经启动了一个进程。那为什么还是不行呢??

怎么想也想不通。。。 突然一个念头。我给别的朋友也介绍过NOD32,他是安在服务器上,服务器上有HTTP,DNS和EMAIL服务,可是安了NOD32以后只有HTTP服务能正常访问,我看了一下安装时的说明,NOD32说,如果你把NOD32安在服务器上,最好不要开IMON服务。于是我让那朋友关掉NOD32的IMON服务,一切正常了。那我这个问题会不会也是IMON的问题呢?

于是,试,关掉IMON,还是不行,再一看,关掉IMON之后,我的SPM和ray3-server都停掉了,重开,slave机器也是一样被关掉了。 结果一切正常了。

再回过头来看看NOD32的IMON服务,好象没有可以控制排除哪个Port的,只有排除什么什么程序。 可是,又不能直接关掉它,必竟它还是有作用的,特别应对是一些IE7不能发现的一些破网站。

好了,结论就是,NOD32的IMON会让mental ray无法Distribute Render。 当然,结论也不限定于mental ray,对于其它的可以Distribute Render的渲染器,肯定也是一样的。那就这么办,反正在渲染的时候也不会去上什么网站,那就是在渲染的时候关掉。

GetRaycastIntersections of XSI

最近做的项目中,需要XSI有一个功能,举例来说吧,我有一堆树(>5000个)也做好了地面,树的位置等等都已经随机好了,但是有一个很大的问题,就是我如何能保证所有的树是种或drop在地面上,而不是飘在空中的。 也许有别的办法一个一个的处理,我想到的就有一种方法,就是地面如果是Nurbs的话,可以用Surface Constraint,但是这个也有没办法处理的情况,如果地面不是Nurbs呢?

于是我就想到另一个方法,几乎所有的三维软件都有Raycast的功能, 那我能不能对树进行一下处理,让每个树的中心都在树的最下面,只要保证每个树的中心点在这个地面的一个polygon的平面上,那不就可以基本保证每个树都是在地面上么? 那就来看看SDK。 SDK里有一个Code Example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
        using namespace XSI; 
        Application app; 
        Model root = app.GetActiveSceneRoot(); 
        Null nullObj; 
        root.AddNull( L"myNull", nullObj ); 
 
        CValueArray args(8); 
        CValue outArg; 
        args[0] = CValue(nullObj.GetRef()); 
        args[1] = CValue(L"-2.0"); 
        args[2] = CValue(L"8.0"); 
        args[3] = CValue(L"5.0"); 
        args[4] = CValue(L"siRelative"); 
        args[5] = CValue(L"siView"); 
        args[6] = CValue(L"siObj"); 
        args[7] = CValue(L"siXYZ"); 
        app.ExecuteCommand( L"Translate", args, outArg ); 
 
        X3DObject meshSphereObj; 
        root.AddGeometry( L"Sphere", L"MeshSurface", L"", meshSphereObj ); 
        PolygonMesh meshSphereGeom( meshSphereObj.GetActivePrimitive().GetGeometry() ); 
 
        CVector3 nullPos = nullObj.GetKinematics().GetGlobal().GetTransform().GetTranslation(); 
        CVector3 sphereCenter = meshSphereObj.GetKinematics().GetGlobal().GetTransform().GetTranslation(); 
        CVector3 ray; 
 
        ray.Sub( sphereCenter, nullPos ); 
 
        PointLocatorData closestPointLocator = meshSphereGeom.GetRaycastIntersections( 1, (double*)&nullPos, (double*)&ray, siSegmentIntersection ); 
 
        double pos[3]; 
        LONG triVtx[3]; 
        float triWei[3]; 
 
        meshSphereGeom.EvaluatePositions(closestPointLocator, -1, 0, pos); 
        meshSphereGeom.GetTriangleVertexIndexArray(closestPointLocator, -1, 0, triVtx); 
        meshSphereGeom.GetTriangleWeightArray(closestPointLocator, -1, 0, triWei); 
 
        app.LogMessage(CString(L"The mesh sphere intersects with the ray coming from the Null at (") 
                + CString(CValue(pos[0])) + L", " + CString(CValue(pos[1])) + L", " + CString(CValue(pos[2])) + L")"); 
        app.LogMessage(CString(L"which corresponds to the triangle made of vertices (") 
                + CString(CValue(triVtx[0])) + L", " + CString(CValue(triVtx[1])) + L", " + CString(CValue(triVtx[2])) + L")."); 
        app.LogMessage(CString(L"The barycentric weight relatively to each triangle vertex is (") 
                + CString(CValue(triWei[0])) + L", " + CString(CValue(triWei[1])) + L", " + CString(CValue(triWei[2])) + L")."); 
        //INFO : The mesh sphere intersects with the ray coming from the NULL at (-0.695969, 3.28837, 1.85179) 
        //INFO : which corresponds to the triangle made of vertices (14, 21, 22). 
        //INFO : The barycentric weight relatively to each triangle vertex is (0.347985, 0.121569, 0.530446).

看了一下,也试了一下,这里并没有处理如果source和target都有各自的global transfrom, 如果有的话(也就是说 source和target的SRT都不是Freeze过的) 那么上面的代码返回的位置都是错的。

其实相对的处理也很简单,如果你搞过DX或OpenGL的开发的话,就明白,这些source物体的位置需要变换一下,由world变换到target的坐标,然后再进行intersection的计算,这样得出的位置是target的local坐标,再把这个local变换到world就可以了。 XSI给我们提供了几个函数来处理,如下

1
2
3
4
5
6
7
8
9
CTransformation  MapObjectPoseToWorldSpace (const CTransformation &in_transfoObjectSpace, const CTransformation &in_transfoPose) 
CVector3  MapObjectOrientationToWorldSpace (const CTransformation &in_transfoObjectSpace, const CVector3 &in_vector3Orientation) 
CVector3  MapObjectPositionToWorldSpace (const CTransformation &in_transfoObjectSpace, const CVector3 &in_vector3Position) 
CTransformation  MapWorldPoseToObjectSpace (const CTransformation &in_transfoObjectSpace, const CTransformation &in_transfoPose) 
CVector3  MapWorldOrientationToObjectSpace (const CTransformation &in_transfoObjectSpace, const CVector3 &in_vector3Orientation) 
CVector3  MapWorldPositionToObjectSpace (const CTransformation &in_transfoObjectSpace, const CVector3 &in_vector3Position) 
CTransformation  MapObjectPoseToObjectSpace (const CTransformation &in_transfoObjectSpace, const CTransformation &in_transfoSpace, const CTransformation &in_transfoPose) 
CVector3  MapObjectOrientationToObjectSpace (const CTransformation &in_transfoObjectSpace, const CTransformation &in_transfoSpace, const CVector3 &in_vector3Orientation) 
CVector3  MapObjectPositionToObjectSpace (const CTransformation &in_transfoObjectSpace, const CTransformation &in_transfoSpace, const CVector3 &in_vector3Position)

有了这样的方式,下面是我的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
	CTransformation localTrans = MATH::MapWorldPoseToObjectSpace(target.GetKinematics().GetLocal().GetTransform(), 
		X3DObject(sourceObjects).GetKinematics().GetGlobal().GetTransform()); 
	CVector3 pos = localTrans.GetTranslation(); 
	PointLocatorData closestPointLocator = targetGeo.GetRaycastIntersections(1, (double*)&pos, (double*)&ray); 
 
	double finalPos[3]; 
	LONG triVtx[3]; 
 
	targetGeo.EvaluatePositions(closestPointLocator, -1, 0, finalPos); 
	targetGeo.GetTriangleVertexIndexArray(closestPointLocator, -1, 0, triVtx); 
 
	pos.Set(finalPos[0], finalPos[1], finalPos[2]); 
	pos = MapObjectPositionToWorldSpace(target.GetKinematics().GetGlobal().GetTransform(), pos); 
 
	if(triVtx[0] != -1) 
	{ 
		CTransformation trans = X3DObject(sourceObjects).GetKinematics().GetGlobal().GetTransform(); 
 
		trans.SetPosX(pos.GetX()); 
		trans.SetPosY(pos.GetY()); 
		trans.SetPosZ(pos.GetZ()); 
		X3DObject(sourceObjects).GetKinematics().GetGlobal().PutTransform(trans); 
	} 
	else 
		app.LogMessage(sourceObjects.GetAsText() + L" can not be droped on to the target object.");

这里面的if(triVtx[0] != -1)就是看是否有intersection,当然PointLocatorData 也会告诉你有几个交叉点,如果是0,当然就是没有交叉点了,这个和我用的方法是一样的, 但是,函数再快,

Next Page »

Based on FluidityTheme Redesigned by Kaushal Sheth