K8S之网络深度剖析(一)(持续更新ing)
K8S之网络深度剖析
一 、关于K8S的网络模型
在K8s的世界上,IP是以Pod为单位进行分配的。一个Pod内部的所有容器共享一个网络堆栈(相当于一个网络命名空间,它们的IP地址、网络设备、配置等都是共享的)。按照这个网络原则抽象出来的为每个Pod都设置一个IP地址的模型也被称作为IP-per-Pod模型。
Kubernetes对集群网络有如下要求:
- 所有容器都可以在不用NAT的方式下同别的容器通讯。
- 所有节点都可以在不用NAT的方式下同别的容器通讯。
- 容器的地址和别人看到的地址是同一个地址。
二、Docker 网络
Docker 使用到的与Linux网络有关的主要技术:
- 网络命名空间(Network Namespace)
- Veth 设备对
- 网桥
- Iptables
- 路由
2.1 网络命名空间
为了支持网络协议栈中的多个实例,Linux 2.6.24内核版本的网络协议栈中引入了网络命名空间,这些独立的协议栈被隔离到不用的命名空间中。处于不用命名空间中的网络栈是完全隔离的,彼此之间无法通讯,就好像两个"平行宇宙"。通过对于网络资源的隔离,就能在一个宿主机上虚拟多个不同的网络环境。Docker 正是利用了网络的命名空间特性,实现了不同容器之间的网络隔离。
在Linux的网络命名空间中可以有自己独立的路由表,及独立的Iptables设置来提供包转发、NAT、及IP包过滤等功能。
为了隔离出独立的协议栈,需要纳入命名空间的元素有进程、套接字、网络设备等。进程创建的套接字必须属于某个网络命名空间,套接字的操作也必须在命名空间中进行。同样,网络设备也必须属于某个命名空间。因为网络设备属于公共资源,所以可以通过修改属性实现在命名空间之间移动。当然,是否允许移动,和设备的特征有关系。
2.1.1 网络命名空间的实现
Linux的网络协议栈是十分复杂的,为了支持独立的协议栈,相关的这些全局变量都必须被修改为协议栈私有,最好的办法就是让这些全局变量成为一个 Net NameSpace变量的成员,然后修改协议栈的函数调用,在加入一个NameSpace的参数,这就是Linux 实现网络命名空间的核心。
同时,为了保证对已经开发的应用程序,以及内核代码的兼容性,内核代码隐式地使用了网络命名空间中的变量,程序如果没有对网络命名空间有特殊需求,就不需要编写额外的代码,网络命名空间对应用程序而言是透明的。
在建立了新的网络命名空间,并将某个进程关联到这个网络命名空间后,就出现了如下图的数据结构,所有的网络栈变量都没放到了私有的命名空间,和其他进程组并不冲突。

