OpenCloudOS 如何利用 nettrace 进行网络故障诊断

在开源 Linux 操作系统 OpenCloudOS 8.6 中,增加了内核对网络工具 nettrace 的支持,允许开发者通过 bpf 进行网络丢包原因跟踪,内核也同时回合相关的丢包跟踪点。今天,就以 nettrace 为典型,介绍如何在 OpenCloudOS 中利用 nettrace 进行网络故障诊断。

一、工具简介

1. 背景

在一些场景下(特别是云原生场景),Linux 系统中的网络部署变得越来越复杂。一个 TCP 连接,从客户端到服务端,中间可能要经过复杂的 NAT、GRE、IPVS 等过程,网络报文在节点(主机)上的处理路径也变得越来越长。在发生网络故障(比如网络丢包)时,如何快速、有效地定位出网络问题成为了一个难题。目前常规的网络故障定位手段,如 tcpdump、dropwatch、ftrace、kprobe 等存在一定的短板:

  • tcpdump:只能在链路层抓包,无法定位内核协议栈中的问题,比如常规的内核丢包问题
  • ftrace:只能跟踪内核函数,无法进行报文过滤,且入手较难,需要对内核协议栈有一定了解
  • kprobe:临时编写内核模块,效率和安全性低
  • BCC:功能单一,临时编写 BCC 程序跟踪效率低,需要对内核有一定了解,入手难
  • dropwatch:功能单一,只能查看网络丢包问题,且无法得到丢包原因和解决方案

在此背景下,笔者结合多年的 Kernel 网络协议栈故障定位经验,基于 eBPF 开发了 Linux 环境下网络故障定位工具集——nettrace。

2. 功能介绍

nettrace 是一款基于 eBPF 的集网络报文跟踪(故障定位)、网络故障诊断、网络异常监控于一体的网络工具集,旨在能够提供一种更加高效、易用的方法来解决复杂场景下的网络问题。目前,其实现的功能包括:

  • 网络报文跟踪:跟踪网络报文从进入到内核协议栈到释放/丢弃的过程中在内核中所走过的路径,实现报文整个生命周期的监控,并采集生命周期各个阶段的事件、信息。通过观察报文在内核中的路径,对于有一定内核协议栈经验的人来说可以快速、有效地发现网络问题。
  • 网络故障诊断:将以往的经验集成到工具的知识库,通过知识匹配的方式来主动诊断当前网络故障,给出诊断结果以及修复建议。该功能入手简单、易用性强,无需过多的网络经验即可进行网络问题定位。
  • 网络异常监控:常态化地部署到生产环境中,主动地发现、上报环境上的网络异常。
  • droptrace:用于跟踪、监控系统中的丢包事件的工具,在文末链接中查看详情介绍。该功能已被遗弃,可以使用 nettrace --drop 实现相同的功能。

二、安装方法

nettrace 是采用 C 语言编写的基于 eBPF(libbpf)的命令行工具,在使用和安装时可以用编译好的 RPM 包和二进制程序。注意:本工具目前仅在 4.14 及以上的内核版本上进行过兼容性测试,因此请确保当前的系统所使用的的内核版本在 4.14 以上。

对于默认继承 yum 的 OpenCloudOS,本工具已经上线到对应的软件仓库,可以方便快捷地直接使用 yum 命令来进行在线安装:

sudo yum install nettrace

也可以直接从 OpenCloudOS releases 页面中下载对应的 RPM/DEB 安装包,手动进行安装。

三、使用方法

nettrace 是用来跟踪内核报文和诊断网络故障的,在进行报文跟踪时可以使用一定的过滤条件来跟踪特定的报文。其基本命令行参数为:

$ nettrace -h
nettrace: a tool to trace skb in kernel and diagnose network problem

Usage:
    -s, --saddr      filter source ip address
    --saddr6         filter source ip v6 address
    -d, --daddr      filter dest ip address
    --daddr6         filter dest ip v6 address
    --addr           filter source or dest ip address
    --addr6          filter source or dest ip v6 address
    -S, --sport      filter source TCP/UDP port
    -D, --dport      filter dest TCP/UDP port
    -P, --port       filter source or dest TCP/UDP port
    -p, --proto      filter L3/L4 protocol, such as 'tcp', 'arp'
    --pid            filter by current process id(pid)
    -t, --trace      enable trace group or trace
    --ret            show function return value
    --detail         show extern packet info, such as pid, ifname, etc
    --date           print timestamp in date-time format
    --basic          use 'basic' trace mode, don't trace skb's life
    --diag           enable 'diagnose' mode
    --diag-quiet     only print abnormal packet
    --diag-keep      don't quit when abnormal packet found
    --hooks          print netfilter hooks if dropping by netfilter
    --drop           skb drop monitor mode, for replace of 'droptrace'
    --drop-stack     print the kernel function call stack of kfree_skb

    -v               show log information
    --debug          show debug information
    -h, --help       show help information

其中,参数 s/d/addr/S/D/port/p/pid 用于进行报文的过滤,可以通过 IP 地址、端口、协议等属性进行过滤。其他参数的用途包括:

  • t/trace:要启用的跟踪模块,默认启用所有
  • ret:跟踪和显示内核函数的返回值
  • detail:显示跟踪详细信息,包括当前的进程、网口和 CPU 等信息
  • date:以时间格式打印(以 2022-10-24 xx:xx:xx.xxxxxx 格式打印),而不是时间戳
  • basic:启用 basic 跟踪模式。默认情况下,启用的是生命周期跟踪模式。启用该模式后,会直接打印出报文所经过的内核函数 /tracepoint。
  • diag:启用诊断模式
  • diag-quiet:只显示出现存在问题的报文,不显示正常的报文
  • diag-keep:持续跟踪。diag 模式下,默认在跟踪到异常报文后会停止跟踪,使用该参数后,会持续跟踪下去。
  • hooks:结合 netfilter 做的适配,详见下文
  • drop:进行系统丢包监控,取代原先的 droptrace
  • drop-stack: 打印 kfree_skb 内核函数的调用堆栈

下面我们首先来看一下默认模式下的工具使用方法。

1. 生命周期

默认情况下,nettrace 会跟踪报文从进入到内核协议栈到离开(销毁)的过程。对于有一定内核网络经验的人来说,可以通过报文的内核路径来快速推断出当前的网络问题,达到快速定位的目的。

1.1 跟踪ping报文

