网站负载均衡与高可用方案的选型

负载均衡与高可用本来是个很大的话题,可以做的方式多种多样,我只是把自己熟悉一点的简单讲一些。 bind1. 其实最原始的负载均衡就是在DNS上直接做多个A记录,这样用户解析网站域名的时候就会随机解析为我们设置的一个A记录的IP。但我们不能改变2个IP被解析的概率,如果2个机器的负载能力不同,那这种方式就欠妥了。2. 既然直接A记录不好控制比例,那么我们可以通过bind的view来根据查询源IP做解析。比如把网通的用户解析为网通机房的IP,把电信的用户解析为电信IP,具体的策略设置非常灵活,根据实际情况而定。对于DNS服务器自身的高可用和负载均衡可以使用keepalived打包完成。如果有条件的可以多个DNS机器跑OSPF集群解决。 LVS 一般大家使用keepalived来管理lvs,并能借助keepalived支持的vrrp协议实现HA。对于CDN节点这种类型的话,非常适用与LVS(DR)+[haproxy+squid],因为单个几点的机器比较少,网络结构不会太负载,而流量又会非常大。直接在一个万兆交换机下挂10多个机器,其中2个做LVS(主/备),另外的所有机器同时部署haproxy和squid,所有机器的squid配置都一样,同时上面跑的haproxy的配置也都一样,backend里面把所有的机器都加进去,这样可以做采用URL hash算法来提高缓存的命中率。但是需要注意此种模式,交换机下的流量会比较大,比不使用haproxy会翻倍,一定要保证交换机的背板带宽够用。LVS相对其他很多负载均衡软件的优势时可以支持UDP,而且DR模式和TUN模式的时候对于大部分业务类型只有用户的请求经过LVS机器,后端服务器响应的内容都是直接返回给用户,所以不会像NAT模式进出的流量都要经过LVS。但是DR和TUN都需要给后端机器绑VIP,比较麻烦。而NAT模式又需要其他机器把LVS机器作为网关,也限制了其使用的范围。相对来说淘宝改的FULLNAT模式部署起来对环境的要求比较低,但是短时间内不会合并到trunk,使用RHEL以外发行版的同学基本不能使用。而且后端机器要获取客户端的IP需要单独安装一个toa的内核模块,因为做了snat后他们只能在一个tcp头的保留字段插入客户的真实IP。 haproxyhaproxy其实是和很成熟的负载均衡软件,4/7通吃,调度算法也非常丰富,配置项目非常多,文档支持也很好,现在也支持了ssl加密了。haproxy因为之前一直采用的tunnel-like模式,所以现在对后端机器开不了keepalive,缺一个非常非常重要的特性。或许在2013年能搞出来。 nginxnginx作为现在最火的webserver,在做代理方面也非常流行,做ssl加密非常方便,配置比起haproxy更加简单和灵活。但是整体而言,不能做4层的反向代理是个遗憾。负载算法方面nginx的不像haproxy有那么多,不过其实一般大家用的最多的还是简单的轮询,流量特别大的网站都是不可能使用wlc算法的,不然机器启动就直接被打暴了。 haproxy和nginx的高可用都需要借助keepalived来做(需要注意改内核参数使备机能直接监听非本地的IP)。当然,如果单个nginx或者haproxy撑不住,也可以使用2层结构,第一层跑LVS,下面一层跑haproxy或者nginx,不过此时VIP都是绑定在第一层的LVS上面。lvs上频繁修改配置reload还是需要注意,不小心搞挂了就悲剧了。 经常见很多论坛,就两三个机器在折腾LVS,问比如遇到RS上不能访问VIP,或者Director上访问不了VIP这些确实比较蛋疼的问题,基本都是没有仔细搞清楚lvs原理,也没有好好看文档的,对于这种我是建议直接跑个haproxy或者nginx就搞定了。

December 27, 2012 · 1 min · pm

分析全站应用的响应时间