上图来自网络命名空间深度好文
在新生成的理由命名空间中,只有回环设备,(名为"lo" 且是停止状态),其他设备默认都不存在,如果我们需要,则要一一手工建立,Docker容器中的各类网络栈设备都是Docker Daemon 在启动时自动创建和配置的。
所有的网络设备(物理的或虚拟接口、桥等在内核里都叫做Net Device)都只能属于一个命名空间、当然,物理设备(连接实际硬件的设备)通常只能关联到root 这个命名空间。虚拟的网络设备(虚拟的以太网接口或者虚拟网口对)则可以被关联到一个给定的命名空间,而且可以在这些命名空间中移动。
前面提到,由于网络命名空间代表的是一个独立的协议栈,所以它们之间是相互隔离的,彼此无法通讯,在协议栈内部都看不到对方, 那么有没有办法打破这种限制,让处于不同网络命名空间的网络互相通讯,甚至和外部的网络通讯呢? 答案就是“有”,应用 “Veth设备对即可”, Veth 设备对一个重要的作用就是打通互相看不到的协议栈的协议栈之间的壁垒,他就像一条管子,一端连着这个网络命名空间的协议栈,一端连着另一个网络命名空间协议栈,所以想要在两个命名空间之间通讯,就必须有一个 Veth 设备对。
2.1.2 网络命名空间的操作
我们可以使用 Linux iproute2 系列配置工具中的IP 命令来操作网络命名空间,注意: 这个命令需要使用root 用户来执行。
安装:
# alphine
apk add iproute2# ubuntu
sudo apt install iproute2# centos
sudo yum -y install iproute2
创建一个网络命名空间:
ip netns add <name>
创建一个名为test1 的网络命名空间
ip netns add test1
在命名空间中执行命令:
ip netns exec <name> <command>
在命名空间test1 执行 ip a s 命令
root@test:~# ip netns exec test1 ip a s
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
也可以先通过切换到对应的网络命名空间,执行各种命令:
ip netns exec <name> bash
执行 exit 或Ctrl +d 退出当前网络命名空间。
上面的操作相当于网络命名空间进行了切换,文件系统还是当前,在Docker 中,其实也做了类似操作,并且将文件系统通过chroot 进行了切换,通过cgroup 对资源进行了隔离。
2.1.3 网络命名空间的实用技巧
操作系统网络命名空间的一些技巧如下:
我们可以在不同的网络命名空间之间转移设备,列如下面会提到的Veth 设备对的转移,因为一个设备只能属于一个命名空间,所以转移后在这个网络命名空间内就看不到这个设备了,具体哪些设备能被转移到不同的网络命名空间, 在设备里面有一个属性: NETIF_F_NETNS_LOCAL,如果这个属性为 on,就不能被转移到其他命名空间中。Veth 设备属于可转移设备,而其他设备如,lo 设备,vxlan,ppp 设备,bridge设备等都是不可转移的。将无法转移的设备移动到别的命名空间时,会得到无效的参数错误提示。
root@test1:~# ip link set lo netns test1
RTNETLINK answers: Invalid argument
如何知道这些设备是否可以转移呢? 可以使用ethtool 工具查看:
root@test1:~# apt install ethtool
root@test1:~# ethtool -k docker0 | grep netns
netns-local: on [fixed]
root@test1:~# ethtool -k veth276e185 | grep netns
netns-local: off [fixed]
2.1.4 使用nsenter 工具来切换网络命名空间
执行lsns 查看当前主机的namespace
root@test1:~# lsns NS TYPE NPROCS PID USER COMMAND
4026531834 time 314 1 root /lib/systemd/systemd --system --deserialize 35 splash
4026531835 cgroup 312 1 root /lib/systemd/systemd --system --deserialize 35 splash
4026531836 pid 312 1 root /lib/systemd/systemd --system --deserialize 35 splash
4026531837 user 313 1 root /lib/systemd/systemd --system --deserialize 35 splash
4026531838 uts 308 1 root /lib/systemd/systemd --system --deserialize 35 splash
4026531839 ipc 312 1 root /lib/systemd/systemd --system --deserialize 35 splash
4026531840 net 308 1 root /lib/systemd/systemd --system --deserialize 35 splash
4026531841 mnt 297 1 root /lib/systemd/systemd --system --deserialize 35 splash
4026531862 mnt 1 62 root kdevtmpfs
4026532391 mnt 1 296 root /lib/systemd/systemd-udevd
4026532392 uts 1 296 root /lib/systemd/systemd-udevd
4026532421 mnt 1 471 systemd-resolve /lib/systemd/systemd-resolved
4026532422 mnt 1 472 systemd-timesync /lib/systemd/systemd-timesyncd
4026532423 mnt 1 2540333 systemd-oom /lib/systemd/systemd-oomd
4026532424 uts 1 2540333 systemd-oom /lib/systemd/systemd-oomd
4026532425 uts 1 472 systemd-timesync /lib/systemd/systemd-timesyncd
4026532426 mnt 1 10214 root /lib/systemd/systemd-logind
4026532427 uts 1 10214 root /lib/systemd/systemd-logind
4026532430 mnt 1 995086 fwupd-refresh /usr/bin/fwupdmgr refresh
4026532448 mnt 1 2435299 root sh
4026532449 uts 1 2435299 root sh
4026532450 ipc 1 2435299 root sh
4026532451 pid 1 2435299 root sh
4026532452 net 1 2435299 root sh
4026532483 mnt 1 30455 root /usr/libexec/accounts-daemon
4026532484 net 1 30455 root /usr/libexec/accounts-daemon
4026532485 net 1 10330 rtkit /usr/libexec/rtkit-daemon
4026532539 mnt 1 525 root /usr/sbin/irqbalance --foreground
4026532542 mnt 2 1469053 root /snap/snapd-desktop-integration/157/usr/bin/snapd-desktop-integration
4026532545 mnt 1 10629 root /usr/libexec/upowerd
4026532546 mnt 1 10631 root /usr/libexec/power-profiles-daemon
4026532547 user 1 10629 root /usr/libexec/upowerd
4026532549 mnt 1 10715 root /usr/sbin/ModemManager
4026532628 cgroup 1 2435299 root sh
4026532631 mnt 1 380128 root sh
4026532632 uts 1 380128 root sh
4026532633 ipc 1 380128 root sh
4026532634 pid 1 380128 root sh
4026532635 net 3 380128 root sh
4026532665 mnt 1 11076 colord /usr/libexec/colord
4026532692 cgroup 1 380128 root sh
这里可以看到pid 380128 容器的namespace,主要为以下几个类型:
- mount
- uts
- ipc
- pid
- network
- user
我们可以通过如下的方法对容器进行抓包:
获取容器的Pid
root@test1:~# docker inspect -f "{{.State.Pid}}" a19dee489a47
380128
可以看到有多个namespace
ls -l /proc/380128/ns/
total 0
lrwxrwxrwx 1 root root 0 7月 2 22:19 cgroup -> 'cgroup:[4026532692]'
lrwxrwxrwx 1 root root 0 7月 2 22:19 ipc -> 'ipc:[4026532633]'
lrwxrwxrwx 1 root root 0 7月 2 22:19 mnt -> 'mnt:[4026532631]'
lrwxrwxrwx 1 root root 0 6月 28 15:56 net -> 'net:[4026532635]'
lrwxrwxrwx 1 root root 0 7月 2 22:19 pid -> 'pid:[4026532634]'
lrwxrwxrwx 1 root root 0 7月 2 22:21 pid_for_children -> 'pid:[4026532634]'
lrwxrwxrwx 1 root root 0 7月 2 22:19 time -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0 7月 2 22:21 time_for_children -> 'time:[4026531834]'
lrwxrwxrwx 1 root root 0 7月 2 22:19 user -> 'user:[4026531837]'
lrwxrwxrwx 1 root root 0 7月 2 22:19 uts -> 'uts:[4026532632]'
进入对应Pid 的Network Namespace
root@test1:~# nsenter -t 380128 -n
root@test1:~# ip a s
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft foreverinet6 ::1/128 scope host valid_lft forever preferred_lft forever
76: eth0@if77: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0valid_lft forever preferred_lft forever
nsenter 工具详解及 namespace 背景
2.2 Veth 设备对
Veth设备对的出现是为了解决不同的网络命名空间之间通讯,利用它可以将两个网络命名空间进行连接,由于要连接多个网络命名空间,所以Veth 设备基本都是成对出现的,很像一对以太网卡,并且中间有一根直连的网线。既然是一对网卡,我们将其中一端称之为另一端的peer。在veth 另一端发送数据时,它会将数据直接发送到另一端,并触发另一端的接收操作。

