从STGW流量下降探秘内核收包机制

本文介绍了在STGW出现流量下降问题时,通过监控发现CPU软中断热点,深入分析了内核的`inet_lookup_listener`函数问题,以及网卡多队列和RPS(Receive Packet Steering)不均衡的情况。文章揭示了由于内核哈希桶大小不足、网卡RSS不支持GRE协议等因素导致的问题,并提出了包括调整内核参数、更换网卡和内核补丁在内的解决方案。
摘要由CSDN通过智能技术生成

导语:STGW作为公司七层接入网关,在云和自研业务中承担多种网络协议接入与转发的功能,由于业务数量庞大、接入形式多样、网络环境复杂,会遇到一些很有挑战的疑难杂症。某次业务出现了流量突然下降,此时用户侧也有延迟上升和重试增多的问题。在团队自研的秒级监控助力下,我们从CPU软中断热点入手追查,发现了内核listen port哈希机制存在消耗过高问题,但热点只出现在部分核心上,接着在网卡多队列、内核Receive Packet Steering(RPS)上发现了负载均衡策略的缺陷,找出最终原因后我们在硬件和内核层面都做出了解决方案,并在现网进行了修复。

问题现象

在STGW现网运营中,出现了一起流量突然下降的Case,此时我们的健康拨测机制探测到失败,并且用户侧重试次数增多、请求延迟增大。但通过已有的各类监控进行定位,只发现整体CPU、内存、进程状态、QPS(每秒请求数)等关键指标虽然出现波动,但均未超过告警水位。

如图,流量出现了跌幅,并且出现健康检查拨测失败。

但是,整体CPU在流量出现缺口的期间,并未超过阈值,反而有一些下降,随后因为恢复正常流量冲高才出现一个小毛刺。

此外,内存和应用层监控,都没有发现明显异常。

前期探索

显然,仅凭这些常规监控,无法定位问题根本原因,尽量拿到更多的问题信息,成为了当务之急。幸运的是,从STGW自研的秒级监控系统中,我们查到了一些关键的信息。

在STGW自研的监控系统里,我们增加了核心资源细粒度监控,针对CPU、内存、内核网络协议栈这些核心指标支持秒级监控、监控指标更细化,如下图就是出问题时间段,cpu各个核心的秒级消耗情况。

通过STGW CPU细粒度监控展示的信息,可以看到在出现问题的时间段内,部分CPU核被跑满,并且是由于软中断消耗造成,回溯整个问题时间段,我们还发现,在一段长时间内,这种软中断热点偏高都会在几个固定的核上出现,不会转移给其他核。

此外,STGW的监控模块支持在出现系统核心资源异常时,抓取当时的函数调用栈信息,有了函数调用信息,我们能更准确的知道是什么造成了系统核心资源异常,而不是继续猜想。如图展示了STGW监控抓到的函数调用及cpu占比信息:

通过函数栈监控信息,我们发现了inet_lookup_listener函数是当时CPU软中断热点的主要消耗者。出现问题时,其他函数调用在没有发生多少变化情况下,inet_lookup_listener由原本很微小的cpu消耗占比,一下子冲到了TOP1。

通过这里,我们可以初步确定,inet_lookup_listener消耗过高跟软中断热点强相关,当热点将cpu单核跑满后就可能引发出流量有损的问题。由于软中断热点持续在产生,线上稳定性隐患很大。基于这个紧迫的稳定性问题,我们从为什么产生热点、为什么热点只在部分cpu core上出现两个方向,进行了问题分析、定位和解决。

为什么产生了热点

1. 探秘 inet_lookup_listener

由于perf已经给我们提供了热点所在,首先从热点函数入手进行分析,结合内核代码得知,__inet_lookup系列函数是用于将收到的数据包定位到一个具体的socket上,但只有握手包会进入到找__inet_lookup_listener的逻辑,大部分数据包是通过__inet_lookup_established寻找socket。

具体分析lookup_listener的代码我们发现,由于listen socket不具备四元组特征,因此内核只能用监听端口计算一个哈希值,并使用了 listening_hash 哈希桶存起来,握手包发过来的时候,就从该哈希桶中寻找对应的listen socket。

struct sock *__inet_lookup_listener(struct net *net,            struct inet_hashinfo *hashinfo,            const __be32 saddr, __be16 sport,            const __be32 daddr, const unsigned short hnum,            const int dif){// 省略了部分代码// 获取listen fd 哈希桶  struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash];        result = NULL;  hiscore = 0;// 遍历桶中的节点        sk_nulls_for_each_rcu(sk, node, &ilb->head) {    score = compute_score(sk, net, hnum, daddr, dif);    if (score > hiscore) {      result = sk;      hiscore = score;      reuseport = sk->sk_reuseport;      if (reuseport) {        phash = inet_ehashfn(net, daddr, hnum,                 saddr, sport);        matches = 1;      }    } else if (score == hiscore && reuseport) {      matches++;      if (((u64)phash * matches) >> 32 == 0)        result = sk;      phash = next_pseudo_random32(phash);    }  }} 

相对来说并不复杂的lookup_listener函数为什么会造成这么大的cpu开销?

经过进一步定位后,发现问题所在:listen哈希桶开的太小了,只有32个。

/* This is for listening sockets, thus all sockets which possess wildcards. */#define INET_LHTABLE_SIZE  32  /
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值