sudo ./nettrace -p icmp
begin trace...
***************** ffff889be8fbd500,ffff889be8fbcd00 ***************
[1272349.614564] [dev_gro_receive     ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 48220
[1272349.614579] [__netif_receive_skb_core] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 48220
[1272349.614585] [ip_rcv              ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 48220
[1272349.614592] [ip_rcv_core         ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 48220
[1272349.614599] [skb_clone           ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 48220
[1272349.614616] [nf_hook_slow        ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 48220
[1272349.614629] [nft_do_chain        ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 48220
[1272349.614635] [ip_rcv_finish       ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 48220
[1272349.614643] [ip_route_input_slow ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 48220
[1272349.614647] [fib_validate_source ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 48220
[1272349.614652] [ip_local_deliver    ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 48220
[1272349.614658] [nf_hook_slow        ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 48220
[1272349.614663] [ip_local_deliver_finish] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 48220
[1272349.614666] [icmp_rcv            ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 48220
[1272349.614671] [icmp_echo           ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 48220
[1272349.614675] [icmp_reply          ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 48220
[1272349.614715] [consume_skb         ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 48220
[1272349.614722] [packet_rcv          ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 48220
[1272349.614725] [consume_skb         ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 48220

***************** ffff889be8fbde00 ***************
[1272349.614681] [nf_hook_slow        ] ICMP: 172.27.0.6 -> 169.254.128.15 ping reply, seq: 48220
[1272349.614688] [ip_output           ] ICMP: 172.27.0.6 -> 169.254.128.15 ping reply, seq: 48220
[1272349.614690] [nf_hook_slow        ] ICMP: 172.27.0.6 -> 169.254.128.15 ping reply, seq: 48220
[1272349.614693] [ip_finish_output    ] ICMP: 172.27.0.6 -> 169.254.128.15 ping reply, seq: 48220
[1272349.614697] [ip_finish_output2   ] ICMP: 172.27.0.6 -> 169.254.128.15 ping reply, seq: 48220
[1272349.614705] [__dev_queue_xmit    ] ICMP: 172.27.0.6 -> 169.254.128.15 ping reply, seq: 48220
[1272349.614709] [dev_hard_start_xmit ] ICMP: 172.27.0.6 -> 169.254.128.15 ping reply, seq: 48220
[1272351.286866] [consume_skb         ] ICMP: 172.27.0.6 -> 169.254.128.15 ping reply, seq: 48220

上面的*中间的表示当前所跟踪的 skb 的地址,由于当前的报文被克隆过,因此当前跟踪上下文存在两个报文。

1.2 指定过滤条件

sudo ./nettrace -p icmp --saddr 169.254.128.15
begin trace...
***************** ffff889be8fbc700,ffff889be8fbdc00 ***************
[1273445.360831] [dev_gro_receive     ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 54754
[1273445.360844] [__netif_receive_skb_core] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 54754
[1273445.360847] [ip_rcv              ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 54754
[1273445.360850] [ip_rcv_core         ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 54754
[1273445.360854] [skb_clone           ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 54754
[1273445.360861] [nf_hook_slow        ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 54754
[1273445.360868] [nft_do_chain        ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 54754
[1273445.360875] [ip_rcv_finish       ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 54754
[1273445.360878] [ip_route_input_slow ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 54754
[1273445.360882] [fib_validate_source ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 54754
[1273445.360887] [ip_local_deliver    ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 54754
[1273445.360890] [nf_hook_slow        ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 54754
[1273445.360895] [ip_local_deliver_finish] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 54754
[1273445.360899] [icmp_rcv            ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 54754
[1273445.360903] [icmp_echo           ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 54754
[1273445.360908] [icmp_reply          ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 54754
[1273445.360922] [consume_skb         ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 54754
[1273445.360930] [packet_rcv          ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 54754
[1273445.360933] [consume_skb         ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 54754

1.3 显示详细信息

sudo ./nettrace -p icmp --saddr 169.254.128.15 --detail
begin trace...
***************** ffff889be8fbcd00,ffff889be8fbcc00 ***************
[1273732.110173] [ffff889be8fbcd00][dev_gro_receive     ][cpu:40 ][ens5 ][pid:0      ][swapper/40  ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 56464
[1273732.110185] [ffff889be8fbcd00][__netif_receive_skb_core][cpu:40 ][ens5 ][pid:0      ][swapper/40  ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 56464
[1273732.110189] [ffff889be8fbcd00][ip_rcv              ][cpu:40 ][ens5 ][pid:0      ][swapper/40  ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 56464
[1273732.110192] [ffff889be8fbcd00][ip_rcv_core         ][cpu:40 ][ens5 ][pid:0      ][swapper/40  ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 56464
[1273732.110196] [ffff889be8fbcd00][skb_clone           ][cpu:40 ][ens5 ][pid:0      ][swapper/40  ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 56464
[1273732.110204] [ffff889be8fbcc00][nf_hook_slow        ][cpu:40 ][ens5 ][pid:0      ][swapper/40  ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 56464
[1273732.110211] [ffff889be8fbcc00][nft_do_chain        ][cpu:40 ][ens5 ][pid:0      ][swapper/40  ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 56464
[1273732.110222] [ffff889be8fbcc00][ip_rcv_finish       ][cpu:40 ][ens5 ][pid:0      ][swapper/40  ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 56464
[1273732.110229] [ffff889be8fbcc00][ip_route_input_slow ][cpu:40 ][ens5 ][pid:0      ][swapper/40  ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 56464
[1273732.110234] [ffff889be8fbcc00][fib_validate_source ][cpu:40 ][ens5 ][pid:0      ][swapper/40  ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 56464
[1273732.110240] [ffff889be8fbcc00][ip_local_deliver    ][cpu:40 ][ens5 ][pid:0      ][swapper/40  ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 56464
[1273732.110243] [ffff889be8fbcc00][nf_hook_slow        ][cpu:40 ][ens5 ][pid:0      ][swapper/40  ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 56464
[1273732.110252] [ffff889be8fbcc00][ip_local_deliver_finish][cpu:40 ][ens5 ][pid:0      ][swapper/40  ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 56464
[1273732.110255] [ffff889be8fbcc00][icmp_rcv            ][cpu:40 ][ens5 ][pid:0      ][swapper/40  ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 56464
[1273732.110260] [ffff889be8fbcc00][icmp_echo           ][cpu:40 ][ens5 ][pid:0      ][swapper/40  ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 56464
[1273732.110267] [ffff889be8fbcc00][icmp_reply          ][cpu:40 ][ens5 ][pid:0      ][swapper/40  ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 56464
[1273732.110283] [ffff889be8fbcc00][consume_skb         ][cpu:40 ][ens5 ][pid:0      ][swapper/40  ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 56464
[1273732.110291] [ffff889be8fbcd00][packet_rcv          ][cpu:40 ][ens5 ][pid:0      ][swapper/40  ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 56464
[1273732.110294] [ffff889be8fbcd00][consume_skb         ][cpu:40 ][ens5 ][pid:0      ][swapper/40  ] ICMP: 169.254.128.15 -> 172.27.0.6 ping request, seq: 56464

可以看到,每个报文的地址、所在 CPU、网口和进程信息都被打印了出来。

1.4 NAT 跟踪

在对报文进行跟踪时,一旦报文被跟踪起来(命中过滤条件),那么这个报文即使内容发生了变化也会持续被跟踪,知道报文被释放。下面是 NAT 场景下的跟踪,可以看到报文的源地址由 192.168.122.8 通过 SNAT 被修改成了 9.135.224.89,但是报文依然被跟踪到了:

$ sudo ./nettrace -p icmp --addr 192.168.122.8
begin tracing......
<------------------- skb: ffff88818f02f900 ---------------------->
463697.331957: [__netif_receive_skb_core]: ICMP: 192.168.122.8 -> 10.123.119.98, ping request   , seq: 0
463697.331972: [nf_hook_slow            ]: ICMP: 192.168.122.8 -> 10.123.119.98, ping request   , seq: 0
463697.331985: [nf_hook_slow            ]: ICMP: 192.168.122.8 -> 10.123.119.98, ping request   , seq: 0
463697.331990: [__netif_receive_skb_core]: ICMP: 192.168.122.8 -> 10.123.119.98, ping request   , seq: 0
463697.331994: [ip_rcv                  ]: ICMP: 192.168.122.8 -> 10.123.119.98, ping request   , seq: 0
463697.331998: [ip_rcv_core             ]: ICMP: 192.168.122.8 -> 10.123.119.98, ping request   , seq: 0
463697.332001: [nf_hook_slow            ]: ICMP: 192.168.122.8 -> 10.123.119.98, ping request   , seq: 0
463697.332004: [ip_rcv_finish           ]: ICMP: 192.168.122.8 -> 10.123.119.98, ping request   , seq: 0
463697.332010: [ip_forward              ]: ICMP: 192.168.122.8 -> 10.123.119.98, ping request   , seq: 0
463697.332014: [nf_hook_slow            ]: ICMP: 192.168.122.8 -> 10.123.119.98, ping request   , seq: 0
463697.332024: [ip_output               ]: ICMP: 192.168.122.8 -> 10.123.119.98, ping request   , seq: 0
463697.332027: [nf_hook_slow            ]: ICMP: 192.168.122.8 -> 10.123.119.98, ping request   , seq: 0
463697.332037: [ip_finish_output        ]: ICMP: 9.135.224.89  -> 10.123.119.98, ping request   , seq: 0
463697.332039: [ip_finish_output2       ]: ICMP: 9.135.224.89  -> 10.123.119.98, ping request   , seq: 0
463697.332042: [dev_queue_xmit          ]: ICMP: 9.135.224.89  -> 10.123.119.98, ping request   , seq: 0
463697.332046: [dev_hard_start_xmit     ]: ICMP: 9.135.224.89  -> 10.123.119.98, ping request   , seq: 0
463697.332060: [consume_skb             ]: ICMP: 9.135.224.89  -> 10.123.119.98, ping request   , seq: 0

2. 诊断模式

使用方式与上面的一致,加个 diag 参数即可使用诊断模式。上文的生命周期模式对于使用者的要求比较高,需要了解内核协议栈各个函数的用法、返回值的意义等,易用性较差。诊断模式是在生命周期模式的基础上,提供了更加丰富的信息,使得没有网络开发经验的人也可进行复杂网络问题的定位和分析。

2.1 基本用法

下面是使用诊断模式进行报文跟踪的用法,可以看出来相比于普通模式,诊断模式提供了更多的可供参考的信息,包括当前报文经过了 iptables 的哪些表和哪些链、报文发生了 NAT、报文被克隆了等。诊断模式设置了三种提示级别:

  • INFO:正常的信息提示
  • WARN:警告信息,该报文可能存在一定的问题,需要关注
  • ERROR:异常信息,报文发生了问题(比如被丢弃)。
./nettrace -p icmp --diag --saddr 192.168.122.8
begin trace...
***************** ffff889fad356200 ***************
[3445.575957] [__netif_receive_skb_core] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0
[3445.575978] [nf_hook_slow        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0 *ipv4 in chain: PRE_ROUTING*
[3445.575990] [nft_do_chain        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0 *iptables table:nat, chain:PREROUT* *packet is accepted*
[3445.576005] [nf_hook_slow        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0 *bridge in chain: PRE_ROUTING*
[3445.576014] [__netif_receive_skb_core] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0
[3445.576024] [ip_rcv              ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0
[3445.576029] [ip_rcv_core         ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0
[3445.576040] [nf_hook_slow        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0 *ipv4 in chain: PRE_ROUTING*
[3445.576044] [ip_rcv_finish       ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0
[3445.576052] [ip_route_input_slow ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0
[3445.576061] [fib_validate_source ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0
[3445.576080] [ip_forward          ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0
[3445.576084] [nf_hook_slow        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0 *ipv4 in chain: FORWARD*
[3445.576087] [nft_do_chain        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0 *iptables table:filter, chain:FORWARD* *packet is accepted*
[3445.576107] [ip_output           ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0
[3445.576113] [nf_hook_slow        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0 *ipv4 in chain: POST_ROUTING*
[3445.576116] [nft_do_chain        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0 *iptables table:nat, chain:POSTROU* *packet is accepted*
[3445.576131] [nf_nat_manip_pkt    ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0 *NAT happens (packet address will change)*
[3445.576148] [ip_finish_output    ] ICMP: 192.168.255.10 -> 10.123.119.98 ping request, seq: 0
[3445.576152] [ip_finish_output2   ] ICMP: 192.168.255.10 -> 10.123.119.98 ping request, seq: 0
[3445.576158] [__dev_queue_xmit    ] ICMP: 192.168.255.10 -> 10.123.119.98 ping request, seq: 0
[3445.576165] [netdev_core_pick_tx ] ICMP: 192.168.255.10 -> 10.123.119.98 ping request, seq: 0
[3445.576177] [dev_hard_start_xmit ] ICMP: 192.168.255.10 -> 10.123.119.98 ping request, seq: 0
[3445.576215] [consume_skb         ] ICMP: 192.168.255.10 -> 10.123.119.98 ping request, seq: 0 *packet is freed (normally)*
---------------- ANALYSIS RESULT ---------------------
[1] WARNING happens in nf_nat_manip_pkt(netfilter):
        NAT happens (packet address will change)

如果当前报文存在 ERROR,那么工具会给出一定的诊断修复建议,并终止当前诊断操作。通过添加 diag-keep 可以在发生 ERROR 事件时不退出,继续进行跟踪分析。下面是发生异常时的日志:

./nettrace -p icmp --diag --saddr 192.168.122.8
begin trace...
***************** ffff889fb3c64f00 ***************
[4049.295546] [__netif_receive_skb_core] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0
[4049.295566] [nf_hook_slow        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0 *ipv4 in chain: PRE_ROUTING*
[4049.295578] [nft_do_chain        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0 *iptables table:nat, chain:PREROUT* *packet is accepted*
[4049.295594] [nf_hook_slow        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0 *bridge in chain: PRE_ROUTING*
[4049.295612] [__netif_receive_skb_core] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0
[4049.295624] [ip_rcv              ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0
[4049.295629] [ip_rcv_core         ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0
[4049.295640] [nf_hook_slow        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0 *ipv4 in chain: PRE_ROUTING*
[4049.295644] [ip_rcv_finish       ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0
[4049.295655] [ip_route_input_slow ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0
[4049.295664] [fib_validate_source ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0
[4049.295683] [ip_forward          ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0
[4049.295687] [nf_hook_slow        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0 *ipv4 in chain: FORWARD* *packet is dropped by netfilter (NF_DROP)*
[4049.295695] [nft_do_chain        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0 *iptables table:filter, chain:FORWARD* *packet is dropped by iptables/iptables-nft*
[4049.295711] [kfree_skb           ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0 *packet is dropped by kernel*
---------------- ANALYSIS RESULT ---------------------
[1] ERROR happens in nf_hook_slow(netfilter):
        packet is dropped by netfilter (NF_DROP)
    fix advice:
        check your netfilter rule

[2] ERROR happens in nft_do_chain(netfilter):
        packet is dropped by iptables/iptables-nft
    fix advice:
        check your iptables rule

[3] ERROR happens in kfree_skb(life):
        packet is dropped by kernel
    location:
        nf_hook_slow+0x96
    drop reason:
        NETFILTER_DROP

analysis finished!

end trace...

从这里的日志可以看出,在报文经过 iptables 的 filter 表的 forward 链的时候,发生了丢包。在诊断结果里,会列出所有的异常事件,一个报文跟踪可能会命中多条诊断结果。这里的诊断建议是让用户检查 iptables 中的规则是否存在问题。

其中,kfree_skb 这个跟踪点是对 drop reason 内核特性做了适配的,可以理解为将 droptrace 的功能集成到了这里的诊断结果中,这里可以看出其给出的对包原因是 NETFILTER_DROP。

2.2 netfilter支持

网络防火墙是网络故障、网络不同发生的重灾区,因此 netfilter 工具对 netfilter 提供了完美适配,包括老版本的 iptables-legacy 和新版本的 iptables-nft。诊断模式下,nettrace 能够跟踪报文所经过的 iptables 表和 iptables 链,并在发生由于 iptables 导致的丢包时给出一定的提示,上面的示例充分展现出了这部分。

除了对 iptables 的支持,nettrace 对整个 netfilter 大模块也提供了支持,能够显示在经过每个 HOOK 点时对应的协议族和链的名称。除此之外,为了应对一些注册到 netfilter 中的第三方内核模块导致的丢包问题,nettrace 还可以通过添加参数 hooks 来打印出当前 HOOK 上所有的的钩子函数,从而深入分析问题:

./nettrace -p icmp --diag --saddr 192.168.122.8 --hooks
begin trace...
***************** ffff889faa054500 ***************
[5810.702473] [__netif_receive_skb_core] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 943
[5810.702491] [nf_hook_slow        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 943 *ipv4 in chain: PRE_ROUTING*
[5810.702504] [nft_do_chain        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 943 *iptables table:nat, chain:PREROUT* *packet is accepted*
[5810.702519] [nf_hook_slow        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 943 *bridge in chain: PRE_ROUTING*
[5810.702527] [__netif_receive_skb_core] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 943
[5810.702535] [ip_rcv              ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 943
[5810.702540] [ip_rcv_core         ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 943
[5810.702546] [nf_hook_slow        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 943 *ipv4 in chain: PRE_ROUTING*
[5810.702551] [ip_rcv_finish       ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 943
[5810.702556] [ip_route_input_slow ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 943
[5810.702565] [fib_validate_source ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 943
[5810.702579] [ip_forward          ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 943
[5810.702583] [nf_hook_slow        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 943 *ipv4 in chain: FORWARD* *packet is dropped by netfilter (NF_DROP)*
[5810.702586] [nft_do_chain        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 943 *iptables table:filter, chain:FORWARD* *packet is dropped by iptables/iptables-nft*
[5810.702599] [kfree_skb           ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 943 *packet is dropped by kernel*
---------------- ANALYSIS RESULT ---------------------
[1] ERROR happens in nf_hook_slow(netfilter):
        packet is dropped by netfilter (NF_DROP)

    following hook functions are blamed:
        nft_do_chain_ipv4

    fix advice:
        check your netfilter rule

[2] ERROR happens in nft_do_chain(netfilter):
        packet is dropped by iptables/iptables-nft
    fix advice:
        check your iptables rule

[3] ERROR happens in kfree_skb(life):
        packet is dropped by kernel
    location:
        nf_hook_slow+0x96
    drop reason:
        NETFILTER_DROP

analysis finished!

end trace...

可以看出,上面 following hook functions are blamed 中列出了导致当前 netfilter 丢包的所有的钩子函数,这里只有 iptables 一个钩子函数。

2.3 其他场景

由于对 drop reason 内核特性进行了适配,因此对于支持 drop reason 的系统,基于 drop reason 本工具可以诊断 70+ 种丢包问题。nettrace 通过将网络诊断经验翻译成规则存储到规则库的方式来进行诊断分析,通过扩充规则配置文件的方式能够不断增强其诊断功能。目前,本工具已经集成了 20+ 典型网络故障诊断功能,并且在实践中不断完善知识库(规则库)。

端口未监听导致的丢包:

./nettrace --diag --diag-quiet
begin trace...
***************** ffff888f97730ee0 ***************
[365673.326016] [ip_output           ] TCP: 127.0.0.1:40392 -> 127.0.0.1:9999 seq:3067626996, ack:0, flags:S
[365673.326026] [ip_finish_output    ] TCP: 127.0.0.1:40392 -> 127.0.0.1:9999 seq:3067626996, ack:0, flags:S
[365673.326029] [ip_finish_output2   ] TCP: 127.0.0.1:40392 -> 127.0.0.1:9999 seq:3067626996, ack:0, flags:S
[365673.326032] [__dev_queue_xmit    ] TCP: 127.0.0.1:40392 -> 127.0.0.1:9999 seq:3067626996, ack:0, flags:S
[365673.326039] [dev_hard_start_xmit ] TCP: 127.0.0.1:40392 -> 127.0.0.1:9999 seq:3067626996, ack:0, flags:S
[365673.326042] [enqueue_to_backlog  ] TCP: 127.0.0.1:40392 -> 127.0.0.1:9999 seq:3067626996, ack:0, flags:S
[365673.326051] [__netif_receive_skb_core] TCP: 127.0.0.1:40392 -> 127.0.0.1:9999 seq:3067626996, ack:0, flags:S
[365673.326059] [ip_rcv              ] TCP: 127.0.0.1:40392 -> 127.0.0.1:9999 seq:3067626996, ack:0, flags:S
[365673.326061] [ip_rcv_core         ] TCP: 127.0.0.1:40392 -> 127.0.0.1:9999 seq:3067626996, ack:0, flags:S
[365673.326068] [ip_rcv_finish       ] TCP: 127.0.0.1:40392 -> 127.0.0.1:9999 seq:3067626996, ack:0, flags:S
[365673.326072] [ip_local_deliver    ] TCP: 127.0.0.1:40392 -> 127.0.0.1:9999 seq:3067626996, ack:0, flags:S
[365673.326075] [ip_local_deliver_finish] TCP: 127.0.0.1:40392 -> 127.0.0.1:9999 seq:3067626996, ack:0, flags:S
[365673.326078] [tcp_v4_rcv          ] TCP: 127.0.0.1:40392 -> 127.0.0.1:9999 seq:3067626996, ack:0, flags:S
[365673.326084] [__inet_lookup_listener] TCP: 127.0.0.1:40392 -> 127.0.0.1:9999 seq:3067626996, ack:0, flags:S *tcp port is not listened*
[365673.326090] [tcp_v4_send_reset   ] TCP: 127.0.0.1:40392 -> 127.0.0.1:9999 seq:3067626996, ack:0, flags:S
[365673.326125] [kfree_skb           ] TCP: 127.0.0.1:40392 -> 127.0.0.1:9999 seq:3067626996, ack:0, flags:S *packet is dropped by kernel*
---------------- ANALYSIS RESULT ---------------------
[1] WARNING happens in __inet_lookup_listener(tcp-in):
        tcp port is not listened
    fix advice:
        check your target tcp port

[2] ERROR happens in kfree_skb(life):
        packet is dropped by kernel
    location:
        tcp_v4_rcv+0x4a

XDP 导致的丢包(XDP 转发会给提示):

./nettrace -p icmp --diag --diag-quiet 
begin trace...
***************** ffff889f015acc00 ***************
[18490.607809] [__netif_receive_skb_core] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0
[18490.607828] [nf_hook_slow        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0 *ipv4 in chain: PRE_ROUTING*
[18490.607840] [nft_do_chain        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0 *iptables table:nat, chain:PREROUT* *packet is accepted*
[18490.607855] [nf_hook_slow        ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0 *bridge in chain: PRE_ROUTING*
[18490.607874] [__netif_receive_skb_core] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0
[18490.607882] [netif_receive_generic_xdp] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0 *packet is dropped by XDP program*
[18490.607888] [kfree_skb           ] ICMP: 192.168.122.8 -> 10.123.119.98 ping request, seq: 0 *packet is dropped by kernel*
---------------- ANALYSIS RESULT ---------------------
[1] ERROR happens in netif_receive_generic_xdp(link-in):
        packet is dropped by XDP program
    fix advice:
        check your XDP eBPF program

[2] ERROR happens in kfree_skb(life):
        packet is dropped by kernel
    location:
        netif_receive_generic_xdp+0x259
    drop reason:
        NOT_SPECIFIED

analysis finished!

3. 丢包监控

使用命令 nettrace --drop 可以对系统中的丢包事件进行监控,对于支持内核特性 skb drop reason 的内核,这里还会打印出丢包原因。可以通过查看 /tracing/events/skb/kfree_skb/format 来判断当前系统是否支持该特性:

cat /tracing/events/skb/kfree_skb/format 
name: kfree_skb
ID: 1524
format:
        field:unsigned short common_type;       offset:0;       size:2; signed:0;
        field:unsigned char common_flags;       offset:2;       size:1; signed:0;
        field:unsigned char common_preempt_count;       offset:3;       size:1; signed:0;
        field:int common_pid;   offset:4;       size:4; signed:1;

        field:void * skbaddr;   offset:8;       size:8; signed:0;
        field:void * location;  offset:16;      size:8; signed:0;
        field:unsigned short protocol;  offset:24;      size:2; signed:0;
        field:enum skb_drop_reason reason;      offset:28;      size:4; signed:0;

print fmt: "skbaddr=%p protocol=%u location=%p reason: %s", REC->skbaddr, REC->protocol, REC->location, __print_symbolic(REC->reason, { 1, "NOT_SPECIFIED" }, { 2, "NO_SOCKET" }, { 3, "PKT_TOO_SMALL" }, { 4, "TCP_CSUM" }, { 5, "SOCKET_FILTER" }, { 6, "UDP_CSUM" }, { 7, "NETFILTER_DROP" }, { 8, "OTHERHOST" }, { 9, "IP_CSUM" }, { 10, "IP_INHDR" }, { 11, "IP_RPFILTER" }, { 12, "UNICAST_IN_L2_MULTICAST" }, { 13, "XFRM_POLICY" }, { 14, "IP_NOPROTO" }, { 15, "SOCKET_RCVBUFF" }, { 16, "PROTO_MEM" }, { 17, "TCP_MD5NOTFOUND" }, { 18, "TCP_MD5UNEXPECTED" }, { 19, "TCP_MD5FAILURE" }, { 20, "SOCKET_BACKLOG" }, { 21, "TCP_FLAGS" }, { 22, "TCP_ZEROWINDOW" }, { 23, "TCP_OLD_DATA" }, { 24, "TCP_OVERWINDOW" }, { 25, "TCP_OFOMERGE" }, { 26, "TCP_RFC7323_PAWS" }, { 27, "TCP_INVALID_SEQUENCE" }, { 28, "TCP_RESET" }, { 29, "TCP_INVALID_SYN" }, { 30, "TCP_CLOSE" }, { 31, "TCP_FASTOPEN" }, { 32, "TCP_OLD_ACK" }, { 33, "TCP_TOO_OLD_ACK" }, { 34, "TCP_ACK_UNSENT_DATA" }, { 35, "TCP_OFO_QUEUE_PRUNE" }, { 36, "TCP_OFO_DROP" }, { 37, "IP_OUTNOROUTES" }, { 38, "BPF_CGROUP_EGRESS" }, { 39, "IPV6DISABLED" }, { 40, "NEIGH_CREATEFAIL" }, { 41, "NEIGH_FAILED" }, { 42, "NEIGH_QUEUEFULL" }, { 43, "NEIGH_DEAD" }, { 44, "TC_EGRESS" }, { 45, "QDISC_DROP" }, { 46, "CPU_BACKLOG" }, { 47, "XDP" }, { 48, "TC_INGRESS" }, { 49, "UNHANDLED_PROTO" }, { 50, "SKB_CSUM" }, { 51, "SKB_GSO_SEG" }, { 52, "SKB_UCOPY_FAULT" }, { 53, "DEV_HDR" }, { 54, "DEV_READY" }, { 55, "FULL_RING" }, { 56, "NOMEM" }, { 57, "HDR_TRUNC" }, { 58, "TAP_FILTER" }, { 59, "TAP_TXFILTER" }, { 60, "ICMP_CSUM" }, { 61, "INVALID_PROTO" }, { 62, "IP_INADDRERRORS" }, { 63, "IP_INNOROUTES" }, { 64, "PKT_TOO_BIG" }, { 65, "MAX" })

该模式下使用的效果与原先的 droptrace 完全相同,如下所示:

nettrace --drop
begin trace...
[142.097193] TCP: 162.241.189.135:57022 -> 172.27.0.6:22 seq:299038593, ack:3843597961, flags:AR, reason: NOT_SPECIFIED, tcp_v4_rcv+0x81
[142.331798] TCP: 162.241.189.135:57022 -> 172.27.0.6:22 seq:299038593, ack:3843597961, flags:A, reason: NOT_SPECIFIED, tcp_v4_do_rcv+0x83
[142.331857] TCP: 162.241.189.135:57022 -> 172.27.0.6:22 seq:299038593, ack:3843597961, flags:AP, reason: NOT_SPECIFIED, tcp_v4_do_rcv+0x83
[146.136576] TCP: 127.0.0.1:43582 -> 127.0.0.1:9999 seq:3819454691, ack:0, flags:S, reason: NO_SOCKET, tcp_v4_rcv+0x81
[146.220414] TCP: 169.254.0.138:8186 -> 172.27.0.6:40634 seq:8486084, ack:2608831141, flags:A, reason: TCP_INVALID_SEQUENCE, tcp_validate_incoming+0x126
[146.533728] TCP: 127.0.0.1:36338 -> 127.0.0.1:56100 seq:1110580666, ack:1951926207, flags:A, reason: TCP_INVALID_SEQUENCE, tcp_validate_incoming+0x126
[147.255946] TCP: 20.44.10.122:443 -> 192.168.255.10:42878 seq:2950381253, ack:211751623, flags:A, reason: NOT_SPECIFIED, tcp_rcv_state_process+0xe9

同样可以使用 man dropreason 命令来查看对应的丢包原因的详细解释。对于不支持 skb drop reason 特性的内核,该模式下将不会打印丢包原因字段,效果如下所示:

nettrace --drop
begin trace...
[2016.965295] TCP: 162.241.189.135:45432 -> 172.27.0.6:22 seq:133152310, ack:2529234288, flags:AR, tcp_v4_rcv+0x50
[2017.201315] TCP: 162.241.189.135:45432 -> 172.27.0.6:22 seq:133152310, ack:2529234288, flags:A, tcp_v4_do_rcv+0x70
[2019.041344] TCP: 176.58.124.134:37441 -> 172.27.0.6:443 seq:1160140493, ack:0, flags:S, tcp_v4_rcv+0x50
[2021.867340] TCP: 127.0.0.1:34936 -> 127.0.0.1:9999 seq:1309795878, ack:0, flags:S, tcp_v4_rcv+0x50
[2024.997146] TCP: 162.241.189.135:46756 -> 172.27.0.6:22 seq:1304582308, ack:1354418612, flags:AR, tcp_v4_rcv+0x50
[2025.235953] TCP: 162.241.189.135:46756 -> 172.27.0.6:22 seq:1304582308, ack:1354418612, flags:A, tcp_v4_do_rcv+0x70
[2025.235967] TCP: 162.241.189.135:46756 -> 172.27.0.6:22 seq:1304582308, ack:1354418612, flags:AP, tcp_v4_do_rcv+0x70

四、小结

nettrace 工具自上线 OpenCloud 社区以来,受到了业界的广泛关注,并在 OpenCloudOS 的网络故障诊断中得到了大量应用。特别是复杂的云原生网络环境中,nettrace 工具通过报文跟踪、网络诊断的方式为用户解决了多次疑难网络问题。

同时,nettrace 与 OpenCloudOS 进行了完美的兼容和适配,并上架了 OpenCloudOS 的软件仓库,使得 OpenCloudOS 用户可以很方便地直接使用 yum 命令来进行安装和使用。

本月,OpenCloudOS 源社区 2301 版本已发布,拥有更多技术新特性,欢迎开发者下载使用。OpenCloudOS ISO 下载地址(亦可选用腾讯云上V8即8.6最新版)

如果在使用过程中遇到技术问题,扫描下方二维码,加入社区用户群,即可了解 OpenCloudOS 最新动态,获取技术相关的支持,分享交流使用体验。

  • droptrace
  • OpenCloudOS release 页面
  • OpenCloudOS 社区官网

本文转载于网络 如有侵权请联系删除

相关文章

  • Maven Compile 编译报错

    首先在IDEA中使用Maven的插件中的Lifecycle进行compile指令,发现报错:Failedtoexecutegoalorg.apache.maven.plugins:maven-compiler-plugin:3.1:compile(default-compile)onprojectssm-utils:Fatalerrorcompiling 复制然后命令行运行mvncompile得到更详细的报错信息如下Failedtoexecutegoalorg.apache.maven.plugins:maven-compiler-plugin:3.1:compile(default-compile)onprojectssm-utils:Fatalerrorcompiling:错误:无效的目标发行版:1.11 复制回到pom.xml的文件中查看配置:<!--指定编码及版本--> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> &

  • HBase优化笔记

    HBase优化 JVM调优内存调优一般安装好的HBase集群,默认配置是给Master和RegionServer1G的内存,而Memstore默认占0.4,也就是400MB。显然RegionServer给的1G真的太少了。exportHBASE_MASTER_OPTS="$HBASE_MASTER_OPTS-Xms2g-Xmx2g" exportHBASE_REGIONSERVER_OPTS="$HBASE_REGIONSERVER_OPTS-Xms8g-Xmx8g"复制这里只是举例,并不是所有的集群都是这么配置。 ==要牢记至少留10%的内存给操作系统来进行必要的操作==如何给出一个合理的JVM内存大小设置,举一个ambari官方提供的例子吧。比如你现在有一台16GB的机器,上面有MapReduce服务、RegionServer和DataNode(这三位一般都是装在一起的),那么建议按照如下配置设置内存:2GB:留给系统进程。8GB:MapReduce服务。平均每1GB分配6个Mapslots+2个Reduceslots。4GB:HBase的R

  • 高阶函数及 map、reduce、filter 的实现

    博客地址:https://ainyi.com/85 2020开年国家经历了不少困难,最为凶猛的局势就是新型冠状病毒的蔓延,国务院最终决定春节假期延长至==2月2号==;公司决定3-7号在家用V**办公。10号正式在职场上班; 在这个看似漫无止境的春节假期中,在家宅着不出门就是对社会最好的贡献,那么一直待在家也确实无聊极致,索性学习学习、看看书吧,再学习学习JavaScript的函数吧 函数 函数是函数式编程的工作单元与中心。函数是任何可调用你且可通过()操作求值的表达式。 JavaScript函数有两个支柱性的重要特性:一等函数和高阶函数 一等函数就是最常见的,如: functionmultiplier(a,b){ returna*b } letsquare=function(x){ returnx*x } //lambda表达式(箭头函数) letsquare=x=>x*x复制 主要说说高阶函数 高阶函数 鉴于函数的行为与普通对象类似,其理所当然地可以作为其他函数的参数进行传递,或是由其他函数返回。这些函数则称为高阶函数。~JavaScript函数式编程指南p36~ 例如A

  • Spring Security(3):数

    核心处理流程1:当一个用户登录时,会先执行身份认证。 2:如果身份认证未通过,则会要求用户重新认证。3:如果身份认证通过,则会调用角色管理器判断它是否可以访问。这里如果我们需要使用数据库中数据进行身份认证,则需要自定义用户登录功能。SpringSecurity为我们提供的UsrtDetailsService接口。package org.springframework.security.core.userdetails; public interface UserDetailsService {     UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException; }复制UserDetails.javapackage org.springframework.security.core.userdetails; import java.io.Serializable; import java.util.Collection; import org.springframework.securit

  • 技术 | 从零开始,实现你的小程序

    从微信发布的小程序这样的应用形态中,才发现渲染Native(ReactNative,Weex)并不一定是最优的利用Web能力的解放。在这里我们不讨论微信小程序的应用实现,而是从零开始,让你来设计一个小程序的核心架构,该如何实现?通过分析微信小程序,我们大概需要实现哪些? 如图:通过观察,小程序的渲染与逻辑是分离开的,这一点上,我个人的判断是限制开发者的编写来达到程序体验的提升,既然分离,那么重点肯定就在通信上了,以iOS的角度来分析,UI的落地呈现使用了WKWebView,那么JS的落地就在JavaScriptCore上了,这两个东西是什么,还请自行了解。 链接:WKWebView:https://developer.apple.com/reference/webkit/wkwebviewJavaScriptCore:https://developer.apple.com/reference/javascriptcore通信的目的是让在WKWebView中渲染的视图可以和在JavaScriptCore中运行的逻辑可以“绑定”起来,这里重要的是如何定义通信的协议和数据结构,双方并理解数据

  • 简书上实现代码块的几种方式

    简书目前还没有插入代码块的选项,对it这一行的我们来说,为了给大家分享技术,代码片呈现的方式或多或少很有必要啦~~,下面,总结一下我知道的几种Markdown模式下在简书插入代码片的方式。准备工作:设置编辑模式为Markdown,然后保存,如图markdown.png方式一: 在你新建的文章中,当需要插入代码片段的时候,在英文状态下输入键盘tab键上的`键后,复制粘贴你的代码块即可。演示图演示,字符为```效果图效果方式二:在你新建的文章中,当需要插入代码片段的时候,利用pre标签,复制粘贴你的代码块到这标签中间即可。演示图pre演示效果图pre结果.png方式三: 在你新建的文章中,当需要插入代码片段的时候,利用code标签,复制粘贴你的代码块到这标签中间即可。演示图code演示效果图code效果希望简书能早日加上代码片的选项,此篇总结仅供大家参考,希望对大家有帮助!网上如果还有其他方式的话,希望大家可以来这里告诉我,不胜感激!

  • 手把手教你全家桶之React(一)

    前言最近项目用到react,其实前年我就开始接触react,时光匆匆,一直没有时间整理下来(太懒啦)!如今再次用到,称工作间隙,对全家桶做一次总结,项目源码地址。废话不多说,上码。创建一个文件目录并初始化package.jsonmkdirreact-Buckets npminit复制填好相关信息如图安装webpack需要有全局安装哦,不然一会用webpack编译时会报错的关于装依赖加入package.json时,加--save-dev表示开发环境要用的依赖,如果加-save表示生产环境依然要用的依赖。 npminstall--save-devwebpack手动添加webpack配置文件 touchwebpack.dev.config.js配置文件 varpath=require('path');module.exports={//入口文件指向src/index.jsentry:path.join(__dirname,'src/index.js'),//打包后的文件到当前目录下的dist文件夹,名为bundle.jsoutput:{path:pat

  • 【Nature 封面论文】随机人工智能群体控制,提高人类协作效率

    【新智元导读】噪音,或过程中无意义的信息通常被视为导致麻烦的原因。但最新研究发现,将制造噪音(也即故意做出不协调行为或“捣乱”)的bot或AI程序放置在人类网络中的特定位置时,反而可以提高人类协作效率,解决从天文、考古乃至量子问题。不可预测的人工智能(AI)听起来可不是件好事。但一项新的研究表明,随机运行的计算机可以促使人类更好地协调行动,更快地完成任务。该方法也可以用于缓解交通流量、改善企业战略,甚至可能改善或巩固婚姻关系。如果想要把一个项目做得好,那么单是项目成员之间能够和谐共处是不够的,他们还需要有一个共同的执行目标。解决这个问题的方法有两个:一是自上而下的控制,也就是有一个人做领导或设置一个管理机构,告诉其他人都做什么;另一个更直观的做法是让项目成员随意选择自己最喜欢做的事情。根据所谓的“复杂系统理论”,后面这种方法最终将使整个系统融合一起。例如,要是两个人在谈判中陷入僵局,其中一人如果提出一个大胆乃至疯狂的方案,可能还会使谈判得出一个结果。用简单巧妙的游戏验证噪音是否有益于协作为了弄清楚随机AI能否帮助人类协调工作,耶鲁大学社会学家和系统工程师HirokazuShirado以

  • 【建议收藏】Android中高级大厂面试秘籍,为你备战2021金三银四,直通大厂

    一眨眼又到年底了,每到这个时候,我们都会慢慢反思,这一年都做了什么?有什么进步?年初的计划都实现了吗?明年年初有跳槽的底气了吗? 况且2020年我们经历了新冠疫情的洗礼,很多程序员都经历了失业,找工作的恐慌。导致今年的互联网环境太差,需要自己有足够的知识储备,才能够应对这凌冽的寒风。 本文主要是整理了中高级Android需要会的(或者说面试被频繁问到的内容),主要作为参考大纲,之后会陆续更新每个详细部分,供大家参考,互相学习。 一、计算机网络部分 1.网页中输入url,到渲染整个界面的整个过程,以及中间用了什么协议?1)过程分析:主要分为三步 DNS解析。用户输入url后,需要通过DNS解析找到域名对应的ip地址,有了ip地址才能找到服务器端。首先会查找浏览器缓存,是否有对应的dns记录。再继续按照操作系统缓存—路由缓存—isp的dns服务器—根服务器的顺序进行DNS解析,直到找到对应的ip地址。客户端(浏览器)和服务器交互。浏览器根据解析到的ip地址和端口号发起HTTP请求,请求到达传输层,这里也就是TCP层,开始三次握手建立连接。服务器收到请求后,发送相应报文给客户端(浏览器),客

  • Java垃圾回收(GC)机制详解

    一、为什么需要垃圾回收   如果不进行垃圾回收,内存迟早都会被消耗空,因为我们在不断的分配内存空间而不进行回收。除非内存无限大,我们可以任性的分配而不回收,但是事实并非如此。所以,垃圾回收是必须的。 二、哪些内存需要回收? 哪些内存需要回收是垃圾回收机制第一个要考虑的问题,所谓“要回收的垃圾”无非就是那些不可能再被任何途径使用的对象。那么如何找到这些对象? 1、引用计数法 这个算法的实现是,给对象中添加一个引用计数器,每当一个地方引用这个对象时,计数器值+1;当引用失效时,计数器值-1。任何时刻计数值为0的对象就是不可能再被使用的。这种算法使用场景很多,但是,Java中却没有使用这种算法,因为这种算法很难解决对象之间相互引用的情况。看一段代码: /** *虚拟机参数:-verbose:gc */ publicclassReferenceCountingGC { privateObjectinstance=null; privatestaticfinalint_1MB=1024*1024; /**这个成员属性唯一的作用就是占用一点内存*/ privatebyte[]bigSize=

  • CS:APP Chapter 4 Y86-64处理器设计-读书笔记

    4处理器体系结构 第四章的目标是设计一个Y86-64的处理器,并运行设计好的Y86-64的指令集。 什么是指令集 指令集ISA,也就是处理器可以处理的指令的集合,Y86-64的指令是简化版的X86-64指令,他把许多指令都细化了,例如movq拆分成了多个irmovq,rrmovq等等,直接在指令中写清楚两个操作数的来源以及他们的转移方向。 简化的指令集也让处理器的设计更加简洁和方便,本章主要是设计顺序处理器与流水线处理器,并不涉及乱序处理器的设计。而顺序处理器也分为两个版本SEQ初始版本以及SEQ+版本,流水线处理器分为PIPE-和PIPE版本,也就是说一共有四种不同的处理器版本。 什么是顺序处理器 所有指令都是串行执行的,执行完上一条指令然后再执行下一条。 优点是设计比较简单,不需要考虑数据冒险,控制冒险之类的问题。 缺点是对处理器的利用效率比较低下,因为一个指令分为多个阶段,由于是顺序执行,导致会让一部分的阶段处理硬件空闲,所以可以进行一些改进。 什么是流水线处理器 这里我们设计的流水线处理器是将顺序执行的指令处理阶段拆分成多个不同阶段,使得处理器在同一时间内,不同阶段可以处理不同

  • Nginx Configuration for windows

    Configurationforwindows nginx.conf 在文件尾部添加以下配置 stream{ includestreams/*.conf; } 复制 *.conf 在Nginx安装目录下,创建streams目录,然后在streams中添加需要的conf配置 \conf\streams\mssql.conf upstreammssql{ #hash$remote_addrconsistent; #server127.0.0.1:49905max_fails=3fail_timeout=30s; server192.168.10.107:1433max_fails=3fail_timeout=30s; } server{ listen1433; proxy_connect_timeout1s; proxy_timeout3s; proxy_passmssql; } 复制

  • Qt-鼠标事件及实例

    1.重写窗体的鼠标事件方法时,需要在窗体构造函数中添加如下代码,设置窗体追踪鼠标 setMouseTracking(true);复制 2.mousePressEvent(QMouseEvent*e):鼠标按下事件响应函数 void项目名::mousePressEvent(QMouseEvent*e) { if(Qt::LeftButton==e->button())//左键 { } elseif(Qt::RightButton==e->button())//右键 { } elseif(Qt::MidButton==e->button())//中键 { } }复制  

  • ARC135 简要题解

    链接 D 首先,能操作的位置有\((n-1)(m-1)\)个,同时我们知道左上角\((n-1)(m-1)\)个格子可以取到任意值,因此这个问题的解域就是一个\((n-1)(m-1)\)个变元的线性空间,理论上可以找到\(n+m-1\)条线性无关的恒等式。 仔细观察可以发现,每次操作同一行或同一列要么没有一个格子被操作,否则被操作的两个格子必定相邻也即在改行或该列的奇偶性相反,于是可以得到操作后的矩阵\(b\)与原矩阵\(a\)的关系: \[\foralli,\sum\limits_{j=1}^m(-1)^jb_{i,j}=\sum\limits_{j=1}^m(-1)^ja_{i,j} \]\[\forallj,\sum\limits_{i=1}^n(-1)^ib_{i,j}=\sum\limits_{i=1}^n(-1)^ia_{i,j} \]注意,对行的限制全部加起来和对列的限制全部加起来是线性相关的,因此这里就是\(n+m-1\)条限制,充分性也很好证。 观察这个式子的实质,限定了每行每列的带权和为不变量,这里同一个元素在行列当中带权可能不同,为了简化可以将限制改为: \[\fo

  • SpringBoot入门-集成mybatis(四)

    pom.xml <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> <relativePath/><!--lookupparentfromrepository--> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> <

  • 阿里云 oss 上传文件,js直传,.net 签名,回调

    后台签名 添加引用   stringdir=string.Format("{0:yyyy-MM-dd}",date)+"/"; OssClientclient=newOssClient(Endpoint,AccessKeyId,AccessKeySecret); DateTimeex=DateTime.Now.AddSeconds(1800); PolicyConditionspolicyConds=newPolicyConditions(); policyConds.AddConditionItem(PolicyConditions.CondContentLengthRange,0L,1048576000L); policyConds.AddConditionItem(MatchMode.StartWith,PolicyConditions.CondKey,dir); stringpostPolicy=client.GeneratePostPolicy(ex,policyConds); byte[]binaryData=Encoding.UTF8.GetByte

  • javascript数据类型,定义方法,(工厂模式及闭包的应用)

    js数据类型分为两大类:一 值类型                     二引用类型 一值类型 string number booleannullundefined   eg:    varstr='aaa' varnum=123 varbool=true varn=null varund=undefined console.log(typeofstr)//string console.log(typeofnum)//number console.log(typeofbool)//boolean console.log(typeofn)//object console.log(typeofund)//undefined复制   二引用类型 Array &n

  • JS编程练习:封装insertAfter函数(功能类似于系统insertBefor)

    那么insertAfter()要实现的功能:在指定的子节点后面插入新的子节点; 1<body> 2<div> 3<p></p> 4<span></span> 5<em></em> 6<i></i> 7</div> 8 9<scripttype="text/javascript"> 10Element.prototype.insertAfter=function(targetNode,afterNode){ 11varbeforNode=afterNode.nextElementSibling; 12 13if(beforNode==null){ 14this.appendChild(targetNode); 15}else{ 16this.insertBefore(targetNode,beforNode); 17} 18} 19 20vardiv=document.getElementsByTagName('div')[0]; 21va

  • 7 张图解 CrashLoopBackOff,如何发现问题并解决它?

    一、什么是CrashLoopBackOffCrashLoopBackOff是一种Kubernetes状态,表示Pod中发生的重启循环:Pod中的容器已启动,但一遍又一遍的崩溃然后又重新启动。 Kubernetes将在重新启动之间等待越来越长的BackOff时间,以便您有机会修复错误。因此,CrashLoopBackOff本身并不是一个错误,而是表明发生了一个错误,导致Pod无法正常启动。   Pod在Running、Failed和Waiting之间循环 请注意,它重新启动的原因是因为它restartPolicy设置为Always(默认情况下)或OnFailure,然后kubelet读取此配置并重新启动Pod中的容器并导致循环。这种行为实际上很有用,因为这为丢失的资源完成加载提供了一些时间,也为我们检测问题和调试提供了一些时间,稍后会详细介绍。 这解释了CrashLoop部分,但是BackOff时间呢?基本上,这是重启之间的指数延迟(10秒、20秒、40秒……),上限为5分钟。当Pod状态显示CrashLoopBackOff时,表示它当前正在等待指示的时间,然后再重新启动Pod

  • LeetCode 86. 链表 Partition List

    LeetCode86.链表PartitionListLeetCodeGivenalinkedlistandavaluex,partitionitsuchthatallnodeslessthanxcomebeforenodesgreaterthanorequaltox. Youshouldpreservetheoriginalrelativeorderofthenodesineachofthetwopartitions. Example: Input:head=1->4->3->2->5->2,x=3 Output:1->2->2->4->3->5 代码: classSolution{ publicListNodepartition(ListNodehead,intx){ if(head==null)returnnull; ListNodefront=null; ListNodefrontFirst=null; ListNodeback=null; ListNodebackFirst=null; while(head!=nu

  • 如何在datetime.strptime中添加时区

    来源:http://tieba.baidu.com/p/3367509493 time_1=datetime.datetime.now(pytz.timezone(*Asia/Shanghai*))time_2=datetime.datetime.strptime(*2014-10-2312:00:00*,"%Y-%m-%d%H:%M:%S")time_1生成的是包含时区的offset-aware型的datetime,time_2好像是不包含时区信息的offset_naive型的datetime。 time_1=datetime.datetime.strptime("2014-10-112:00:00","%Y-%m-%d%H:%M:%S")如此得到的time_1是offset-naive类型的datetime,time_1=time_1.replace(tzinfo=pytz.timezone(*Asia/Shanghai*))如此即可转换为offset-aware类型的datetime

相关推荐

推荐阅读