2.2.1 Veth 设备对的操作命令
创建veth设备对:
root@test1:~# ip link add veth0 type veth peer name veth1
创建后,使用ip link show 查看所有网络接口
root@test1:~# ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp6s18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000link/ether 2e:8d:36:b4:77:40 brd ff:ff:ff:ff:ff:ff
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default link/ether 02:42:ae:2b:65:9a brd ff:ff:ff:ff:ff:ff
69: veth9b5ea1d@if68: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default link/ether 7e:29:46:4f:52:9e brd ff:ff:ff:ff:ff:ff link-netnsid 0
77: veth276e185@if76: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default link/ether 0e:73:3e:73:b5:b3 brd ff:ff:ff:ff:ff:ff link-netnsid 1
82: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ether 6e:3b:cf:dc:10:a6 brd ff:ff:ff:ff:ff:ff
83: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ether da:61:bf:d0:bf:25 brd ff:ff:ff:ff:ff:ff
可以看到,上面的命令生成了两个网络接口 一个是veth1 他的peer 是veth0,现在两个设备都在默认的命名空间,我们将其中一个设备加入到另一个命名空间
root@test1:~# ip link set veth1 netns test1
这时在外面看两个设备的情况
root@test1:~# ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: enp6s18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000link/ether 2e:8d:36:b4:77:40 brd ff:ff:ff:ff:ff:ff
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default link/ether 02:42:ae:2b:65:9a brd ff:ff:ff:ff:ff:ff
69: veth9b5ea1d@if68: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default link/ether 7e:29:46:4f:52:9e brd ff:ff:ff:ff:ff:ff link-netnsid 0
77: veth276e185@if76: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP mode DEFAULT group default link/ether 0e:73:3e:73:b5:b3 brd ff:ff:ff:ff:ff:ff link-netnsid 1
83: veth0@if82: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ether da:61:bf:d0:bf:25 brd ff:ff:ff:ff:ff:ff link-netns test1
只剩下一个veth 设备了,已经看不到另一个设备了,另一个设备已经被转移到了test1的网络命名空间中了。
在test1的网络命名空间中可以看到veth1 设备,符合预期
root@test1:~# ip netns exec test1 ip link show
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
82: veth1@if83: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000link/ether 6e:3b:cf:dc:10:a6 brd ff:ff:ff:ff:ff:ff link-netnsid 0
现在我们看到,两个不用的命名空间中有一个veth 设备,在Docker的实现里面,它除了将veth 放入到容器内,还将它的名字改成了eth0
现在我们给他分配一个IP地址,让他们可以通讯
root@test1:~# ip netns exec test1 ip addr add 10.1.1.1/24 dev veth1
root@test1:~# ip addr add 10.1.1.2/24 dev veth0
在启动他们:
root@test1:~# ip netns exec test1 ip link set dev veth1 up
root@test1:~# ip link set dev veth0 up
现在两个网络命名空间可以互相通讯了
root@test1:~# ping 10.1.1.1
PING 10.1.1.1 (10.1.1.1) 56(84) bytes of data.
64 bytes from 10.1.1.1: icmp_seq=1 ttl=64 time=0.074 ms
64 bytes from 10.1.1.1: icmp_seq=2 ttl=64 time=0.064 ms
root@test1:~# ip netns exec test1 ping 10.1.1.2
PING 10.1.1.2 (10.1.1.2) 56(84) bytes of data.
64 bytes from 10.1.1.2: icmp_seq=1 ttl=64 time=0.130 ms
在Docker内部,Veth 设备是连通容器与宿主机网络的主要设备。
2.2.2 veth 设备如何查看对端
一旦将veth 设备对的另一端放入另一个命名空间,在本命名空间就看不到它了,那我们如何知道设备的对端在哪里呢?可以使用ethtool 工具来查看(当网络命名空间较多时,这个也不太容器)。
首先,在命名空间test1中查询Veth设备对端接口在设备列表中的序列号:
root@test1:~# ip netns exec test1 ethtool -S veth1
NIC statistics:peer_ifindex: 83
获取另一端的接口序列号是83,我们在查看序列号83是什么设备
root@test1:~# ip link | grep 83
83: veth0@if82: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP mode DEFAULT group default qlen 1000
veth 参考资料
2.3 网桥
Linux可以支持多个不同的网络,他们之间可以相互通讯,如何将这些网络连接起来,并实现各网络中主机的相互通讯? 可以用网桥,网桥是一个二层的虚拟网络设备,把若干个网络接口”连接“起来,使得网络接口之间得报文能够互相转发,网桥能够解析收发得报文,读取目标MAC地址得信息,和自己记录得Mac表结合,来决策报文的转发目标网络接口,为了实现这些功能,网桥会学习源MAC地址(二层网桥转发得依据就是MAC地址),在转发报文时,网桥只需要向特定得网口进行转发,来避免不必要得网络交互,如果他遇到一个自己从未学习过得地址,就无法知道这个报文需要转发给哪个网络接口,就将报文广播给所有得网络接口(报文来源得接口除外)。
在实际得网络中,网络拓扑不可能永远不变,设备如果被移动到另一个端口上,却没有发送任何数据,网桥设备就无法感知这个变化,网桥还是向原来得端口转发数据时,这种情况数据就会丢失。所以网桥还要对学习到的MAC地址表加上超时时间(默认为5min)。如果网桥收到了对应带你看MAC地址回发的数据包,则重置超时时间,否则过了超时时间后,就认为设备已经不再那个端口上,它就会重新发送广播。
Linux内核支持网口的桥接(目前只支持以太网接口),但是与单纯的交换机不同,交换机只是一个二层设备,对于接收到的报文,要么转发,要么丢弃.运行着Linux 内核的机器本身就是一台主机,有可能时网络报文的目的地,其收到的报文除了转发和丢弃,还有可能会被送到网络协议栈的上层(网络层),从而被自己(这台主机本身的协议栈)消化,所以我们既可以把他当作一个二层设备,也可以当作一个三层设备。
2.3.1 Linux 网桥的实现
Linux 内核是通过一个虚拟的网桥设备(Net Device)来实现桥接的。这个虚拟设备可以绑定若干个以太网接口设备,从而将它们桥接起来, Net Device 和普通的设备,最明显的特征是它还可以有一个IP地址。

