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_NODELAY
nagle算法本来是为了避免每次发一个小的分组,造成网络性能非常差。Nagle算法要求一个TCP连接上最多只有一个未被确认的完成的小分组,但因为确认过程本身是由延迟定时器(100-200ms)来完成所以可能会造成额外的延迟。一般来说nagle算法会在对方确认的越快就会发送的最快,比较自适应。但是如果是类似telnet这种远程登录操作,或者是X系统里面鼠标的一个移动,那们都是希望能尽量实时,不要延迟,基本都会选择TCP_NODELAY选项关闭nagle算法。
6.TIME_WAIT累计和端口耗尽。
每个socket其实是由
[text]
源ip:源port,目标IP,目标port
[/text]
组成。对于做代理服务器时,因为能变的只有源端口,所以可能造成TIME_WAIT挤压把端口毫无的情况,对后端的访问速度有(65535-1024)/60限制。

当然,假如后面有很多机器,那么因为目标IP不同,实际情况会好些。

发表在 System, Web server | 留下评论

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

上周结果有客户投诉说不能正常下载账单,之前我以为是nginx的proxy_read_timeout设置引起的。因为我之前错误地把这个理解为nginx从后端读取返回的总时间限制。但是后来对这个参数的说明进行了进一步的查阅,发现不是这么个意思。

[text]
Defines a timeout for reading a response from the proxied server. A timeout is only set between two successive read
operations, not for the transmission of the whole response. If a proxied server does not transmit anything within this
time, a connection is closed.
[/text]
然后就只能自己测试下载文件,一切正常。后来是怀疑nginx没有正确返回Content-length:LENGTH或者是Transfer-Encoding:chunked,造成浏览器那边不能把文件下载完。但我在EC2上进行了测试,并tcpdump抓包查看,也没有任何异常,自己在Firefox下用httpfox查看查看也是正确返回了Content-length的(因为我们是把keepalive 关闭了的)。后来单独让同事找客户尝试下载,然后我去把nginx和apache上的日志都找出来了。发现apache上的日志显示返回的大小与该文件真实大小一致的,但是nginx的日志上显示传输的大小却小了很多。

今天晚上回到家后又测试了一下,因为家里的网络非常烂,但是测试了几次都能正常下载。考虑到之前我让同事转告客户那边换个chrome浏览器,但客户那边说无法下载其他浏览器,让我们给QQ传个文件过去,我就猜想对付是不是内部使用代理系统上网的,可能会引发很多这样那样的问题。

只能让对方再试试下载其他文件有没有问题了。如果还有问题肯定是对方内部网络的问题了。

发表在 nginx | 留下评论

用shc加密shell脚本

大家经常有这样的需求,比如脚本里放了一个公共的账户密码,给别人用的时候不希被发现。这个时候可以使用shc对脚本进行加密。可以直接在这个页面下载。

编译后就能使用了,使用的方式比较简单

./shc -r -f script.sh

详细的用法可以help一下看看
[text]
# ./shc -help
shc Version 3.8.7, Generic Script Compiler
shc Copyright (c) 1994-2009 Francisco Rosales <frosal@fi.upm.es>
shc Usage: shc [-e date] [-m addr] [-i iopt] [-x cmnd] [-l lopt] [-rvDTCAh] -f script

-e %s Expiration date in dd/mm/yyyy format [none]
-m %s Message to display upon expiration ["Please contact your provider"]
-f %s File name of the script to compile
-i %s Inline option for the shell interpreter i.e: -e
-x %s eXec command, as a printf format i.e: exec(‘%s’,@ARGV);
-l %s Last shell option i.e: —
-r Relax security. Make a redistributable binary
-v Verbose compilation
-D Switch ON debug exec calls [OFF]
-T Allow binary to be traceable [no]
-C Display license and exit
-A Display abstract and exit
-h Display help and exit

[/text]

发表在 Admin | 留下评论

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

公司有很多windows的服务器,因此就有了windows的跳板机机器。但是因为很多很多蛋疼的问题,造成现在直接A记录搞的经常有问题。我自己其实在测试环境测试过用haproxy做负载均衡的,比较简单。直接贴配置文件吧
[text]
global
log 127.0.0.1 local0
maxconn 40960
pidfile /opt/haproxy/haproxy.pid
nbproc 1
uid 99
gid 99
daemon

defaults
log global
mode tcp
option dontlognull
retries 3
option redispatch
contimeout 5000
clitimeout 50000
srvtimeout 50000
listen stat *:8000
mode http
option httplog
maxconn 1000
stats refresh 60s
stats uri /stats
stats realm GNUer
stats auth admin:admin
frontend window
bind *:3389
default_backend windowsrs
backend windowsrs
mode tcp
balance leastconn
server windows1 10.15.0.51:3389 weight 10 check inter 2000 rise 2 fall 4
server windows2 10.15.1.109:3389 weight 10 check inter 2000 rise 2 fall 4
frontend window_1
bind *:3390
default_backend window_rs1
backend window_rs1
mode tcp
balance leastconn
server windows1 10.15.0.51:3389 weight 10 check inter 2000 rise 2 fall 4
frontend window_2
bind *:3391
default_backend window_rs2
backend window_rs2
mode tcp
balance leastconn
server windows2 10.15.1.109:3389 weight 10 check inter 2000 rise 2 fall 4
[/text]
默认的3389端口后面是有2个机器,调度算法选用最小链接数。
另外新起了2个端口,方便指定端口后能直接访问到指定的机器。

6

