1、概述linux
咱们以前介绍过rtt、ssthresh等变量,这些变量通常在TCP链接创建的时候有个初始值,而后随着TCP的数据交互逐渐调整到适应对应的网络状态的值。可是若是每次TCP创建链接都依靠默认初始值逐渐调整,那么可能须要一段时间才能调整到合适值,这显然会下降TCP性能,对于这种场景一种优化方案就是destination metrics。git
RFC2140中描述,若是新创建的链接从已经关闭的链接缓存的状态信息中获取初始化信息,称呼为temporal sharing,若是新创建的链接从其余已创建的TCP链接获取初始化信息称为ensemble sharing。linux中实现的是temporal sharing。RFC3124中提出一个Congestion Manager,congestion manager是一个操做系统服务,TCP链接能够从这个服务中获取链接相关信息。一样linux也是使用destination metrics来实现的Congestion Manager。
web
2、Linux实现简介缓存
destination metrics是指TCP根据用户预设的一些值或者以前TCP链接缓存的一些值来初始化相关的状态变量。也就是说destination metric其实能够分为两部分,一部分是用户预设的值,另一部分则是以前TCP链接缓存缓存的值,后面这一部分也称为TCP metrics。显然一个TCP链接的网络状态(如RTT时延、拥塞窗口cwnd)只与目的IP地址强相关,而与传输层的端口并没有太大关系。TCP metric就是以IP地址来缓存的,每一个IP地址对应一个缓存条目。通常来讲,当TCP链接创建的时候,若是要初始化一个对应的状态变量,首先会查询TCP metrics缓存中是否存在目标地址的metric,若是存在则根据metric信息来初始化链接的参数,若是不存在则会在TCP metrics缓存中建立对应这个IP地址的TCP metric,建立的时候还会根据destination metrics的设置来初始化tcp metrics。TCP链接在关闭的时候也会尝试把最新的链接状态信息写入到TCP metrics缓存中。网络
上面说了这么多废话,那么linux中到底有那些置destination metrics、tcp metrics呢?首先咱们说一下destination metrics包含那些状态信息,与TCP关系比较大的几个有mtu、 window、 rtt、 rttvar、 rto_min、 ssthresh、 cwnd、 initcwnd、 initrwnd、 quickack、 reordering、congctl、 advmss,其中标识为红色的5个即为TCP metrics。咱们在以前的文章中已经演示过mtu、 ssthresh、 initcwnd、 quickack、 congctl、window等destination metrics的设置和影响。这些参数的详细解释请查阅man ip-route和man ip-tcp_metrics。其中须要注意的cwnd这个metric,这个值表示TCP链接拥塞窗口cwnd的上限,而不是拥塞窗口的初始值,metric中的cwnd更名为cwnd_clamp,显然更合适一些,另外就是这个metric的设置须要加lock关键字才生效。另外内核目前并不会根据rttvar来初始化状态变量了。tcp
下面咱们说一下设置destination metrics时候,加不加lock的关系,其中rtt、rttvar、ssthresh、cwnd、reordering这5个TCP metrics能够在设置的时候添加lock关键字,TCP链接在初始创建时候若是没有对应目标IP地址的TCP metric,则会根据设置值来初始化对应这条IP 地址的TCP metric,若是添加了lock关键字,那么随后这个TCP链接关闭的时候就不会更新对应的metric,若是没有添加lock字段,而且tcp_no_metrics_save参数为0,那么就会根据当前状态来更新TCP metric缓存。性能
最后说一下,使用ip route设置的destination metrics并非当即生效的,上面咱们说了TCP链接创建的时候会先从TCP metrics缓存中初始化链接相关的状态信息,若是没有TCP metric缓存才会从ip route设置中读取参数配置来创建TCP metrics。也就是说destination metrics为TCP metrics提供了初始值,一旦缓存中的TCP metric有效,就不会ip route设置的destination metric来初始化TCP链接了。那么TCP metric何时会失效呢?若是读取TCP metric缓存的时候发现距离上次更新这条IP地址的metric时间超过一小时,那么这个TCP metric就无效了,就须要从destination metric来从新初始化TCP metric。测试
3、示例优化
下面咱们经过示例来看一下destination metric和tcp metric的常见操做及相关特性
ui
#经过ip route add命令能够添加路由,而后针对这条路由的目标地址设置destination metrics****@Inspiron:****/04_cc/tcp17# ip route add local 127.0.0.2 dev lo congctl reno initcwnd 5 ssthresh lock 4 #设置后查看一下相关信息****@Inspiron:****/04_cc/tcp17# ip route show table all | grep 127.0.0.2; ss -i sport = 9877;ip tcp_metrics show 127.0.0.2#下面一行对应 ip route show table all | grep 127.0.0.2 该命令能够查看destination metrics设置local 127.0.0.2 dev lo table local scope host ssthresh lock 50 cwnd lock 9 initcwnd 5 congctl reno#下面一行对应命令 ss -i sport = 9877 查看源端口为9877 的tcp链接信息Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port #下面一行对应 ip tcp_metrics show 127.0.0.2 该命令能够查看tcp metrics设置,此时咱们虽然设置了destination metric,可是由于#尚未创建到127.0.0.2的tcp链接,于是尚未tcp metric信息RTNETLINK answers: No such process#启动server server端在与client创建链接后会休眠30ms 而后连续发送15个数据包 每一个数据包的大小为50bytes,发送间隔为3ms#数据包发送完后休眠30s,而后关闭与client的链接****@Inspiron:****/04_cc/tcp17# ./server.out &[1] 24091#client创建与server端的tcp链接,client对于每一个收到的数据包都会回复一个ACK确认包****@Inspiron:****/04_cc/tcp17# ./client.out > rst_client &[2] 24093****@Inspiron:****/04_cc/tcp17# conn setup sleep 30s#上面一行是server端打印,提示已经与client创建链接,开始休眠30s,此时咱们再次查看信息****@Inspiron:****/04_cc/tcp17# ip route show table all | grep 127.0.0.2; ss -i sport = 9877;ip tcp_metrics show 127.0.0.2#能够看到路由表中的destination metric的设置值是静态不变的local 127.0.0.2 dev lo table local scope host ssthresh lock 50 cwnd lock 9 initcwnd 5 congctl reno#如今能够查看到server端与client端的链接了 注意路由表中设置了ssthresh lock 50,可是下面的链接信息告诉咱们server端ssthresh=9,缘由就是路由表中设置了cwnd lock 9,cwnd这个metric生效的时候,TCP链接会设置ssthresh=min(ssthresh,cwnd)=9Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port tcp ESTAB 0 0 127.0.0.1:9877 127.0.0.2:webmin reno wscale:0,7 rto:252 rtt:50.258/25.129 mss:50 cwnd:5 ssthresh:9 segs_in:2 send 39.8Kbps lastsnd:6064456 lastack:6064456 pacing_rate 47.8Kbps rcv_space:43690#能够看到在新创建TCP链接后,就会初始化一个tcp metric,初始值来自路由表中静态的destination metric 127.0.0.2 age 4.760sec ssthresh 50 cwnd 9 source 127.0.0.1****@Inspiron:****/04_cc/tcp17# ****@Inspiron:****/04_cc/tcp17# server send startserver send end sleep 30s#server端发送数据后再次查看相关信息****@Inspiron:****/04_cc/tcp17# ip route show table all | grep 127.0.0.2; ss -i sport = 9877;ip tcp_metrics show 127.0.0.2local 127.0.0.2 dev lo table local scope host ssthresh lock 50 cwnd lock 9 initcwnd 5 congctl reno#因为路由表中设置cwnd lock 9,限制了拥塞窗口最大值只能到9,注意路由表中的cwnd限制的是拥塞窗口的最大值,从下面链接信息能够看到cwnd=9Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port tcp ESTAB 0 0 127.0.0.1:9877 127.0.0.2:webmin reno wscale:0,7 rto:252 rtt:50.271/0.392 mss:50 cwnd:9 ssthresh:9 bytes_acked:750 segs_out:15 segs_in:17 send 71.6Kbps lastsnd:6094556 lastack:6094604 pacing_rate 85.9Kbps rcv_space:43690#从下面的age能够看到server端并无更新tcp metrics127.0.0.2 age 39.528sec ssthresh 50 cwnd 9 source 127.0.0.1****@Inspiron:****/04_cc/tcp17# server sockfd close[2]+ 已完成 ./client.out > rst_client#server端链接关闭,再次查看相关信息****@Inspiron:****/04_cc/tcp17# ip route show table all | grep 127.0.0.2; ss -i sport = 9877;ip tcp_metrics show 127.0.0.2local 127.0.0.2 dev lo table local scope host ssthresh lock 50 cwnd lock 9 initcwnd 5 congctl reno#已经查不到源端口为9877的tcp链接Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port #能够从age信息里面看到tcp 链接关闭的时候更新了tcp metric,可是能够看到ssthresh和cwnd的值并无更新这个就是lock的做用,#被lock的tcp metric是不会被tcp链接更新的 另外能够看到rtt和rttvar这两个metric发生了更新,可是reordering并无更新,也就是说TCP#链接关闭的时候还要检测当前状态的有效性来决定是否更新相关的metric。127.0.0.2 age 6.116sec ssthresh 50 cwnd 9 rtt 50281us rttvar 50281us source 127.0.0.1#接下来咱们更新路由表destination metric的cwnd和ssthresh两个metrics****@Inspiron:****/04_cc/tcp17# ip route change local 127.0.0.2 dev lo initcwnd 5 cwnd lock 8 ssthresh lock 40 congctl reno****@Inspiron:****/04_cc/tcp17# ./client.out > rst_client &[2] 24190****@Inspiron:****/04_cc/tcp17# conn setup sleep 30s#从新进行测试 链接创建****@Inspiron:****/04_cc/tcp17# ip route show table all | grep 127.0.0.2; ss -i sport = 9877;ip tcp_metrics show 127.0.0.2local 127.0.0.2 dev lo table local scope host ssthresh lock 40 cwnd lock 8 initcwnd 5 congctl reno#从下面的链接信息中的ssthresh值能够看到,路由表中的设置并无生效,链接创建的时候是从tcp metrics中读取的链接缓存信息Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port tcp ESTAB 0 0 127.0.0.1:9877 127.0.0.2:webmin reno wscale:0,7 rto:248 rtt:50.225/25.112 mss:50 cwnd:5 ssthresh:9 segs_in:2 send 39.8Kbps lastsnd:6393360 lastack:6393360 pacing_rate 47.8Kbps rcv_space:43690#能够看到此时的tcp metric中cwnd和ssthresh并无从destination中更新,缘由就是上面说的#只有TCP metrics过时后或者初始创建时候才会从destination metric更新 127.0.0.2 age 273.460sec ssthresh 50 cwnd 9 rtt 50281us rttvar 50281us source 127.0.0.1****@Inspiron:****/04_cc/tcp17# server send startserver send end sleep 30s****@Inspiron:****/04_cc/tcp17# ip route show table all | grep 127.0.0.2; ss -i sport = 9877;ip tcp_metrics show 127.0.0.2local 127.0.0.2 dev lo table local scope host ssthresh lock 40 cwnd lock 8 initcwnd 5 congctl renoNetid State Recv-Q Send-Q Local Address:Port Peer Address:Port tcp ESTAB 0 0 127.0.0.1:9877 127.0.0.2:webmin reno wscale:0,7 rto:252 rtt:50.306/0.377 mss:50 cwnd:9 ssthresh:9 bytes_acked:750 segs_out:15 segs_in:17 send 71.6Kbps lastsnd:6423460 lastack:6423508 pacing_rate 85.9Kbps rcv_space:43690127.0.0.2 age 303.164sec ssthresh 50 cwnd 9 rtt 50281us rttvar 50281us source 127.0.0.1****@Inspiron:****/04_cc/tcp17# server sockfd close[2]+ 已完成 ./client.out > rst_client****@Inspiron:****/04_cc/tcp17# ip route show table all | grep 127.0.0.2; ss -i sport = 9877;ip tcp_metrics show 127.0.0.2local 127.0.0.2 dev lo table local scope host ssthresh lock 40 cwnd lock 8 initcwnd 5 congctl renoNetid State Recv-Q Send-Q Local Address:Port Peer Address:Port #链接关闭后 再次更新了tcp metrics 这一点能够从age看到 也能够从rtt、rttvar中看到 127.0.0.2 age 4.800sec ssthresh 50 cwnd 9 rtt 50312us rttvar 37785us source 127.0.0.1#接下来咱们看一下不填加lock关键字的效果****@Inspiron:****/04_cc/tcp17# ip route change local 127.0.0.2 dev lo initcwnd 5 cwnd 8 ssthresh 40 congctl reno****@Inspiron:****/04_cc/tcp17# ip route show table all | grep 127.0.0.2; ss -i sport = 9877;ip tcp_metrics show 127.0.0.2local 127.0.0.2 dev lo table local scope host ssthresh 40 cwnd 8 initcwnd 5 congctl renoNetid State Recv-Q Send-Q Local Address:Port Peer Address:Port 127.0.0.2 age 91.740sec ssthresh 50 cwnd 9 rtt 50312us rttvar 37785us source 127.0.0.1#为了让路由表中的destination metric生效,须要使用下面的命令从tcp metrics中删除127.0.0.2对应的缓存信息****@Inspiron:****/04_cc/tcp17# ip tcp_metrics flush 127.0.0.2****@Inspiron:****/04_cc/tcp17# ip route show table all | grep 127.0.0.2; ss -i sport = 9877;ip tcp_metrics show 127.0.0.2local 127.0.0.2 dev lo table local scope host ssthresh 40 cwnd 8 initcwnd 5 congctl renoNetid State Recv-Q Send-Q Local Address:Port Peer Address:Port #能够看到TCP metrics中已经没有对应127.0.0.2的缓存了RTNETLINK answers: No such process****@Inspiron:****/04_cc/tcp17# ./client.out > rst_client &[2] 24265****@Inspiron:****/04_cc/tcp17# conn setup sleep 30s#client再次创建链接 进行新的测试****@Inspiron:****/04_cc/tcp17# ip route show table all | grep 127.0.0.2; ss -i sport = 9877;ip tcp_metrics show 127.0.0.2local 127.0.0.2 dev lo table local scope host ssthresh 40 cwnd 8 initcwnd 5 congctl reno#从下面的链接信息中能够看到,cwnd虽然设置了 可是并无限制到server端的拥塞窗口,由于若是cwnd这个metric设置生效的话,那么ssthresh应为8Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port tcp ESTAB 0 0 127.0.0.1:9877 127.0.0.2:webmin reno wscale:0,7 rto:252 rtt:50.243/25.121 mss:50 cwnd:5 ssthresh:40 segs_in:2 send 39.8Kbps lastsnd:6574128 lastack:6574128 pacing_rate 79.6Kbps rcv_space:43690#链接创建后建立新的tcp metric,能够看到其中的初始值来自与路由表中destination metrics设置127.0.0.2 age 2.980sec ssthresh 40 cwnd 8 source 127.0.0.1****@Inspiron:****/04_cc/tcp17# server send startserver send end sleep 30s****@Inspiron:****/04_cc/tcp17# ip route show table all | grep 127.0.0.2; ss -i sport = 9877;ip tcp_metrics show 127.0.0.2local 127.0.0.2 dev lo table local scope host ssthresh 40 cwnd 8 initcwnd 5 congctl reno#server端发送数据后,能够看到cwnd已经增加到了20,超越了路由表中cwnd的设置。说明没有添加lock关键字的cwnd metric并无生效Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port tcp ESTAB 0 0 127.0.0.1:9877 127.0.0.2:webmin reno wscale:0,7 rto:252 rtt:50.273/0.372 mss:50 cwnd:20 ssthresh:40 bytes_acked:750 segs_out:15 segs_in:17 send 159.1Kbps lastsnd:6604192 lastack:6604244 pacing_rate 191.0Kbps rcv_space:43690127.0.0.2 age 34.248sec ssthresh 40 cwnd 8 source 127.0.0.1****@Inspiron:****/04_cc/tcp17# server sockfd close[2]+ 已完成 ./client.out > rst_client****@Inspiron:****/04_cc/tcp17# ip route show table all | grep 127.0.0.2; ss -i sport = 9877;ip tcp_metrics show 127.0.0.2local 127.0.0.2 dev lo table local scope host ssthresh 40 cwnd 8 initcwnd 5 congctl renoNetid State Recv-Q Send-Q Local Address:Port Peer Address:Port #由于路由表中的cwnd metric没有lock,所以链接关闭的时候会更新cwnd,实际上更新了也没用,由于新的tcp链接并不会使用127.0.0.2 age 3.064sec ssthresh 40 cwnd 24 rtt 50275us rttvar 50275us source 127.0.0.1#接着进行新的测试,下面命令指示client丢掉第8个数据包 形成server端快速重传,从而更新ssthresh****@Inspiron:****/04_cc/tcp17# ./client.out 8 >rst_client &[2] 24606****@Inspiron:****/04_cc/tcp17# conn setup sleep 30s****@Inspiron:****/04_cc/tcp17# ip route show table all | grep 127.0.0.2; ss -i sport = 9877;ip tcp_metrics show 127.0.0.2local 127.0.0.2 dev lo table local scope host ssthresh 40 cwnd 8 initcwnd 5 congctl reno#新创建链接的拥塞窗口cwnd始终从路由表中的initcwnd初始化 而不是从tcp metric或者destination metric中的cwnd metric更新,#再说一次 cwnd这个metric限制的是拥塞窗口的最大值,并且只有在加lock关键字设置后才会生效Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port tcp ESTAB 0 0 127.0.0.1:9877 127.0.0.2:webmin reno wscale:0,7 rto:252 rtt:50.287/25.143 mss:50 cwnd:5 ssthresh:40 segs_in:2 send 39.8Kbps lastsnd:7230928 lastack:7230928 pacing_rate 79.5Kbps rcv_space:43690127.0.0.2 age 601.280sec ssthresh 40 cwnd 24 rtt 50275us rttvar 50275us source 127.0.0.1****@Inspiron:****/04_cc/tcp17# server send startserver send end sleep 30s****@Inspiron:****/04_cc/tcp17# ip route show table all | grep 127.0.0.2; ss -i sport = 9877;ip tcp_metrics show 127.0.0.2local 127.0.0.2 dev lo table local scope host ssthresh 40 cwnd 8 initcwnd 5 congctl reno#快速重传及快速恢复后,cwnd=7,ssthresh=6Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port tcp ESTAB 0 0 127.0.0.1:9877 127.0.0.2:webmin reno wscale:0,7 rto:252 rtt:49.999/0.534 mss:50 cwnd:7 ssthresh:6 bytes_acked:750 segs_out:16 segs_in:17 send 56.0Kbps lastsnd:7261040 lastack:7261088 pacing_rate 67.2Kbps retrans:0/1 rcv_space:43690127.0.0.2 age 630.636sec ssthresh 40 cwnd 24 rtt 50275us rttvar 50275us source 127.0.0.1****@Inspiron:****/04_cc/tcp17# server sockfd close[2]+ 已完成 ./client.out 8 > rst_client****@Inspiron:****/04_cc/tcp17# ip route show table all | grep 127.0.0.2; ss -i sport = 9877;ip tcp_metrics show 127.0.0.2local 127.0.0.2 dev lo table local scope host ssthresh 40 cwnd 8 initcwnd 5 congctl renoNetid State Recv-Q Send-Q Local Address:Port Peer Address:Port #链接关闭后 能够看到cwnd和ssthresh都已经更新了 127.0.0.2 age 4.996sec ssthresh 6 cwnd 15 rtt 50244us rttvar 37823us source 127.0.0.1****@Inspiron:****/04_cc/tcp17#
补充说明:
一、实际在TCP metrics中有7个参数,可是有两个是冗余的,主要是为了接口兼容保留下来的,参考kernel修改https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=740b0f1841f6e39085b711d41db9ffb07198682b&dt=1,Iproute2配套修改 https://patchwork.ozlabs.org/patch/386544/,Iproute2即为ss命令的程序包,Iproute2还包含其余的一些命令,用来取代net-tools程序包,net-tools程序包就是netstat/ip等程序所在的软件包。
二、TCP metrics枚举tcp_metric_index、TCP_METRICS_ATTR_UNSPEC,destination metric枚举RTAX_UNSPEC
三、链接创建时候更新metric信息初始化链接状态变量tcp_init_metrics,链接关闭时候更新tcp metrics代码点tcp_update_metrics
四、显然TCPIP详解中对于destination metric中,cwnd的解释也是错误的了。