-
2021-08-13 07:09:51
测试偶尔访问指定网站速度慢的原因
1 现象 某业务在客户服务器上,开发人员反映周期性速度慢,开发人员反馈,由于需要到xxx.com去取数据,慢的原因是取数据慢
直接访问该站点下载文件发下下载速度很快
2 测试脚本如下
3 测试结果发现 是每次第一次访问xxx.com的时候的 建立连接的时间很慢,需要9秒以上
4 最终原因 操作系统启用了TCP ECN,而目的地路由器未使用ECN 导致TCP握手时间延长
netsh interface tcp set global ecncapability=disabled 关闭后正常
5 没有介绍TCP ECN 只是介绍如何发现问题原因
#!/bin/env python
# -*- coding: utf-8-*-
#author: skybug
#date: 2017-12-2
#web_perf_test
import urllib2,sys,pycurl,json,StringIO
import os,subprocess
import platform,_winreg
#ipvip = socket.gethostbyname ("www.xxx.com")#获取DNS解析值 本次未用
reload(sys)
sys.setdefaultencoding('utf-8')
iplist = ["113.x.x.1","113.x.x.x","x.x.x.x","x.x.x.x","x.x.x.x"]#vipgate,vip,cnki,cnki2,chaoxing
urllist=["http://x.com","http://a.com"]
UA = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Mobile Safari/537.36"
headers = {}
def header_function(header_line): #获取响应头,本次未调用
header_line = header_line.decode('iso-8859-1')
if ':' not in header_line:
return
name, value = header_line.split(':', 1)
name = name.strip()
value = value.strip()
name = name.lower()
headers[name] = value
def webperf_keep(url,times=1): #获取访问页面的性能数据
b = StringIO.StringIO() #定义个IO流
pc=pycurl.Curl()#创建pycurl对象
cnt=0
alldata=[]
for i in range(int(times)):
pc.setopt(pycurl.URL,url) #设置访问url
pc.setopt(pycurl.HTTPHEADER, ['Content-Type: application/json'])
pc.setopt (pycurl.USERAGENT,UA)#设置UA
pc.setopt(pycurl.MAXREDIRS,50) #MAX REDIRECT count#设置最大重定向次数
pc.setopt(pycurl.WRITEFUNCTION, b.write)#把相应内容写到流里
pc.setopt( pycurl.FOLLOWLOCATION,1)#跟踪重定向
pc.setopt(pycurl.FORBID_REUSE, 0)#允许复用连接
pc.setopt(pycurl.FRESH_CONNECT,0)
pc.setopt (pycurl.HEADERFUNCTION, header_function)#把头信息写到头函数里
print "testing access {0} {1} times.....".format (url, cnt)
pc.perform()#执行pycurl
cnt+=1
dns_time = pc.getinfo(pycurl.NAMELOOKUP_TIME)#dns解析时间
conn_time = pc.getinfo(pycurl.CONNECT_TIME)#建立连接的时间(TCP握手)
ttfb = pc.getinfo(pycurl.STARTTRANSFER_TIME)#TTFB的时间
total_time = pc.getinfo(pycurl.TOTAL_TIME)#总时间
http_code = pc.getinfo(pycurl.HTTP_CODE)#返回code
http_conn_code= pc.getinfo(pycurl.HTTP_CONNECTCODE)#
redirect_count = pc.getinfo(pycurl.REDIRECT_COUNT)#重定向次数
size_upload = pc.getinfo(pycurl.SIZE_UPLOAD)
size_download = pc.getinfo(pycurl.SIZE_DOWNLOAD)
size_header = pc.getinfo(pycurl.HEADER_SIZE)
size_request = pc.getinfo(pycurl.REQUEST_SIZE)
content_type = pc.getinfo(pycurl.CONTENT_TYPE)
reponse_code = pc.getinfo(pycurl.RESPONSE_CODE)
transfer_time = pc.getinfo(pycurl.PRETRANSFER_TIME) #传输时间
startrans_time= pc.getinfo(pycurl.STARTTRANSFER_TIME)#开始传输时间
speed_download = pc.getinfo(pycurl.SPEED_DOWNLOAD)#下载速度
speed_upload = pc.getinfo(pycurl.SPEED_UPLOAD)#上传速度
redirect_time = pc.getinfo(pycurl.REDIRECT_TIME)#重定向时间
num_conn = pc.getinfo(pycurl.NUM_CONNECTS)#建立连接的次数
last_socket= pc.getinfo(pycurl.LASTSOCKET)#最后一个socker
data = []
perfdata={"dns_time":dns_time,"ttfb":ttfb,"total_time":total_time,"http_code":http_code,"redirect_count":redirect_count
,"size_upload":size_upload,"size_download":size_download,"size_header":size_header,"size_request":size_request
,"content_type":content_type,"reponse_code":reponse_code,"conn_time":conn_time,"transfer_time":transfer_time,"speed_download":speed_download
,"speed_upload":speed_upload,"startrans_time":startrans_time,"redirect_time":redirect_time,"http_conn_code":http_conn_code,"num_conn":num_conn,"last_socket":last_socket}
data.append(url)
data.append(perfdata)
alldata.append(cnt)
alldata.append(data)
#pc.close()
#b.close()
jsondata=json.dumps({"perfdata":alldata},indent=4)
pc.close()
b.close()
return jsondata
def getos():#获取操作系统版本
os = {}
if sys.platform == "win32":
try:
reg_key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion")
if reg_key:
ProductName = _winreg.QueryValueEx(reg_key, "ProductName")[0] or None
EditionId = _winreg.QueryValueEx(reg_key, "EditionId")[0] or None
ReleaseId = _winreg.QueryValueEx(reg_key, "ReleaseId")[0] or None
CurrentBuild = _winreg.QueryValueEx(reg_key, "CurrentBuild")[0] or None
BuildLabEx = _winreg.QueryValueEx(reg_key, "BuildLabEx")[0][:9] or None
os = {"ProductName": ProductName, "EditionId": EditionId, "ReleaseId": ReleaseId,
"CurrentBuild": CurrentBuild, "BuildLabEx": BuildLabEx}
jsondata = json.dumps({"OS": os}, indent=4)
return jsondata
except Exception as e:
print e.message.decode(DEFAULT_LOCALE_ENCODING)
def getcmd(shell):#执行cmd
ps = subprocess.Popen(shell, shell=True, stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
out, err = ps.communicate()
return out.decode('cp936').encode('utf-8')
def writelog(str):
with open("result.txt",'a+') as fr:
fr.write(str)
fr.write("################################")
if len(sys.argv) ==2 and sys.argv[1] == "full":
cnt = 0
print "test setp {0},Collect routing information....".format(cnt)
cmd="route print "
writelog(getcmd(cmd))#获取路由表
print "test setp {0} ,Collect routing information....OK".format(cnt)
cnt = cnt+1
print "test setp {0},Collecting network information....".format(cnt)
cmd="ipconfig /all "#获取网卡配置
writelog(getcmd(cmd))
print "test setp {0},Collecting network information....OK".format(cnt)
cnt = cnt+1
print "test setp{0},Collecting TCP information....".format(cnt)
cmd = "netsh int tcp show global"#获取TCP全局配置
writelog(getcmd(cmd))
print "test setp{0},Collecting TCP information....OK".format(cnt)
cnt=+1
print "test setp{0},Collecting OS information....".format(cnt)
writelog(getos())#获取操作系统版本
print "test setp{0},Collecting OS information....OK".format(cnt)
for index,item in enumerate(iplist):
cmd="tracert "+item#获取路由跟踪
print "test setp {0},Collecting route tracking information....".format(cnt)
writelog(getcmd(cmd))
print "test setp {0},Collecting route tracking information....OK".format(cnt)
cnt=cnt+1
for index,item in enumerate(urllist):
print "test setp {0},Collecting web to access information data....".format(cnt)
writelog(webperf_keep(item))
print "test setp {0},Collecting web to access information data....OK".format(cnt)
cnt=cnt+1
print "All test data collection is completed!"
if len(sys.argv) ==3:
url = sys.argv[1]
times = sys.argv[2]
print "pre test access {0} {1} times.....".format(url,times)
writelog(webperf_keep(url,times))
if len(sys.argv)==1:
print "Please run web.perf.test full to full test \nor run web.perf.test 'http://www.xxx.com/' 10 ro run 10 times access test"
更多相关内容 -
2012关闭ECN
2017-09-07 09:52:00Windows Server 2012 关闭TCP ECN (2014-03-20 18:22:42) 转载 ▼ 标签: it 分类: windows 转载于:https://www.cnblogs.com/gaoyuechen/p/7488200.html转载于:https://www.cnblogs.com/gaoyuechen/p/7488200.html
-
H85-SVP-06-03AX_ECN_M076.zip
2020-11-09 10:19:29windows平台下可视化服务器管理软件,通过简单配置以后即可对服务器进行监控以及管理。此链接为第四部分。 -
H85-SVP-06-03AX_ECN_M076.z01
2020-11-07 11:54:16windows平台下可视化服务器管理软件,通过简单配置以后即可对服务器进行监控以及管理。此链接为第一部分。 -
H85-SVP-06-03AX_ECN_M076.z03
2020-11-07 19:09:58windows平台下可视化服务器管理软件,通过简单配置以后即可对服务器进行监控以及管理。此链接为第三部分。 -
H85-SVP-06-03AX_ECN_M076.z02
2020-11-07 12:57:45windows平台下可视化服务器管理软件,通过简单配置以后即可对服务器进行监控以及管理。此链接为第二部分。 -
显式拥塞通告(ECN)及其在Linux上的实现
2019-09-29 05:22:281 ECN简介 首先看看ECN握手报文的特点,根据RFC3168,ECN握手报文IP头部不能够设置ECT和CE位的 SYN报文TCP标志字段的CWR和ECE位被置1 ...服务器端在接收到有CE标志的报文后,立即构造带有ECE标志的...1 ECN简介
首先看看ECN握手报文的特点,根据RFC3168,ECN握手报文IP头部不能够设置ECT和CE位的
SYN报文TCP标志字段的CWR和ECE位被置1
SYN-ACK报文的CWR位被置0,ECE位被置1
报文在网络上传输的过程中,如果路由器判断自身发生拥塞则在报文的IP首部设置CE标志
服务器端在接收到有CE标志的报文后,立即构造带有ECE标志的ACK报文,服务器端在接收到该ACK报文后进入TCP_CA_CWR状态,在该状态下发送窗口每两个ACK减1。
发生拥塞之前的报文都被确认后,客户端会走出TCP_CA_CWR状态,转入TCP_CA_Open状态,重新开始拥塞避免,并向服务端发送CWR标志,终止服务端向客户端发送ECE报文。
2 ECN在Linux上的实现
以下所有分析基于Linux内核3.16.38
Linux内核通过调整tcp_sock结构体的ecn_flags来标识ECN所处的状态,在文件include/net/tcp.h, line 393内,Linux定义了ECN可能的4种状态,本文将通过这4中状态的转化把ECN从协议栈中肢解出来。
393 #define TCP_ECN_OK 1 //套接字支持ECN协议 394 #define TCP_ECN_QUEUE_CWR 2 //发送端在接收到ECE报文后,设置该标志,并将拥塞状态机设置为TCP_CA_CWR状态 395 #define TCP_ECN_DEMAND_CWR 4 //接收端处于该状态,将在所有ACK报文中添加ECE,直到接收到CWR报文 396 #define TCP_ECN_SEEN 8 //是否接收到过ECT报文
2.1 实现握手
STEP1 :客户端发送SYN,用户态程序调用connect后,内核态通过tcp_connect构造SYN报文,tcp_connect会调用TCP_ECN_send_syn函数,该函数通过系统配置sysctl_tcp_ecn判断是否启用了ECN协议,如果启用了ECN协议,则在SYN报文中添加ECE和CWR标志,并临时设置该套接字为TCP_ECN_OK,这里说临时的原因为在ECN握手失败后,该标志还可能被取消。
3046 /* Build a SYN and send it off. */ 3047 int tcp_connect(struct sock *sk) 3048 { ... 3070 TCP_ECN_send_syn(sk, buff); 3071 ... 3089 }
328 /* Packet ECN state for a SYN. */ 329 static inline void TCP_ECN_send_syn(struct sock *sk, struct sk_buff *skb) 330 { 331 struct tcp_sock *tp = tcp_sk(sk); 332 333 tp->ecn_flags = 0; 334 if (sock_net(sk)->ipv4.sysctl_tcp_ecn == 1) { //通过 /proc/sys/net/ipv4/tcp_ecn进行配置 335 TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_ECE | TCPHDR_CWR; //SYN报文需要添加ECE和CWR 336 tp->ecn_flags = TCP_ECN_OK; //握手阶段ecn_flags设为支持ECN通信,如果握手失败TCP_ECN_OK会被取消 337 } 338 }
STEP2 :服务端处理SYN, tcp_rcv_state_process函数是接收数据时TCP层上的必经之路,它会根据报文类型调用不同函数来处理,所有握手报文都会交给tcp_v4_conn_request,而tcp_v4_conn_request又会调用TCP_ECN_create_request进行ECN-SYN报文,当SYN报文符合ECN-SYN标准时,套接字添加支持ECN标识。
5611 int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
5612 const struct tcphdr *th, unsigned int len)
5613 {
... 5623 switch (sk->sk_state) { 5624 case TCP_CLOSE: 5625 goto discard; 5626 5627 case TCP_LISTEN: 5628 if (th->ack) 5629 return 1; 5630 5631 if (th->rst) 5632 goto discard; 5633 5634 if (th->syn) { 5635 if (th->fin) 5636 goto discard; 5637 if (icsk->icsk_af_ops->conn_request(sk, skb) < 0) //调用tcp_v4_conn_request
5638 return 1; 5639 5657 kfree_skb(skb); 5658 return 0; 5659 } 5660 goto discard; 5661
...5662 case TCP_SYN_SENT: 5663 queued = tcp_rcv_synsent_state_process(sk, skb, th, len);
5672 }
1257 int tcp_v4_conn_request(struct sock *sk, struct sk_buff *skb) 1258 { ... 1326 if (!want_cookie || tmp_opt.tstamp_ok) 1327 TCP_ECN_create_request(req, skb, sock_net(sk)); ...1398 }
737 static inline void 738 TCP_ECN_create_request(struct request_sock *req, const struct sk_buff *skb, 739 struct net *net) 740 { 741 const struct tcphdr *th = tcp_hdr(skb); 742 743 if (net->ipv4.sysctl_tcp_ecn && th->ece && th->cwr && //服务端也配置了ECN,同时SYN报文中函授ECE和CWR 744 INET_ECN_is_not_ect(TCP_SKB_CB(skb)->ip_dsfield)) 745 inet_rsk(req)->ecn_ok = 1; //套接字设置支持ECN标识 746 }
STEP3 :客户端处理SYN-ACK报文,首先调用tcp_rcv_synsent_state_process,继而调用TCP_ECN_rcv_synack来完成ECN握手。
5611 int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, 5612 const struct tcphdr *th, unsigned int len) 5613 { ... 5662 case TCP_SYN_SENT: 5663 queued = tcp_rcv_synsent_state_process(sk, skb, th, len); 5672 }
...5672 }
5384 static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, 5385 const struct tcphdr *th, unsigned int len) 5386 { ... 5446 TCP_ECN_rcv_synack(tp, th); ...5602 }
246 static inline void TCP_ECN_rcv_synack(struct tcp_sock *tp, const struct tcphdr *th) 247 { 248 if ((tp->ecn_flags & TCP_ECN_OK) && (!th->ece || th->cwr)) //SYN-ACK报文含有CWR或不含ECE则握手失败,客户端撤销TCP_ECN_OK 249 tp->ecn_flags &= ~TCP_ECN_OK; 250 }
2.2 客户端发送带有ECT的报文
所有支持ECN通信的流,在传输层 tcp_transmit_skb -> TCP_ECN_send -> INET_ECN_xmit的流程中都会打上ECT(0)标记。
350 static inline void TCP_ECN_send(struct sock *sk, struct sk_buff *skb, 351 int tcp_header_len) 352 { 353 struct tcp_sock *tp = tcp_sk(sk); 354 355 if (tp->ecn_flags & TCP_ECN_OK) { //ECN通信添加ECT 356 /* Not-retransmitted data segment: set ECT and inject CWR. */ 357 if (skb->len != tcp_header_len && 358 !before(TCP_SKB_CB(skb)->seq, tp->snd_nxt)) { 359 INET_ECN_xmit(sk); 360 if (tp->ecn_flags & TCP_ECN_QUEUE_CWR) { 361 tp->ecn_flags &= ~TCP_ECN_QUEUE_CWR; 362 tcp_hdr(skb)->cwr = 1; 363 skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; 364 } 365 } else { 366 /* ACK or retransmitted segment: clear ECT|CE */ 367 INET_ECN_dontxmit(sk); 368 } 369 if (tp->ecn_flags & TCP_ECN_DEMAND_CWR) 370 tcp_hdr(skb)->ece = 1; 371 } 372 }
51 static inline void INET_ECN_xmit(struct sock *sk) 52 { 53 inet_sk(sk)->tos |= INET_ECN_ECT_0; 54 if (inet6_sk(sk) != NULL) 55 inet6_sk(sk)->tclass |= INET_ECN_ECT_0; 56 }
2.3 路由器处理ECT报文
根据设计思路,路由器在认为发生拥塞时,给所有支持ECN协议的流打上CE标记,然而路由器如何判断拥塞发生并没有一个统一的标准,一般来说为平滑后的队列长度超过一定阈值,以RED队列为例,它维护一个队列长度的移动平均值,在该值大于设置的阈值,之后以一定概率给过往的报文打上CE标记(没有启用ECN时为以一定概率丢弃报文)。
59 static int red_enqueue(struct sk_buff *skb, struct Qdisc *sch) 60 { ... 72 switch (red_action(&q->parms, &q->vars, q->vars.qavg)) { //根据平均队列长度决定如何处理报文 73 case RED_DONT_MARK: 74 break; 75 76 case RED_PROB_MARK: //标记报文 77 sch->qstats.overlimits++; 78 if (!red_use_ecn(q) || !INET_ECN_set_ce(skb)) { //没有启用ECN,或者打CE标记失败则丢弃报文 79 q->stats.prob_drop++; 80 goto congestion_drop; 81 } 82 83 q->stats.prob_mark++; 84 break; 85 86 case RED_HARD_MARK: 87 sch->qstats.overlimits++; 88 if (red_use_harddrop(q) || !red_use_ecn(q) || 89 !INET_ECN_set_ce(skb)) { 90 q->stats.forced_drop++; 91 goto congestion_drop; 92 } 93 94 q->stats.forced_mark++; 95 break; 96 } ...
110 }2.4 服务器端处理CE报文
服务器端在收到带有CE标志的IP报文后,将套接字结构体tp->ecn_flags置TCP_ECN_DEMAND_CWR,并进入quick ack模式,之后所有ack报文都置有ECE标志,直到接收端接收到CWR报文后,取消TCP_ECN_DEMAND_CWR。
STEP1 : 转入TCP_ECN_DEMAND_CWR状态。具体流程为tcp_rcv_established -> tcp_event_data_recv -> TCP_ECN_check_ce,在TCP_ECN_check_ce中检查报文是否包含CE标记,在遇到CE标记时转入TCP_ECN_DEMAND_CWR状态。
220 static inline void TCP_ECN_check_ce(struct tcp_sock *tp, const struct sk_buff *skb) 221 { 222 if (!(tp->ecn_flags & TCP_ECN_OK)) 223 return; 224 225 switch (TCP_SKB_CB(skb)->ip_dsfield & INET_ECN_MASK) { 226 case INET_ECN_NOT_ECT: 227 /* Funny extension: if ECT is not set on a segment, 228 * and we already seen ECT on a previous segment, 229 * it is probably a retransmit. 230 */ 231 if (tp->ecn_flags & TCP_ECN_SEEN) 232 tcp_enter_quickack_mode((struct sock *)tp); 233 break; 234 case INET_ECN_CE: 235 if (!(tp->ecn_flags & TCP_ECN_DEMAND_CWR)) { 236 /* Better not delay acks, sender can have a very low cwnd */ 237 tcp_enter_quickack_mode((struct sock *)tp); //进入quick ack模式,立即构造ack报文 238 tp->ecn_flags |= TCP_ECN_DEMAND_CWR; //在ecn_flags中添加TCP_ECN_DEMAND_CWR状态 239 } 240 /* fallinto */ 241 default: 242 tp->ecn_flags |= TCP_ECN_SEEN; 243 } 244 }
STEP2 :构造ECE - ACK。在构造ACK报文时,tcp_transmit_skb 调用 TCP_ECN_send来判断是否处于TCP_ECN_DEMAND_CWR状态,并决定是否在ACK报文中添加ECE标志。
350 static inline void TCP_ECN_send(struct sock *sk, struct sk_buff *skb, 351 int tcp_header_len) 352 { 353 struct tcp_sock *tp = tcp_sk(sk); 354 355 if (tp->ecn_flags & TCP_ECN_OK) { 356 /* Not-retransmitted data segment: set ECT and inject CWR. */ 357 if (skb->len != tcp_header_len && 358 !before(TCP_SKB_CB(skb)->seq, tp->snd_nxt)) { 359 INET_ECN_xmit(sk); 360 if (tp->ecn_flags & TCP_ECN_QUEUE_CWR) { 361 tp->ecn_flags &= ~TCP_ECN_QUEUE_CWR; 362 tcp_hdr(skb)->cwr = 1; 363 skb_shinfo(skb)->gso_type |= SKB_GSO_TCP_ECN; 364 } 365 } else { 366 /* ACK or retransmitted segment: clear ECT|CE */ 367 INET_ECN_dontxmit(sk); 368 } 369 if (tp->ecn_flags & TCP_ECN_DEMAND_CWR) //在CWR状态时,给ACK报文添加ece标志 370 tcp_hdr(skb)->ece = 1; 371 } 372 }
STEP3 :退出TCP_ECN_DEMAND_CWR状态。具体流程为tcp_rcv_established -> tcp_data_queue -> TCP_ECN_accept_cwr,在TCP_ECN_accept_cwr中,判断接收到的报文中是否有CWR标志,并决定是否退出TCP_ECN_DEMAND_CWR状态。
209 static inline void TCP_ECN_accept_cwr(struct tcp_sock *tp, const struct sk_buff *skb) 210 { 211 if (tcp_hdr(skb)->cwr) //退出TCP_ECN_DEMAND_CWR状态 212 tp->ecn_flags &= ~TCP_ECN_DEMAND_CWR; 213 }
2.5 客户端处理ECE报文
客户端首先要根据ACK报文中的ECE调整TCP拥塞状态机到TCP_CA_CWR状态,并开始减小发送窗口,在拥塞发生之前的所有报文都被确认后,恢复TCP_CA_Open状态,并向服务器端发送CWR终止服务器端的TCP_ECN_DEMAND_CWR,结束整个流程。
STEP1 :处理ACK报文,并确定是否包含ECE标志,流程为tcp_rcv_established -> tcp_ack 。
3360 static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) 3361 { ... 3407 if (!(flag & FLAG_SLOWPATH) && after(ack, prior_snd_una)) {
...
3419 } else { 3420 if (ack_seq != TCP_SKB_CB(skb)->end_seq) 3421 flag |= FLAG_DATA; 3422 else 3423 NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPPUREACKS); 3424 3425 flag |= tcp_ack_update_window(sk, skb, ack, ack_seq); 3426 3427 if (TCP_SKB_CB(skb)->sacked) 3428 flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una, 3429 &sack_rtt_us); 3430 3431 if (TCP_ECN_rcv_ecn_echo(tp, tcp_hdr(skb))) //确定ACK报文是否含有ECE标志 3432 flag |= FLAG_ECE; 3433 3434 tcp_ca_event(sk, CA_EVENT_SLOW_ACK); 3435 }
...
3508 }STEP2 : 调整拥塞控制状态机。具体流程为tcp_rcv_established -> tcp_ack -> tcp_fastretrans_alert -> tcp_try_to_open,其中tcp_fastretrans_alert 函数为整个TCP拥塞控制状态机的核心,而tcp_try_to_open函数则根据flag中是否包含FLAG_ECE标志,确定是否将拥塞状态机调整为TCP_CA_CWR状态。拥塞控制状态机进入TCP_CA_CWR状态后,协议栈需要调用tcp_enter_cwr函数来保存当前snd_nxt等重要的变量,之后发送窗口大致为每两个ack减1。
2557 static void tcp_try_to_open(struct sock *sk, int flag, const int prior_unsacked) 2558 { 2559 struct tcp_sock *tp = tcp_sk(sk); 2560 2561 tcp_verify_left_out(tp); 2562 2563 if (!tcp_any_retrans_done(sk)) 2564 tp->retrans_stamp = 0; 2565 2566 if (flag & FLAG_ECE) //将拥塞控制状态机从OPEN或REORDER状态调整为CWR状态 2567 tcp_enter_cwr(sk, 1); 2568 2569 if (inet_csk(sk)->icsk_ca_state != TCP_CA_CWR) { 2570 tcp_try_keep_open(sk); 2571 } else { 2572 tcp_cwnd_reduction(sk, prior_unsacked, 0); 2573 } 2574 }
STEP3 : 退出CWR状态。上面介绍过Linux的拥塞控制状态机主要由tcp_fastretrans_alert 控制,在STEP2中记录的snd_nxt之前的报文都被确认后,拥塞状态机也将退出TCP_CA_CWR状态,并转入TCP_CA_Open状态。
2774 static void tcp_fastretrans_alert(struct sock *sk, const int acked, 2775 const int prior_unsacked, 2776 bool is_dupack, int flag) 2777 { ... 2801 /* D. Check state exit conditions. State can be terminated 2802 * when high_seq is ACKed. */ 2803 if (icsk->icsk_ca_state == TCP_CA_Open) { 2804 WARN_ON(tp->retrans_out != 0); 2805 tp->retrans_stamp = 0; 2806 } else if (!before(tp->snd_una, tp->high_seq)) { 2807 switch (icsk->icsk_ca_state) { 2808 case TCP_CA_CWR: 2809 /* CWR is to be held something *above* high_seq 2810 * is ACKed for CWR bit to reach receiver. */ 2811 if (tp->snd_una != tp->high_seq) { //退出TCP_CA_Cwr 2812 tcp_end_cwnd_reduction(sk); 2813 tcp_set_ca_state(sk, TCP_CA_Open); 2814 } 2815 break; 2816 ...
2824 } 2825 }
...
2886 }转载于:https://www.cnblogs.com/codingMozart/p/6389165.html
-
【RDMA】低时延网络实践---百度高级项目|PFC+ECN
2021-08-04 06:15:52PFC 和 ECN PFC是一种基于队列的反压协议,在单机场景下,PFC可以快速、有效的调节服务器速率来保证网络不丢包,但是在多级网络中,就会出现不公平降速、PFC风暴、死锁等问题,而且当有异常服务器向网络中注入PFC...原文:https://www.sohu.com/a/190664909_210640
报告人:高峰
低时延网络的实践:
第一是低时延网络解决方案,会介绍百度在低时延网络解决方案设计过程中如何思考的
第二是低时延网络技术展望,会介绍低时延网络技术研究方向
第三是总结。
以前数据中心:
追求大带宽,无阻塞 。
现在数据中心:
追求低时延、无丢包。
网络时延组成5部分:光电传输时延、数据串行时延、设备转发时延、重新排队时延、主机处理时延。
光电传播时延:是固定值,没办法改变
数据串行时延和设备转发时延:主要是取决于芯片技术的发展
我们聚焦的重点是重新排队时延和主机处理时延,通过主机端加速技术,可以减小主机处理时延,我们选择的方向是RDMA和RoCE,主要考虑成本和技术成熟度,另外随着100G技术的成熟,RoCE的优势越来越明显,网络侧我们选择的方向是DCB和ECN,通过流控技术,避免网络拥塞造成的业务丢包。
主机端的加速,我们是RDMA和RoCE,RDMA性能方面有两个方面,RDMA的性能优势主要体现在以下几个方面:
1.Zerocopy:减少数据拷贝次数,由于没有将数据拷贝到内核态,传输延迟会显著提高,
2、Kernelbypass&Protocoloffload:不需要内核参与,数据通路中没有繁琐的处理报头逻辑,不仅会使延迟降低,而且也大大节省了CPU的资源。
RDMA和TCP相比,性能提升比较明显,但是数据包大小,以及业务模型不同情况下,提升的效果也不同。我们在语音识别训练提速2倍,在机器翻译训练提速15倍。
RoCE是RDMA承载协议,RoCE和Infiniband的性能基本相近,而且比iWARP产业生态更加健全,主流网卡厂商都已支持。除此之外,RoCE网络在数据链路层支持标准以太网协议,在网络层上支持IP协议,因此可以无缝融合到现有的数据中心网络中,部署和运维更加方便,而且设备成本更低。
以太网为何容易丢包
以太网采用的是尽力而为的转发方式,每个网络设备都会尽力的把数据转发给下游的设备。当下游设备处理能力不足的时候,网络就会出现拥塞或者丢包,所以网络本身是不可靠的,无论是TCP或者RDMA协议,网络拥塞和丢包重传都会让业务性能受到影响,尤其是RDMA协议对网络丢包的容忍度更低。如何减少或者避免网络拥塞和丢包,现在通用的解决方案是PFC和ECN的流控技术。
PFC 和 ECN
PFC是一种基于队列的反压协议,在单机场景下,PFC可以快速、有效的调节服务器速率来保证网络不丢包,但是在多级网络中,就会出现不公平降速、PFC风暴、死锁等问题,而且当有异常服务器向网络中注入PFC报文时,还可能造成整个网络瘫痪,因此,在数据中心开启PFC,需要通过对Pause帧进行严格的监控、管理,以保证网络的可靠性。
ECN是一种基于流的端到端流控技术,效果上会优于PFC,但是也不是很理想,主要有几个问题:
1、ECN缺点是需要网卡侧生成反压报文,反馈路径周期比较长。
2、随机性标记,会不公平。
3、水线设计比较复杂,这也是现阶段ECN方案的最大挑战,因为水线不是一个固定值,要结合网络架构和业务特点来设计。
4、目前各个网卡厂商拥塞算法不一致。虽然方案不理想,但是目前也没有更好的选择。
从解决方案设计上面来说,ECN和PFC组合配置,针对PFC固有的缺陷问题,可以通过优先触发ECN报文,用来减少网络中PFC的数量,在PFC生效前完成流量的降速。
避免触发流控机制+加速比
(PFC流控,但是拥塞发生后触发的。那要在拥塞之前就接入,避免发生拥塞:ECN)
依靠有效流控机制只能是减少网络拥塞和丢包的发生,网络是共享资源,面对多个业务并发流量导致拥塞的问题,是很难避免的。高效的网络一定是避免触发流控机制,那么在组网架构方面也要同步思考这个问题,比较有效的办法是用带宽来换时间,为服务器提供端到端的线速转发能力。下面介绍一下网络架构设计过程中要关注什么,在低时延网络架构设计中最关键的指标是加速比,加速比越大,网络拥塞越少,时延越低。目前我们的网络架构设计是1:1加速比,下一代新架构会提升加速比到4:3以上,主要来避免fabric内部拥塞和丢包问题,加速比提升会让网络性能提升,新架构在性能提升的同时,也要付出更高的组网成本。
(加速比:http://blog.chinaunix.net/uid-26893610-id-3769239.html ?、http://net.zhiding.cn/network_security_zone/2008/0925/1152705.shtml ?)
下面分享一下在整个设计过程中的思考。在整个低时延网络解决方案中有两个选择:
第一个是单独部署PFC,
第二个就是PFC和ECN的结合。
结果很明显:
1、ECN+PFC的方式优于单独部署PFC.
2、加速比是很关键的指标,决定了我们的效率,加速比越高,网络优势就越明显。
3、就是水线的设计,PFC的水线越大,ECN的水线要适合网络模型。
下面分享一下我们在方案设计过程中的一些分析,有两种技术方案选择,
第一种是单独部署PFC
第二种是PFC+ECN组合
我们分别在加速比1:1和加速比4:3环境下,以及在不同的带宽利用率下面测试,分别是50%、75%、100%利用率。
结果很明显:
1、ECN+PFC优于单独部署PFC,而且在各种利用率情况下均有优势。
2、加速比是关键指标,加速比决定网络效率,越高,优势越明显。
3、水线设计一定要合理,PFC水线的设置只要满足HEADROOM,越大越好,ECN水线的设置需要视不同流量模型而定。
PFC+ECN VS 新方案
这个分享是PFC+ECN和新方案的对比,新方案是我们在探索的一个方向,就是在tor下行端口单独部署ECN,这个方案需要两个前提条件,ECN控制环不失效,fabirc内部不能丢包,提高加速比来解决fabric内部丢包问题,从结果上看会优于PFC+ECN的方案,但是如果fabric内部无法保证不丢包,在仅部署ECN时,丢包率非常高,100%利用率时,丢包率高达5%以上,影响会非常严重,稳妥一些还是PFC+ECN的组合方案比较好。提高加速比可以缓解Fabric内部端口的拥塞,仍然存在流量不均导致丢包的可能,也要配合一种理想的负载均衡方案。
对未来的技术展望
以上是百度在低时延网络解决方案上面的思考,下面是我们对未来的技术展望。我们
希望从四个方面进行深度的优化,控制面、数据面、管理面、功能强化。
控制面-优化反馈机制,目前拥塞反馈信息比较单一,反馈内容很少,由于是网卡做拥塞通知,反馈路径周期太长,控制面数据未高优保障。需要优化通知消息,引入更多级别的拥塞通知机制,包括拥塞程度等信息,通过多种方式提速,比如交换机设备直接反馈拥塞通知,缩短反馈路径,确保控制面消息在网络传递过程中不被丢弃,同时由交换机来触发丢包重传。
数据面-多路径负载均衡,当前多路径下多采用基于流的哈希算法,实现数据在不同链路上调度,大象流叠加容易造成流量不均,在特定路径的拥塞。如前面解决方案中介绍,fabric内部的负载均衡很重要,需要从负载均衡算法方面进行优化,例如:基于成员接口历史负载情况,选择空闲链路。把出接口队列长度作为流量均衡的hash因子。切割大象流,把一条流切分为多组,调度到不同路径,且保证不乱序。从这三个方面协作处理,实现完美的负载均衡调度
管理面-自适应网络。低时延网络对运维的管理自动化提出更多的要求,相对于低时延网络在丢包、性能方面提出更高的要求,网络运维管理要屏蔽网络环境变化对性能的影响,确保配置永远是最优的。要达到自适应的网络效果,我们认为应该建立分析。第一点是业务的探索和发现,我们要构建自己业务测量的能力,把业务沿途网络节点转发信息进行记录和提取,第二点是计算和特征分析,根据现网实时数据和业务特征,计算出最优的水线阈值和最优策略。第三点是下发和持续的优化。根据业务流量特点,自动配置并动态调整参数,自动下发给服务器和网络设备,实现自适应网络配置。
功能强化-队列优化,数据中心内流量特征有两种,大象流和老鼠流,大象流对时延不敏感,丢包对整体性能影响较小,但是占据了80%的流量,网络拥塞期间,很容易把交换机的队列占满,时延敏感的业务流量被饿死,需要从交换机队列层面优化,将大象流隔离到单独的队列中,为老鼠流预留足够的buffer,以及单独的队列设计,实现设备层面的低时延转发。
以上技术分享就结束了。在低时延网络里面业界也关注很多,也有很多相应的技术,由于时间关系就分享这么多。总结一下今天我的分享。共4个部分,第一部分是业务定位,低时延网络在百度来说主要是面向百度云和人工智能的内生需求,我们分别部署了25G、40G、100G的低时延网络,用来支撑业务需求。从网络定位上面,我们配合整体的网络布局,实现局部的加速的能力。第三点是产品定位,目前低时延网络中仍然有很多问题和挑战,技术的优化空间还很大,在未来也希望跟厂商共同的去探索。第四点是架构演进定位,向大规模网络架构探索,随着技术发展,逐步优化迭代。
大象流和老鼠流
主要是通过流的大小和速率区分。
大象流:大速率,长时的流就是elephant flow,如:虚机的迁移,数据的迁移,MapReduce
老鼠流:小速率,短时的就是mouse flow,如:发邮件,看网页,聊微信
多说一点,因为 per flow的哈希肯定是不精确的,所以elephant flow会影响到mouse flow。虽然 per packet能解决问题,但使用同时也有乱序的可能。所以,可以通过使用segment routing的flowlets来彻底解决这个问题。
链接:https://www.zhihu.com/question/50171430/answer/470878604https://www.zhihu.com/question/50171430
自适应地隔离大象流和老鼠流在不同的路径上传输:https://blog.csdn.net/qq_36028921/article/details/85012707
@UESTC
-
深信服防火墙问题排查系列-ECN
2021-07-02 11:04:45在部署服务测试的时候,内网服务器访问dmz服务器telnet很慢。经过多台测试发现有的访问快,有的访问慢,然后尝试在dmz防火墙加白名单后,都很快。对比和分析数据包后得出结论: 访问该目的服务的25邮件服务的端口,... -
【RDMA】无损网络和PFC(基于优先级的流量控制)|ECN
2021-03-31 11:42:52目录 前言 一、为什么会产生拥塞 二、PFC如何实现流控 三、PFC存在的问题 四、利用ECN实现端到端的拥塞控制 五、ECN交互过程 QC-QCN (网络工程师必会) (待续) PFC和ECN对比* 总结 PFC的水线设置 无损网络测试... -
在WIN2012R2和WIN2016服务器打开网站慢的解决方法
2021-08-02 08:08:58在WIN2012R2和WIN2016服务器中访问网站或网页需要等待加载N秒才显示解决步骤:1、执行netsh int tcp show global查看默认TCP全局参数等相关设置“ECN功能”后面的参数是否为disabled如果不是,请关闭ECN:netsh int ... -
ECN与ECO工程变更操作
2018-07-04 19:20:10丽姐的方法:首先将脚本copy到建模器运行的workspace的default下面,然后建模器重新加载模型,最后重新部署到服务器。 然后在服务器的conf下面的脚本中也加上对应的脚本。 综上,我觉得只需要在我的代码运行的服务器... -
syslog搭建服务器登录日志系统
2021-05-12 00:47:35一、syslog服务器配置首先,配置Red Hat上自带的syslog服务,使它能够记录来自其他服务器的log信息。1、编辑/etc/sysconfig/syslog文件,修改SYSLOGD_OPTIONS选项,如下:# Options to syslogd# -m 0 disables 'MARK... -
【网络】H3C交换机(S6820)配置ECN例子
2021-08-04 06:14:16·在交换机连接服务器的接口上使能DCBX,以便交换机和服务器网卡协商ETS和PFC参数。 ·配置 ETS 以保证设备 A 的 25-5GigE 1/0/3 和设备 B 的 25-5GigE 1/0/2 上的 802.1p 优先级为 5 的数据包的带宽。 ·在Device ... -
如何查看线上服务器日志
2019-03-10 15:46:09Xshell可以在Windows界面下用来访问远端不同系统下的服务器,从而比较好的达到远程控制终端的目的。打开软件之后,点击左上角文件里的新建。 填上服务所部署的那台服务器的地址和端口号 ... -
外汇MetaTrader 4 常见交易商服务器ip地址汇总
2021-08-07 06:04:25转自http://blog.2forex.cn/sy_binbin/2569.html常见交易商服务器ip地址汇总:诺德外汇nordfxNordGroupInv-Demo78.140.130.82:443NordGroupInv-Real178.140.165.83:443NordGroupInv-Real278.140.166.246:443... -
常见外汇交易商服务器ip地址汇总【转载】
2021-07-31 04:29:41常见交易商服务器ip地址汇总:诺德外汇nordfxNordGroupInv-Demo78.140.130.82:443NordGroupInv-Real178.140.165.83:443NordGroupInv-Real278.140.166.246:443NordGroupInv-Real378.140.166.244:443NordGroupInv-Real... -
网络设备丢弃ECN包导致的慢响应问题
2017-08-12 11:11:18慢响应,ECN -
在10Gbps网络下开启Huawei CE6850交换机的ECN功能并实验DCTCP协议
2017-07-12 16:52:02假设iperf服务端为主机tian04,使用TCP协议,那么在控制台执行以下两个命令开启服务器进程。 iperf-s -p 12000 -i1 iperf-s -p 12001 -i1 假设iperf客户端为主机tian05,tian06。注意,与1G的实验环境使用的命令不同... -
服务器访问API超时报错:connection timed out 连接超时
2021-08-08 08:56:51如果您的服务器是Windows Server2012版本(其他 Windows Server版本也可尝试) 请在服务器上执行以下命令:netsh interface tcp show global 检查[ECN 功能]是否为“enable”,如果为“enable”请执行命令: netsh int ... -
服务器TCP优化1
2019-11-09 23:58:27ECN 功能:netsh int tcp set global ecncapability=disabled RFC 1323 时间戳:netsh int tcp set global timestamps=enabled 再加一条优化网卡缓存配合使用 网上流传的 在“运行”对话框中输入“MSINFO32.... -
优化Linux服务器方案:解决TIME_WAIT过多造成的问题
2021-05-14 22:30:301、 time_wait的作用:TIME_WAIT状态存在的理由:1)可靠地实现TCP全双工连接的终止在进行关闭连接四次挥手协议时,最后的ACK是由主动关闭端发出的,如果这个最终的ACK丢失,服务器将重发最终的FIN,因此客户端必须... -
在Linux上搭建日志服务器过程
2021-05-11 15:42:313 配置windows服务器,使其发送日志到日志服务器。需要安装一个第三方软件,名字叫eventsys,用于转发日志到linux系统的syslog日志服务程序。下载地址为:... -
【每日鲜蘑】Linux 服务器配置调优
2021-05-15 15:11:38net.ipv4.tcp_ecn = 0 #把TCP的直接拥塞通告(tcp_ecn)关掉 net.ipv4.tcp_sack = 1 #关闭tcp_sack #启用有选择的应答(Selective Acknowledgment), #这可以通过有选择地应答乱序接收到的报文来提高性能(这样可以... -
基于MM、STP、ECN、MTF的外汇平台模式深度分析
2017-03-26 18:37:23外汇经纪商平台的分类: * MM,做市商模式,全称Market Maker;... * ECN,电子自动撮合成交模式,全称Electronic Communications Network; * MTF,多边交易设备模式,全称Multilateral Trading F