因为流量镜像分析的功能还没有做好,目前要统计全站的应用的响应时间就比较麻烦。我直接把做代理的nginx的日志都拷贝出来,然后跑脚本。由于日志是压缩过的,本来直接perl里面可以使用cpan里的一个gunzip的模块,但是测试后发现性能实在烂的不行,连gunzip速度的1/4都不到。后来直接换成调用pigz多线程解压,虽然pigz在解压时不如在压缩时能体现出多线程优势,但是相对直接gunzip还是快了些,反正机器有的是CPU,主要是磁盘性能不行。[perl]#!/usr/bin/perluse warnings; use strict; use IO::Uncompress::Gunzip qw(gunzip $GunzipError); my %dtimes; my %dseconds; my $DEBUG=1; my @logs; my $PRE="/home/test/temp"; my $tempfile="$PRE/gunziptemp"; my @time=(1,2,3,4,5,6,7,8,9,10); opendir my $DIR,"$PRE" or die "open dirctory failed"; while(my $dir=readdir $DIR) {push @logs,$dir if $dir =~/log.gz/ ; }close $DIR; foreach my $log(@logs) {print STDERR "analysis $log\n" if ($DEBUG ==1 ); # open my $logfd,"<$PRE/$log" or die "open $log failed\n"; # open my $unziplog,">$tempfile" or die "open unzip file failed\n"; # gunzip $logfd => $unziplog or die "unzip $log failed\n"; `pigz -d -k -c $PRE/$log >$tempfile ` or "unzip $log failed\n"; print STDERR "unzip $log ok \n" if ($DEBUG ==1 ); # close $unziplog; open my $unziplog,"<$tempfile" or die "open2 unzip file failed\n"; print STDERR "start analysis log\n" if ($DEBUG ==1 ); while(my $line=<$unziplog>) {my @temp; my ($domain,$restime); if($line=~/xxxx.com/) {@temp=split(/\s+/,$line); $domain=$temp[7]; $restime=$temp[-1]; next if( $restime !~ /\d+/); next if( $domain !~ /xxxx.com/ ); $domain=~s/:\d+//; }if(defined $domain && defined $restime ){$dtimes{$domain}++; my @t=grep { $_ – $restime <0} @time; foreach my $k(@t){$dseconds{$domain}{$k}++; } } ...

December 26, 2012 · 2 min · pm

nginx反向代理时文件下载异常的一次排查

上周结果有客户投诉说不能正常下载账单,之前我以为是nginx的proxy_read_timeout设置引起的。因为我之前错误地把这个理解为nginx从后端读取返回的总时间限制。但是后来对这个参数的说明进行了进一步的查阅,发现不是这么个意思。 [查阅](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_read_timeout)``` Defines a timeout for reading a response from the proxied server. A timeout is only set between two successive readoperations, not for the transmission of the whole response. If a proxied server does not transmit anything within thistime, a connection is closed. 今天晚上回到家后又测试了一下,因为家里的网络非常烂,但是测试了几次都能正常下载。考虑到之前我让同事转告客户那边换个chrome浏览器,但客户那边说无法下载其他浏览器,让我们给QQ传个文件过去,我就猜想对付是不是内部使用代理系统上网的,可能会引发很多这样那样的问题。 只能让对方再试试下载其他文件有没有问题了。如果还有问题肯定是对方内部网络的问题了。

December 25, 2012 · 1 min · pm

tcp传输时影响性能的几个常见点

其实都是摘自TCP/IP详解和HTTP权威指南上面的。 1.http事务的时延主要是指我们要发起一个请求,需要先进行dns查询,然后对对应的IP:PORT发起请求。整个过程总每个环境都可能造成时延,比如dns查询,连接建立,服务器响应,请求和响应报文的大小。2.TCP三次握手。连接建立的三次握手也比较耗时,尤其是client和server相隔很远的时候物理距离造成的光传播时间本来就长,而且可能网络之类不佳造成重传等等都可能使得连接的建立耗时很久。对于返回内容比较少的情况下,连接建立时间占整体时间的比例会相对较大。3.延迟确认简单地说server向client连续发了几个组的数据,client其实不会直针对每个组都去发ACK确认,而是会在收到第一组数据后启动延迟定时器,过100-200ms再看看现在收到的所有组有那些,能连续组装起来的,就直接把最大的序号的组确认一下就OK了。4.TCP慢启动TCP的慢启动本来是为了避免快的发送方到慢的接受方这样的情况。所有每个连接连接后,开始发送的数据量比较小,比如一个分组,加入对方收到好ACK确认了,那么下次就直接发2个分组的数据,以此类推。因此新的连接比已经完成“调谐”的连接要慢一些,如果双方是持续连接(keepalive),那么效果就会比较好。5.Nagle算法与TCP_NODELAYnagle算法本来是为了避免每次发一个小的分组,造成网络性能非常差。Nagle算法要求一个TCP连接上最多只有一个未被确认的完成的小分组,但因为确认过程本身是由延迟定时器(100-200ms)来完成所以可能会造成额外的延迟。一般来说nagle算法会在对方确认的越快就会发送的最快,比较自适应。但是如果是类似telnet这种远程登录操作,或者是X系统里面鼠标的一个移动,那们都是希望能尽量实时,不要延迟,基本都会选择TCP_NODELAY选项关闭nagle算法。6.TIME_WAIT累计和端口耗尽。每个socket其实是由``` 源ip:源port,目标IP,目标port 当然,假如后面有很多机器,那么因为目标IP不同,实际情况会好些。

