TCP Thin-Stream连接

Thin-stream属性,意味着应用程序以很低的速率发送数据,致使TCP等传输协议的重传机制不能有效的运行。一些场景(类似于在线游戏,控制系统,股票交易等)中,用户体验取决于数据的发送时延,报文丢失对于服务质量来说是灾难性的。极大的时延是由于TCP依赖于应用程序新的报文的发送,进而通过快速重传来启动丢失报文的重传,而不用等待较长时间的RTO超时。

以上提到的时间敏感的交互应用,通常是会产生thin-stream,并且在生命期内一直保持此流量模型。这将导致TCP的高延时。

为了降低报文丢失时的应用层延时,提出了一系列的应对机制。简单来说,如果内核检测到thin-stream,将按如下方式修改重传机制:

1) 在接收到第一个dupACK时,启动快速重传;
2) 不执行指数级退避。

注意,只有在检测到此连接为thin-stream时,才能应用以上的措施。如果inflight报文少于4个,而传统的快速重传需要3个重复ACK,此情况下将不能触发快速重传,此连接可能经历大的重传延时。通过定义InfightSize阈值4,来确定Thin-stream连接。

既然这些机制是针对时间敏感的应用程序,就需要在应用程序中使用IOCTL选项 TCP_THIN_LINEAR_TIMEOUTS 和 TCP_THIN_DUPACK明确的开启,或者通过sysctl选项tcp_thin_linear_timeouts 和 tcp_thin_dupack开启。前两个是针对连接的开关,而后两个是对整个网络命名空间的开关,默认情况下都是关闭状态。

$ cat /proc/sys/net/ipv4/tcp_thin_linear_timeouts
0
$ cat /proc/sys/net/ipv4/tcp_thin_dupack
0

Thin-Stream配置

如下函数do_tcp_setsockopt,选项TCP_THIN_LINEAR_TIMEOUTS设置套接口成员变量thin_lto,取值为0或者1。选项TCP_THIN_DUPACK目前没有使用。

static int do_tcp_setsockopt(struct sock *sk, int level,
        int optname, char __user *optval, unsigned int optlen)
{
    switch (optname) { 
    case TCP_THIN_LINEAR_TIMEOUTS:
        if (val < 0 || val > 1)
            err = -EINVAL;
        else
            tp->thin_lto = val;
        break;

    case TCP_THIN_DUPACK:
        if (val < 0 || val > 1)
            err = -EINVAL;
        break;

Thin-stream判断

如下函数tcp_stream_is_thin,如果inflight报文数量小于阈值4,并且不是位于初始的slowstart阶段,判定此连接为thin-stream。

/* Determines whether this is a thin stream (which may suffer from
 * increased latency). Used to trigger latency-reducing mechanisms.
 */
static inline bool tcp_stream_is_thin(struct tcp_sock *tp)
{
    return tp->packets_out < 4 && !tcp_in_initial_slowstart(tp);
}
static inline bool tcp_in_initial_slowstart(const struct tcp_sock *tp)
{
    return tp->snd_ssthresh >= TCP_INFINITE_SSTHRESH;
}

Thin-stream超时处理

如下函数tcp_retransmit_timer,如果应用层通过setsockopt开启了thin_lto,或者在此网络命名空间全局开启了线性超时(tcp_thin_linear_timeouts),并且内核确认了此连接为Thin-stream,将不进行退避处理,由函数__tcp_set_rto根据当前的SRTT设置RTO值。并且将icsk_backoff计数清零,这样在此连接上进行窗口探测等操作时,也不执行退避操作。

void tcp_retransmit_timer(struct sock *sk)
{

    icsk->icsk_backoff++;
    icsk->icsk_retransmits++;

out_reset_timer:
    /* If stream is thin, use linear timeouts. Since 'icsk_backoff' is
     * used to reset timer, set to 0. Recalculate 'icsk_rto' as this
     * might be increased if the stream oscillates between thin and thick,
     * thus the old value might already be too high compared to the value
     * set by 'tcp_set_rto' in tcp_input.c which resets the rto without
     * backoff. Limit to TCP_THIN_LINEAR_RETRIES before initiating
     * exponential backoff behaviour to avoid continue hammering
     * linear-timeout retransmissions into a black hole
     */
    if (sk->sk_state == TCP_ESTABLISHED &&
        (tp->thin_lto || net->ipv4.sysctl_tcp_thin_linear_timeouts) &&
        tcp_stream_is_thin(tp) &&
        icsk->icsk_retransmits <= TCP_THIN_LINEAR_RETRIES) {
        icsk->icsk_backoff = 0;
        icsk->icsk_rto = min(__tcp_set_rto(tp), TCP_RTO_MAX);
    } else {
        /* Use normal (exponential) backoff */
        icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX);
    }     
    inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
                  tcp_clamp_rto_to_user_timeout(sk), TCP_RTO_MAX);

但是,如果在Thin-stream连接上线性重传了6次之后,内核将启用指数级别的退避处理,变量icsk_rto表示的时长每次增大一倍。

/* TCP thin-stream limits */
#define TCP_THIN_LINEAR_RETRIES 6       /* After 6 linear retries, do exp. backoff */

内核版本 5.0

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页