发表在 Haproxy | 留下评论

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<stdio.h>
#include <pthread.h>
#include <unistd.h>
#define MAX 40000
void *sleep1k(void){
sleep(1000);
return NULL;
}
int main()
{
int i = 0;
pthread_t thread;

while (1) {
if (pthread_create(&thread, NULL,(void *)sleep1k, NULL) != 0){
break;
}
i ++;
if(i >=MAX) {
break;
}
}
printf("i = %d\n", i);
getchar();
return 0;
}

[/c]

发表在 OS | 留下评论

使用perl mail::sender发送邮件

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

smtp=>’your smtp server host’,
from=>’xxx@xxx.com’,
debug=>’./perllog’
} or die "new sender failed.";

$sender->MailFile(
{
to=>$add,
subject=>"hello",
msg=>"$msg",
debug=>$LOG,
file => $file,
charset => ‘gb2312’
}
) or die "$Mail::Sender::Error\n";
close($LOG);
$sender->Close;
}
[/pl]

发表在 Perl Script | 留下评论

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

这个是去年管理测试环境的时候,直接用脚本批量操作数据库
[pl]
#!/usr/bin/perl -w
use 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=<LIST>){
my ($ip,$name)=split /\s+/,$line;
$sth->execute($ip,$name,$manage,$dev,$location);
print "insert $ip $name\n";
}
$dbh->disconnect();

exit(0);
##############################
sub connect() {
my $source=q{DBI:mysql:database=xxx;host=xx.xx.xx};
my $user="xxx";
my $passwd="xxxx";
my $dbh0=DBI->connect($source,$user,$passwd,{RaiseError=>1}) or die "connect mysql failed.";
$dbh0->do("SET character_set_client = ‘gb2312’");
$dbh0->do("SET character_set_connection = ‘gb2312’");
$dbh0->do("SET character_set_results= ‘gb2312’"); #解决GBK编码问题
return $dbh0;
}
[/pl]

发表在 Perl Script | 留下评论

perl的输出陷阱

以前现用perl为我们的首页系统写个daemon进程来做更新首页的事情,其实简单的说就是前端的同事把新的页眉放到存储里面。我的脚本去定期检测一下md5值,然后取出来。取出来后会对指定的字段进行替换。但是在做的时候就遇到点问题,日志一直不打出来。后来简单测试了一下
[pl]
#!/usr/bin/perl
use strict;
use warnings;
open LOG,">test.out" or die "open file failed\n";
foreach my $k (0..25)
{

print LOG ($k,"\n");
sleep(1);
}
[/pl]
运行这个的时候你会发现只有程序结束了才会写到文件。后来咨询了一下学了这样搞
[pl]
#!/usr/bin/perl
use 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);
}
[/pl]

当然也可以选择这样做
[pl]
#!/usr/bin/perl
use 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);
}
[/pl]

发表在 Perl Script | 留下评论

开始贴老blog

以前在cu上的blog,虽然整体写的比较乱。不过也算是自己以前折腾了好久写的,准备慢慢迁移过来。。

发表在 System | 留下评论

TCP的7个定时器

这两天看了下TCP/IP详解的第二卷,买了很久把第一卷和第三卷大概都看了下,但是第二卷一直没有怎么看过。实际上在第一卷里就经常提到这个定时器的概念,在第二卷里25章是专门介绍定时器的。7种定时器分别是
1.连接建立定时器。在发送每个SYN的时候会启动,如果在75s内没有收到对方的ACK,那么连接终止。
2.保活定时器。在新建socket的时候如果指定了SO_KEEPALIVE才会生效。链接超过设置的时候就会连续发几次探测包,如果对几次尝试都没有收到回复就减低对方已经崩溃。
这个两个定时器的实现:

3.重传定时器。在发送数据的时候,如果指定时间内没有收到对放的回复就会启动重传。如果一次重传后对方还是没有回应,那么这个重转时限每次都会进行调整,越来越长。
4.persist定时器。在连接的对端通告窗口为0 的时候会启动一个定时器,在指定的时间内不像对方发送数据。当定时器超时后才会向对方发送1字节的数据。
5.延迟ACK定时器。当收到对方的确认请求后,这个请求虽然是必须要确认的,但是不是太急,那么就启动定时器200ms,如果在这个定时器超时之前又收到对方的数据,那么就把最新的ACK发回去就行了。这个主要是持续传输数据的时候使用,比如A向B先发出了N-N+1000报文段,这个时候A就期望收到ACK是N+1001,如果A接着继续发了N+1001-N+2001,那么只需要把ACK的序号设置为N+2002就能对前面的2个报文一起做确认了。
6.FIN_WAIT2定时器。A与B是ESTABLISHED的时候,如果A主动发起关闭,调用了close,那么就A会主动对B发送FIN,这个时候A就进入了FIN_WAIT1状态,而B在收到FIN后回复ACK时就变成了CLOSE_WAIT,在A发出FIN和收到ACK的这段时间内A处于FIN_WAIT1,A收到ACK后状态变化为FIN_WAIT2。但是如果B始终不给A发FIN包,岂不是A就一直得在FIN_WAIT2状态?所以有了FIN_WAIT2定时器,linux下默认是60s,其实就是内核参数tcp_fin_timeout 设置的那个参数。当定时器超时后就会把FIN_WAIT2会自动转换到TIME_WIAT状态。
7.TIME_WAIT定时器。这个就是2MSL的时间了,链接主动关闭后等待回收的一段时间,2MSL称为平静时间,就是为了防止如果这个socket被马上重新使用了,之前的那个链接有迟到(后者重传过来的包)到了的话会被新socket直接接收到。linux对这个的定义是写死在头文件的。定义在net/tcp.h #define TCP_TIMEWAIT_LEN (60*HZ) 约为60s

可能每个系统具体的实现和书上的不一样,不过从概念上还是好理解的。

发表在 OS | 留下评论