网桥的参考资料
如图所示,网桥设备绑定了 veth0 ,veth1。对于网络协议栈的上层来说,只看得到网桥就可以。因为桥接实在数据链路层实现的,上层不需要关心桥接的细节,所以协议栈上层需要发送的报文被送到 网桥,网桥设备判断报文是被转发到 veth0还是 veth1,或者两者皆转发,反过来从eth0 或从eth1 接收到的报文被提交给网桥的,在这里会判断应该被转发、丢弃、还是协议栈上层。
而有时,veth0,veth1 也可能作为报文的源地址或者目的地址,直接参与报文的发送与接收,从而绕过网桥
2.3.2 网桥的常用操作命令
Docker自动完成了对网桥的创建和维护。为了进一步理解网桥,下面举几个常用的网桥的例子,对网桥进行手工操作:
bridge-utils包中的 brctl 用来管理以太网桥,在内核中建立、维护、检查网桥配置。一个网桥一般用来连接多个不同的网络,这样这些不同的网络就可以像一个网络那样进行通讯。
网桥是一种在链路层实现中继,对帧进行转发的技术,根据MAC分区块,可隔离碰撞,将网络的多个网段在数据链路层连接起来的网络设备。网桥工作在数据链路层,将两个LAN连起来,根据MAC地址来转发帧,可以看作一个“底层的路由器”。
在网桥上每个以太网连接可以对应到一个物理接口,这些以太网接口组合成一个大的逻辑的接口,这个逻辑接口对应于桥接网络。
root@test1:~# apt-get install bridge-utils -y
相关文章:
K8S之网络深度剖析(一)(持续更新ing)
K8S之网络深度剖析 一 、关于K8S的网络模型 在K8s的世界上,IP是以Pod为单位进行分配的。一个Pod内部的所有容器共享一个网络堆栈(相当于一个网络命名空间,它们的IP地址、网络设备、配置等都是共享的)。按照这个网络原则抽象出来的为每个Pod都设置一个IP地址的模型也被称作为I…...
Land survey boundary report (template)
Land survey boundary report (template) 土地勘测定界报告(模板).doc...
[数据集][目标检测]婴儿状态睡觉哭泣检测数据集VOC+YOLO格式7109张3类别
数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):7109 标注数量(xml文件个数):7109 标注数量(txt文件个数):7109 标注…...
深入解析 MySQL 的 SHOW FULL PROCESSLIST
在数据库管理中,监控和理解数据库进程是至关重要的。MySQL 提供了 SHOW PROCESSLIST 命令,它允许管理员查看当前所有活动线程的列表,包括它们的状态、执行的命令、消耗的资源等。这不仅帮助我们了解数据库的运行情况,还可以用于性…...
IPsec连接 和 SSL连接
Psec和SSL连接是两种用于保障网络通信安全的技术 IPsec 通常用于连通两个局域网,主要是网对网的连接,如分支机构与总部之间,或者本地IDC与云端VPC的子网连接。适合站点间的稳定通讯需求以及对网络层安全有严格要求的场合。要求两端有固定的网…...
Redis【超详细】
Redis 是一个基于内存的key-value结构的数据库 一、redis的安装 1.1、安装步骤 1)安装Redis依赖 Redis是基于c语言编写的,因此需要安装对应的gcc环境 yum install -y gcc tcl 2)进入/usr/local/src/目录上传并解压安装包 解压…...
通过ip获取用户位置信息以及地区时间
项目需要获取用户得位置信息以及地区时间,因为第一次搞,以防还有下次,特此记录 1.首先就是显得拿到用户得ip地址 先上代码: public boolean checkIp(String ip) {return null ip || ip.isEmpty() || "unknown".equa…...
pytest-yaml-sanmu(七):使用fixture返回值
fixture 是 pytest 中非常重要的功能,大部分项目都可能会用到 fixture。 pytest 的内置标记 usefixtures 可以帮助用例自动的使用 fixture 1. 创建 fixture pytest 中的 fixtures 大致有两个用途 在用例执行之前、执行之后,自动的执行 通过 fixture …...
2024最全软件测试面试八股文(答案+文档+视频讲解)
Part1 1、你的测试职业发展是什么? 测试经验越多,测试能力越高。所以我的职业发展是需要时间积累的,一步步向着高级测试工程师奔去。而且我也有初步的职业规划,前3年积累测试经验,按如何做好测试工程师的要点去要求自…...
EasyBoss ERP移动端上线数据分析模块,随时查Shopee/TikTok本土店数据
前段时间,EasyBoss ERP出了个超酷炫的数字大屏功能,广受好评。 但是也有老板说,电脑端看数据不够方便啊,你们EasyBoss有本事上个手机就能看数据的功能啊! 说干就干,直接满足你们的需求! 于是在…...
机器学习与AI大数据的融合:开启智能新时代
在当今这个信息爆炸的时代,大数据和人工智能(AI)已经成为推动社会进步的强大引擎。作为AI核心技术之一的机器学习(Machine Learning, ML),与大数据的深度融合正引领着一场前所未有的科技革命,不…...
视频监控业务平台LntonCVS国标视频综合管理平台功能及技术优势
随着安防行业的快速进步,传统的视频监控平台正在与先进的技术和互联网技术融合,包括5G通信、GIS、大数据、云计算、边缘计算、AI识别、智能分析和视频直播等。这些技术的整合形成了综合性视频监控管理平台,具备集中管理、多级联网共享、互联互…...
Python面试宝典第6题:有效的括号
题目 给定一个只包括 (、)、{、}、[、] 这些字符的字符串,判断该字符串是否有效。有效字符串需要满足以下的条件。 1、左括号必须用相同类型的右括号闭合。 2、左括号必须以正确的顺序闭合。 3、每个右括号都有一个对应的相同类型的左括号。 注意:空字符…...
Windows上使用Navicat连接ubuntu上的mysql8报错:10061和1130
问题一:can’t connect to mysql server on ‘192.168.xxx.xxx’(10061) 解决: sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf,bind-address绑定了登陆的IP,把这两行代码注释掉,然后重启mysql。 问题二:1…...
Feign远程调用,请求头丢失情况
现象 解决方案 import feign.RequestInterceptor; import feign.RequestTemplate; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.context.request.RequestContextHolde…...
Windows 11 安装 安卓子系统 (WSA)
How to Install Windows Subsystem for Android (WSA) on Windows 11 新手教程:如何安装Windows 11 安卓子系统 说明 Windows Subsystem for Android 或 WSA 是由 Hyper-V 提供支持的虚拟机,可在 Windows 11 操作系统上运行 Android 应用程序。虽然它需…...
CD4017 – 带解码输出的十进制计数器
CD4017 IC 是一个十进制计数器,它有 10 个输出,分别代表 0 到 9 的数字。计数器在(14号引脚)每个时钟脉冲上升时增加 1。计数器达到 9 后,它会在下一个时钟脉冲时从 0 重新开始。 引脚名称管脚 #类型描述VD…...
Spring Boot 文件上传和下载指南:从基础到进阶
文章目录 引言1. 环境配置2. 文件上传2.1 配置文件上传路径2.2 创建上传服务2.3 创建上传控制器 3. 文件下载3.1 创建下载服务3.2 创建下载控制器 4. 前端页面4.1 文件上传页面4.2 文件下载页面 5. 技术分析结论 🎉欢迎来到SpringBoot框架学习专栏~ ☆* o(≧▽≦)o …...
Windows Server 2019部署网络负载均衡NLB服务的详细操作步骤
部署前准备 首先需要准备两台Windows Server 2019服务器,虚拟机创建请参考 VMware Workstation安装Windows Server2019系统详细操作步骤_安装windows server 2019操作系统(写出操作过程)-CSDN博客 克隆虚拟机请参考 VMware Workstation克隆虚拟机详细步骤-CSDN博…...
Java增加线程后kafka仍然消费很慢
文章目录 一、问题分析二、控制kafka消费速度属性三、案例描述 一、问题分析 Java增加线程通常是为了提高程序的并发处理能力,但如果Kafka仍然消费很慢,可能的原因有: 网络延迟较大:如果网络延迟较大,即使开启了多线…...
RocketMQ延迟消息机制
两种延迟消息 RocketMQ中提供了两种延迟消息机制 指定固定的延迟级别 通过在Message中设定一个MessageDelayLevel参数,对应18个预设的延迟级别指定时间点的延迟级别 通过在Message中设定一个DeliverTimeMS指定一个Long类型表示的具体时间点。到了时间点后…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路
进入2025年以来,尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断,但全球市场热度依然高涨,入局者持续增加。 以国内市场为例,天眼查专业版数据显示,截至5月底,我国现存在业、存续状态的机器人相关企…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
Spring AI 入门:Java 开发者的生成式 AI 实践之路
一、Spring AI 简介 在人工智能技术快速迭代的今天,Spring AI 作为 Spring 生态系统的新生力量,正在成为 Java 开发者拥抱生成式 AI 的最佳选择。该框架通过模块化设计实现了与主流 AI 服务(如 OpenAI、Anthropic)的无缝对接&…...
select、poll、epoll 与 Reactor 模式
在高并发网络编程领域,高效处理大量连接和 I/O 事件是系统性能的关键。select、poll、epoll 作为 I/O 多路复用技术的代表,以及基于它们实现的 Reactor 模式,为开发者提供了强大的工具。本文将深入探讨这些技术的底层原理、优缺点。 一、I…...
AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
Pinocchio 库详解及其在足式机器人上的应用
Pinocchio 库详解及其在足式机器人上的应用 Pinocchio (Pinocchio is not only a nose) 是一个开源的 C 库,专门用于快速计算机器人模型的正向运动学、逆向运动学、雅可比矩阵、动力学和动力学导数。它主要关注效率和准确性,并提供了一个通用的框架&…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...