December 25, 2012 · 1 min · pm

用shc加密shell脚本

大家经常有这样的需求,比如脚本里放了一个公共的账户密码,给别人用的时候不希被发现。这个时候可以使用shc对脚本进行加密。可以直接在这个页面下载。 [shc](http://www.datsi.fi.upm.es/~frosal/sources/shc.html)[页面](http://www.datsi.fi.upm.es/~frosal/sources/shc-3.8.9.tgz)编译后就能使用了,使用的方式比较简单 ./shc -r -f script.sh 详细的用法可以help一下看看# ./shc -helpshc Version 3.8.7, Generic Script Compilershc Copyright (c) 1994-2009 Francisco Rosalesshc Usage: shc [-e date] [-m addr] [-i iopt] [-x cmnd] [-l lopt] [-rvDTCAh] -f script

December 24, 2012 · 1 min · pm

64位linux下单个进程的最大线程数

32位系统下的单个进程的最大线程数受限于3G的虚拟内存限制,所以能起的线程最多就1024*3/8=384个。那么64位的系统是不是就没有限制了呢?实际上64位系统的情况就比较复杂了,有很多限制条件。。比如/proc/sys/kernel/pid_max 设置的 pid的最大值(默认32768); /proc/sys/kernel/threads-max 设置的最大线程数(默认32128)。/proc/sys/vm/max_map_count(默认65530)设置最大的内存映射区域数量。另外就是 /proc/sys/vm/overcommit_memory 和/proc/sys/vm/overcommit_ratio 也有些关系。 max_thread.c[c]#include#include#include#define MAX 40000void *sleep1k(void){sleep(1000); return NULL; }int main(){int i = 0; pthread_t thread; [/c]

December 22, 2012 · 1 min · pm

使用haproxy为windows远程桌面做负载均衡

公司有很多windows的服务器,因此就有了windows的跳板机机器。但是因为很多很多蛋疼的问题,造成现在直接A记录搞的经常有问题。我自己其实在测试环境测试过用haproxy做负载均衡的,比较简单。直接贴配置文件吧``` globallog 127.0.0.1 local0maxconn 40960pidfile /opt/haproxy/haproxy.pidnbproc 1uid 99gid 99daemon defaultslog globalmode tcpoption dontlognullretries 3option redispatchcontimeout 5000clitimeout 50000srvtimeout 50000listen stat *:8000mode httpoption httplogmaxconn 1000stats refresh 60sstats uri /statsstats realm GNUerstats auth admin:adminfrontend windowbind *:3389default_backend windowsrsbackend windowsrsmode tcpbalance leastconnserver windows1 10.15.0.51:3389 weight 10 check inter 2000 rise 2 fall 4server windows2 10.15.1.109:3389 weight 10 check inter 2000 rise 2 fall 4frontend window_1bind *:3390default_backend window_rs1backend window_rs1mode tcpbalance leastconnserver windows1 10.15.0.51:3389 weight 10 check inter 2000 rise 2 fall 4frontend window_2bind *:3391default_backend window_rs2backend window_rs2mode tcpbalance leastconnserver windows2 10.15.1.109:3389 weight 10 check inter 2000 rise 2 fall 4 ![6](/picture/10a446df.png)

December 22, 2012 · 1 min · pm

perl的输出陷阱

以前现用perl为我们的首页系统写个daemon进程来做更新首页的事情,其实简单的说就是前端的同事把新的页眉放到存储里面。我的脚本去定期检测一下md5值,然后取出来。取出来后会对指定的字段进行替换。但是在做的时候就遇到点问题,日志一直不打出来。后来简单测试了一下perl #!/usr/bin/perluse strict; use warnings; open LOG,">test.out" or die "open file failed\n"; foreach my $k (0..25){ print LOG ($k,"\n"); sleep(1); } 运行这个的时候你会发现只有程序结束了才会写到文件。后来咨询了一下学了这样搞```perl #!/usr/bin/perluse strict; use warnings; use IO::Handle; open LOG,">test.out" or die “open file failed\n”; LOG->autoflush(); foreach my $k (0..25){ print LOG ($k,"\n"); sleep(1); } 当然也可以选择这样做```perl #!/usr/bin/perluse strict; use warnings; open LOG,">test.out" or die "open file failed\n"; foreach my $k (0..25){syswrite(LOG,"$k\n",1024); sleep(1); }

December 17, 2012 · 1 min · pm

使用DBI模块来批量插入数据库

这个是去年管理测试环境的时候,直接用脚本批量操作数据库```perl #!/usr/bin/perl -wuse DBI;use strict;my $dbh;my $INFILE=$ARGV[0];open LIST ,"<$INFILE" or die "can’t open $INFILE\n";my $table="table_appinfo";$dbh=&connect();my $qurey=qq{insert into $table (ip,name,manage,dev,location) values(?,?,?,?,?) };my $sth=$dbh->prepare($qurey) || die $dbh->errstr;my $dev="lab";my $manage="namexxx";my $location=’location’;while(my $line=){my ($ip,$name)=split /\s+/,$line;$sth->execute($ip,$name,$manage,$dev,$location);print "insert $ip $name\n";}$dbh->disconnect();

December 17, 2012 · 1 min · pm

使用perl mail::sender发送邮件

如果是登陆到远程smtp服务器上发送的话可以使用```perl #!/usr/bin/perl -wuse Mail::Sender; use strict; printf("please input your passwd:\n"); system "stty -echo"; my $pass; chomp($pass=); system "stty echo"; my $sender= new Mail::Sender{smtp=> ‘smtp.126.com’,from=>’[email protected]’,auth=>’LOGIN’,authid=>’user’,authpwd=> $pass,debug=>’./perllog’}; $sender->MailMsg({to=>’[email protected]’,subject=>’perl send mail’,msg=>’中文测试’,debug=>’./perllog’}) or die "failed"; $sender->Close(); 再提供一个可以发送附件的版本,直接使用内部的邮件服务器发送一些小文件。如果想使用远程服务器可以根据前面的脚本修改。#!/usr/bin/perl -wuse Mail::Sender; my $txt; $txt="hi,all :\n\t xxxx!"; &sendmsg($ARGV[0],$txt,$ARGV[1]); sub sendmsg(){my $add=$_[0]; #email addressmy $msg="$_[1]"; #email txtmy $file=$_[2]; #fileopen my $LOG ,">/tmp/mail.log"; my $sender =new Mail::Sender{ $sender->MailFile({to=>$add,subject=>“hello”,msg=>"$msg",debug=>$LOG,file => $file,charset => ‘gb2312’}) or die “$Mail::Sender::Error\n”;close($LOG);$sender->Close;}

December 17, 2012 · 1 min · pm