K8S MetalLB LoadBalancer

news2024/11/24 12:19:59

1. 简介

kubernetes集群没有L4负载均衡,对外暴漏服务时,只能使用nodePort的方式,比较麻烦,必须要记住不同的端口号。

LoadBalancer:使用云提供商的负载均衡器向外部暴露服务,外部负载均衡器可以将流量路由到自动创建的 NodePort 服务和 ClusterIP 服务上。

img

MetalLB 是裸机 Kubernetes 集群的负载均衡器实现,使用标准路由协议。

它提供了两个功能:

  • 地址分配(address allocation):当创建 LoadBalancer Service 时,MetalLB 会为其分配 IP 地址。这个 IP 地址是从预先配置的 IP 地址库获取的。同样,当 Service 删除后,已分配的 IP 地址会重新回到地址库。
  • 对外公告(external announcement):分配了 IP 地址之后,需要让集群外的网络知道这个地址的存在。使用了标准路由协议实现:
    • Layer2 模式:ARP(ipv4)、NDP(ipv6)
    • BGP 模式

不管是Layer2模式还是BGP模式,两者都不使用Linux的网络栈,即无法使用诸如ip命令准确的查看VIP所在的节点和相应的路由,相对应的是在每个节点上面都能看到一个kube-ipvs0网卡接口上面的IP。同时,两种模式都只是负责把VIP的请求引到对应的节点上面,之后的请求怎么到达pod,按什么规则轮询等都是由kube-proxy实现的。

对应的的两种工作负载:

  • ControllerDeployment,用于监听 Service 的变更,分配/回收 IP 地址。
  • SpeakerDaemonSet,对外广播 Service 的 IP 地址。把服务类型为LoadBalancer的服务的EXTERNAL-IP公布到网络中去,确保客户端能够正常访问到这个IP。

2. 部署

2.1 测试服务

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.21.4
        ports:
        - name: http
          containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
spec:
  ports:
  - name: http
    port: 8080
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx
  type: LoadBalancer

2.2 MetalLB

# 1. ipvs 开启严格 arp模式
$ kubectl edit configmap -n kube-system kube-proxy
...
ipvs:
  strictARP: true

# 2. 安装组件
$ mkdir -p $HOME/metallb && cd $_
$ wget https://raw.githubusercontent.com/metallb/metallb/v0.13.10/config/manifests/metallb-native.yaml

$ kubectl apply -f metallb-native.yaml

$ kubectl get pod -n metallb-system
NAME                          READY   STATUS    RESTARTS      AGE
controller-6f5c46d94b-b7psv   1/1     Running   1 (29s ago)   80s
speaker-6v8hn                 1/1     Running   0             80s
speaker-nzkb6                 1/1     Running   0             79s
speaker-wjqxx                 1/1     Running   0             79s

组件说明:

  • metallb-system/controller:负责IP地址的分配,以及service和endpoint的监听
  • metallb-system/speaker:负责保证service地址可达,在Layer 2模式下,speaker会负责ARP请求应答

3. Layer2 模式

  • 每个service会有集群中的一个node来负责。当服务客户端发起ARP解析的时候,对应的node会响应该ARP请求,之后,该service的流量都会指向该node(看上去该node上有多个地址)。

  • 并不是真正的负载均衡,因为流量都会先经过一个node后,再通过kube-proxy转给多个endpoints。如果该node故障,MetalLB会迁移 IP到另一个node,并重新发送ARP告知客户端迁移。

  • 更为通用,不需要用户有额外的设备;但由于Layer 2模式使用ARP/NDP,地址池分配需要跟客户端在同一子网,地址分配略为繁琐。

img

3.1 工作原理

  • Speaker 工作负载类型是 DeamonSet ,在每台节点上都调度一个 Pod。首先,几个 Pod 会先进行选举,选举出 LeaderLeader 获取所有 LoadBalancer 类型的 Service,将已分配的 IP 地址绑定到当前主机到网卡上。即所有 LoadBalancer 类型的 Service 的 IP 同一时间都是绑定在同一台节点的网卡上。

  • 当外部主机有请求要发往集群内的某个 Service,需要先确定目标主机网卡的 mac 地址。这是通过发送 ARP 请求,Leader 节点的会以其 mac 地址作为响应。外部主机会在本地 ARP 表中缓存下来,下次会直接从 ARP 表中获取。

  • 请求到达节点后,节点再通过 kube-proxy 将请求负载均衡目标 Pod。所以说,假如Service 是多 Pod 这里有可能会再跳去另一台主机。

img

3.2 试验

配置 layer2 IP池 (layer2.yaml):

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: layer2-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.3.170-192.168.3.179
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: layer2-lb
  namespace: metallb-system
spec:
  ipAddressPools:
  - layer2-pool

查看结果:

# 获取
$ kubectl get svc nginx-svc
NAME        TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)          AGE
nginx-svc   LoadBalancer   10.96.155.95   192.168.3.170   8080:31717/TCP   9m34s

$ kubectl get ep nginx-svc
NAME        ENDPOINTS                                           AGE
nginx-svc   10.244.172.62:80,10.244.172.63:80,10.244.46.53:80   9m38s

$ ipvsadm -Ln | grep 192.168.3.170 -A 3
TCP  192.168.3.170:8080 rr
  -> 10.244.46.53:80              Masq    1      0          0
  -> 10.244.172.62:80             Masq    1      0          0
  -> 10.244.172.63:80             Masq    1      0          0

# 每个节点自动增加配置
$ ip addr show kube-ipvs0 | grep 192.168.3.170 -A 1
    inet 192.168.3.170/32 scope global kube-ipvs0
       valid_lft forever preferred_lft forever

查询实际的工作代理节点:

$ curl -I 192.168.3.170:8080
HTTP/1.1 200 OK
Server: nginx/1.21.4
Date: Thu, 10 Aug 2023 06:44:21 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Tue, 02 Nov 2021 14:49:22 GMT
Connection: keep-alive
ETag: "61814ff2-267"
Accept-Ranges: bytes

$ arp 192.168.3.170
Address                  HWtype  HWaddress           Flags Mask            Iface
192.168.3.170            ether   52:54:00:63:8f:d3   C                     br0

根据mac地址,查询实际的节点:

$ ip addr | grep 52:54:00:63:8f:d3 -A 1 -B 1
2: ens3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 52:54:00:63:8f:d3 brd ff:ff:ff:ff:ff:ff
    inet 192.168.3.105/24 brd 192.168.3.255 scope global ens3

3.3 清理

kubectl delete -f layer2.yaml

kubectl get pod -n metallb-system | grep Running | awk '{print $1}' | xargs kubectl delete pod -n metallb-system

4. BGP 模式

  • 所有node都会跟上级路由器建立BGP连接,并会告知路由器应该如何转发service流量
  • 是真正的 Load Balancer

在这里插入图片描述

4.1 工作原理

BGP模式不限于一个二层网络里,各个节点都会与交换机建立BGP Peer,宣告Service External IP的下一跳为自身,这样通过ECMP实现了一层负载。客户端请求通过交换机负载到后端某个节点后,再由Kube-proxy进行转发

img

4.2 路由配置

通过 openwrt 旁路由实现 BGP。使用命令 vtysh 进行 AS 配置

  • 本地:AS 65000

  • 远端:AS 65001

$ vtysh
OpenWrt# conf t
OpenWrt(config)# router bgp 65000
OpenWrt(config-router)# neighbor 192.168.3.103 remote-as 65001
OpenWrt(config-router)# neighbor 192.168.3.103 description master-01
OpenWrt(config-router)# neighbor 192.168.3.104 remote-as 65001
OpenWrt(config-router)# neighbor 192.168.3.104 description worker-01
OpenWrt(config-router)# neighbor 192.168.3.105 remote-as 65001
OpenWrt(config-router)# neighbor 192.168.3.105 description worker-02
OpenWrt(config-router)# exit
OpenWrt(config)# exit

OpenWrt# show ip bgp summary
BGP router identifier 192.168.3.180, local AS number 65000
RIB entries 0, using 0 bytes of memory
Peers 3, using 27 KiB of memory

Neighbor        V         AS MsgRcvd MsgSent   TblVer  InQ OutQ Up/Down  State/PfxRcd
192.168.3.103   4 65001       0       6        0    0    0 never    Active
192.168.3.104   4 65001       0       4        0    0    0 never    Active
192.168.3.105   4 65001       0       3        0    0    0 never    Active

Total number of neighbors 3

OpenWrt# exit

查看 bgpd 端口监听 179 是否已打开:

$  netstat -lantp | grep -E 'zebra|bgpd'
tcp        0      0 0.0.0.0:179             0.0.0.0:*               LISTEN      5355/bgpd
tcp        0      0 0.0.0.0:2601            0.0.0.0:*               LISTEN      5350/zebra
tcp        0      0 0.0.0.0:2605            0.0.0.0:*               LISTEN      5355/bgpd
tcp        0      0 :::179                  :::*                    LISTEN      5355/bgpd
tcp        0      0 :::2601                 :::*                    LISTEN      5350/zebra
tcp        0      0 :::2605                 :::*                    LISTEN      5355/bgpd

4.3 试验

配置BPG IP池 (bgp.yaml):

apiVersion: metallb.io/v1beta2
kind: BGPPeer
metadata:
  name: sample
  namespace: metallb-system
spec:
  myASN: 65001
  peerASN: 65000
  peerAddress: 192.168.3.180
---
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.0.10-192.168.0.99
---
apiVersion: metallb.io/v1beta1
kind: BGPAdvertisement
metadata:
  name: example
  namespace: metallb-system
spec:
  ipAddressPools:
  - first-pool

查看结果:

$ kubectl get svc nginx-svc
NAME        TYPE           CLUSTER-IP      EXTERNAL-IP    PORT(S)          AGE
nginx-svc   LoadBalancer   10.96.167.171   192.168.0.10   8080:31323/TCP   25h

$ kubectl get ep nginx-svc
NAME        ENDPOINTS                                           AGE
nginx-svc   10.244.172.59:80,10.244.172.60:80,10.244.46.52:80   25h

# 每个节点均添加
$ ip addr show kube-ipvs0 | grep 192.168.0.10 -A 1
    inet 192.168.0.10/32 scope global kube-ipvs0
       valid_lft forever preferred_lft forever
       
$ curl -I 192.168.0.10:8080
HTTP/1.1 200 OK
Server: nginx/1.21.4
Date: Thu, 10 Aug 2023 03:39:37 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Tue, 02 Nov 2021 14:49:22 GMT
Connection: keep-alive
ETag: "61814ff2-267"
Accept-Ranges: bytes

$ ipvsadm -Ln | grep 192.168.0 -A 3
TCP  192.168.0.10:8080 rr
  -> 10.244.46.52:80              Masq    1      0          0
  -> 10.244.172.59:80             Masq    1      0          0
  -> 10.244.172.60:80             Masq    1      0          0

客户端(windows, 192.168.3.3)添加路由:

route add 192.168.0.0 MASK 255.255.0.0 192.168.3.180

访问:https://192.168.0.10:8080 ok

4.4 清理

kubectl delete -f bgp.yaml

kubectl get pod -n metallb-system | grep Running | awk '{print $1}' | xargs kubectl delete pod -n metallb-system

5. IP地址共享

默认情况下,MetalLB只会将一个IP地址分配到一个LoadBalancer Service上,用户可以通过spec.loadBalancerIP来指定自己想用的IP,如果用户指定了已被分配了的IP会,则会报错。但MetalLB也提供了方式去支持多个Service共享相同的IP,主要为了解决:K8S不支持对LoadBalancer Service中的Port指定多协议;有限的IP地址资源。

具体的方式是:创建两个Service,并加上metallb.universe.tf/allow-shared-ip为Key的annotation,表明Service能容忍使用共享的LoadBalancerIP;然后通过spec.loadBalancerIP给两个Service指定共享的IP。

IP地址共享也有限制:

1)两个Service的metallb.universe.tf/allow-shared-ip值是一样的。

2)两个Service的“端口”(带协议)不同,比如tcp/53udp/53是属于不同的“端口”。

3)两个Service对应的后端Pod要一致,如果不一致,那么他们的externalTrafficPolicy需要都是Cluster,不然会无法进行正确的BGP。

6. 总结

6.1 nodeport

在创建LoadBalancer服务时,会默认创建一个nodeport服务,可通过配置allocateLoadBalancerNodePorts来关闭。

不同的loadbalancer实现原理不同,有的需要依赖nodeport来进行流量转发,有些则直接转发请求到pod中。

对于MetalLB而言,是通过kube-proxy将请求的流量直接转发到pod,因此可以关闭nodeport。

apiVersion: v1
kind: Service
metadata:
  name: nginx-svc
spec:
  allocateLoadBalancerNodePorts: false
  externalTrafficPolicy: Cluster
  internalTrafficPolicy: Cluster
  ports:
  - name: http
    port: 8080
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx
  type: LoadBalancer
  loadBalancerIP: 192.168.0.20  

验证:

$ kubectl delete svc nginx-svc
$ kubectl apply -f nginx-svc-disable-nodeport.yaml

$ kubectl get svc nginx-svc
NAME        TYPE           CLUSTER-IP     EXTERNAL-IP    PORT(S)    AGE
nginx-svc   LoadBalancer   10.96.54.226   192.168.0.20   8080/TCP   3s

6.2 Layer2

优点:

  • 通用性强,不需要BGP路由器支持,几乎可以适用于任何网络环境,云厂商例外

缺点:

  • 所有的流量都会在同一个节点上,该节点的容易成为流量的瓶颈;
  • 当VIP所在节点宕机之后,需要较长时间进行故障转移(一般在10s),主要是因为MetalLB使用了memberlist来进行选主,当VIP所在节点宕机之后重新选主的时间要比传统的keepalived使用的vrrp协议要更长;
  • 难以定位VIP所在节点,没有提供一个简单直观的方式查看到底哪一个节点是VIP所属节点,只能通过抓包或者查看pod日志来确定,当集群规模变大的时会非常麻烦

6.3 BGP

优点:

  • 无单点故障,在开启ECMP的前提下,k8s集群内所有的节点都有请求流量,都会参与负载均衡并转发请求

缺点:

  • 条件苛刻,需要有BGP路由器支持,配置起来也更复杂;
  • ECMP的故障转移(failover)并不是特别地优雅,这个问题的严重程度取决于使用的ECMP算法;当集群的节点出现变动导致BGP连接出现变动,所有的连接都会进行重新哈希(使用三元组或五元组哈希),这对一些服务来说可能会有影响;

7. 附录

7.1 BGP

BGP,Border Gateway Protocol,边界网关协议

BGP是互联网上一个核心的去中心化自治路由协议。它通过维护IP路由表或“前缀”表来实现自治系统(AS)之间的可达性,属于矢量路由协议。BGP不使用传统的内部网关协议(IGP)的指标,而使用基于路径、网络策略或规则集来决定路由。因此,它更适合被称为矢量性协议,而不是路由协议。

BGP的邻居关系(或称通信对端/对等实体,peer)是通过人工配置实现的,对等实体之间通过TCP端口179建立会话交换数据。BGP路由器会周期地发送19字节的保持存活(keep-alive)消息来维护连接(默认周期为60秒)。在各种路由协议中,只有BGP使用TCP作为传输层协议。

同一个AS自治系统中的两个或多个对等实体之间运行的BGP被称为iBGP(Internal/Interior BGP)。归属不同的AS的对等实体之间运行的BGP称为eBGP(External/Exterior BGP)。在AS边界上与其他AS交换信息的路由器被称作边界路由器(border/edge router),边界路由器之间互为eBGP对端。在Cisco IOS中,iBGP通告的路由距离为200,优先级比eBGP和任何内部网关协议(IGP)通告的路由都低。其他的路由器实现中,优先级顺序也是eBGP高于IGP,而IGP又高于iBGP。

iBGP和eBGP的区别主要在于转发路由信息的行为。例如,从eBGP peer获得的路由信息会分发给所有iBGP peer和eBGP peer,但从iBGP peer获得的路由信息仅会分发给所有eBGP peer。所有的iBGP peer之间需要全互联。

在这里插入图片描述

三个核心名词:

  • 自治系统(AS)
  • 内部网关协议(IGP)
  • 外部网关协议(EGP)

7.1.1 自治系统 AS

自制系统(Autonomous system,缩写 AS),是指在互联网中,一个或多个实体管辖下的所有IP 网络和路由器的组合,它们对互联网执行共同的路由策略。自治系统编号都是16位长的整数,这最多能被分配给65536个自治系统。自治系统编号被分成两个范围。第一个范围是公开的ASN,从1到64511,它们可在互联网上使用;第二个范围是被称为私有编号的从64512到65535的那些,它们仅能在一个组织自己的网络内使用。

简单理解,电信、移动、联通都有自己的 AS 编号,且不只一个。除了互联网公开的 ASN 以外,私有的编号可以在内部使用。比如我可以我的家庭网络中使用私有编号创建几个 AS。

7.1.2 内部路由协议 IGP

内部路由协议(Interior Gateway Protocol,IGP)是指在一个自治系统(AS)内部所使用的一种路由协议。

7.1.3 外部网关协议 EGP

外部网关协议(Exterior Gateway Protocol,EGP)是一个已经过时互联网路由协议。已由 BPG 取代。

7.1.4 BGP

BPG 是为了替换 EGP 而创建的,而除了应用于 AS 外部,也可以应用在 AS 内部。因此又分为 EBGP 和 IBGP。

7.2 OpenWrt

7.2.1 安装

wget https://mirrors.aliyun.com/openwrt/releases/19.07.4/targets/x86/64/openwrt-19.07.4-x86-64-combined-ext4.img.gz

gunzip openwrt-19.07.4-x86-64-combined-ext4.img.gz
cp openwrt-19.07.4-x86-64-combined-ext4.img  openwrt.img

# 双网口(wan+lan)
virt-install --name=openwrt --vcpus=1 --ram=512 --os-type=generic --disk path=/root/eli/openwrt.img,bus=ide --autostart --network bridge=br0,model=e1000 --network bridge=br0,model=e1000 --import --noautoconsole --graphics vnc,listen=0.0.0.0,password=123456

# 单网口(lan)
virt-install --name=openwrt --vcpus=1 --ram=512 --os-type=generic --disk path=/root/eli/openwrt.img,bus=ide --autostart --network bridge=br0,model=e1000 --import --noautoconsole --graphics vnc

修改网络配置:

# 改成与宿主机在同一网络下
$ vi /etc/config/network
...
config interface 'lan'
        option type 'bridge'
        option ifname 'eth0'
        option proto 'static'
        option ipaddr '192.168.3.180'
        option netmask '255.255.255.0'
        option ip6assign '60'
        option gateway '192.168.3.1'
        option dns '114.114.114.114'
        
$ service network restart

登录配置页面:http://192.168.3.180

7.2.2 Quagga

为了让 OpenWrt 支持 BGP,需要安装路由软件套件 Quagga。它提供了 OSPFv2、OSPFv3、RIP v1 v2、RIPng 和 BGP-4 的实现。

Quagga 架构由核心守护进程和 zebra 组成,后者作为底层 Unix 内核的抽象层,并通过 Unix 或者 TCP 向 Quagga 客户端提供 Zserv API。正是这些 Zserv 客户端实现了路由协议,并将路由的更新发送给 zebra 守护进程。当前 Zserv 的实现:

img

Quagga 的守护进程可以通过网络可访问的 CLI(简称 vty)进行配置。 CLI 遵循与其他路由软件类似的风格。还额外提供了一个工具 vtysh,充当了所有守护进程的聚合前端,允许在一个地方管理所有 Quagga 守护进程的所有功能。

$ opkg update && opkg install quagga quagga-zebra quagga-bgpd quagga-vtysh

$ netstat -lantp | grep -E 'zebra|bgpd'
tcp        0      0 0.0.0.0:2601            0.0.0.0:*               LISTEN      5350/zebra
tcp        0      0 0.0.0.0:2605            0.0.0.0:*               LISTEN      5355/bgpd
tcp        0      0 :::2601                 :::*                    LISTEN      5350/zebra
tcp        0      0 :::2605                 :::*                    LISTEN      5355/bgpd

参考资料:

https://metallb.universe.tf/configuration/

https://tinychen.com/20220519-k8s-06-loadbalancer-metallb/

https://atbug.com/load-balancer-service-with-metallb-bgp-mode/

https://ieevee.com/tech/2019/06/30/metallb.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/865550.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数学】CF1514 C

Problem - 1514C - Codeforces 题意&#xff1a; 思路&#xff1a; Code&#xff1a; #include <bits/stdc.h>using i64 long long;constexpr int N 2e5 10; constexpr int M 2e5 10; constexpr int mod 998244353;void solve() {int n;std::cin >> n;std:…

图像处理技巧形态学滤波之腐蚀操作

1. 引言 欢迎回来&#xff0c;我的图像处理爱好者们&#xff01;今天&#xff0c;让我们深入研究图像处理领域中的形态学计算。这些非线性的图像处理技术允许我们操纵图像中对象的形状和结构。在本系列中&#xff0c;我们将依次介绍四种基本的形态学操作&#xff1a;腐蚀、膨胀…

走出象牙塔:李郓梁的区块链实践之路丨对话MVP

如何从科研走向实践&#xff1f;李郓梁在社区找到了答案。 作为西安工业大学的硕士研究生&#xff0c;李郓梁从学校的实验室接触区块链技术。通过研读大量论文&#xff0c;李郓梁为区块链多中心化、不可篡改等前沿理论深深着迷&#xff0c;并选择将区块链作为主要研究方向&…

网络原理(JavaEE初阶系列11)

目录 前言&#xff1a; 1.网络原理的理解 2.应用层 2.1自定义协议的约定 2.1.1确定要传输的信息 2.1.2确定数据的格式 3.传输层 3.1UDP 3.1.1UDP报文格式 3.2TCP 3.2.1确认应答 3.2.2超时重传 3.2.3连接管理 3.2.3.1三次握手 3.2.3.2四次挥手 3.2.4滑动窗口 3.…

Invalid bound statement (not found)

在使用Mybatisplus时报错Invalid bound statement (not found)&#xff0c;在此记录一下 先附上解决办法 step 1、启动类加上MapperScan注解 package com.study.test;import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; …

基于QT ,halcon实现可视化程序编程框架

基于qt ,halcon 实现可视化程序编程框架 完整源码 :订阅后 请加微信 SimpleAuto6,将在第一时间发送,未订阅,时间宝贵,勿扰 部分源码: #include "LogInDialog.h"LogInDialog::LogInDialog(int w, int h,QWidget *parent): QDialog(parent),m_Password(…

SpringBoot复习:(31)Controller中返回的对象是如何转换成json字符串给调用者的?

首先&#xff0c;SpringBoot自动装配了HttpMessageConvertersAutoConfiguration这个自动配置类 而这个自动配置类又通过Import注解导入了JacksonHttpMessageConvertersConfiguration类&#xff0c; 在这个类中配置了一个类型为MappingJackson2HttpMessageConverter类型的bean…

教你一招:非计算机科班如何丝滑转码?

近年来&#xff0c;很多人想要从其他行业跳槽转入计算机领域。非计算机科班如何丝滑转码&#xff1f; 目录 一、确定方向 二、确定学习计划&#xff08;自学&#xff09; 三、学习 看到组里好多非科班姐妹决定转码之后&#xff0c;因为相关背景知识不足难以确定学习计划&am…

田间农业数字管理系统-高标准农田建设

政策背景 2019年11月&#xff0c;国务院办公厅印发的《国务院办公厅关于切实加强高标准农田建设提升粮食安全保障能力的意见》明确提出&#xff0c;到2022年&#xff0c;全国要建成10亿亩高标准农田。 2021年9月16日&#xff0c;由农业农村部印发的《全国高标准农田建设规划&a…

python时间戳转换字符串时间

Python时间戳和日期格式之间的相互转化 将10位或13位时间戳转为日期格式&#xff08;年-月-日 时-分-秒&#xff09; python毫秒时间戳转为字符串时间 java默认精度是毫秒级别的&#xff0c;生成的时间戳是13位&#xff0c; 而python默认是10位的&#xff0c;精度是秒。如p…

2023牛客暑期多校训练营8-I Make It Square

2023牛客暑期多校训练营8-I Make It Square https://ac.nowcoder.com/acm/contest/57362/I 文章目录 2023牛客暑期多校训练营8-I Make It Square题意解题思路代码实现 题意 解题思路 这里有两种情况&#xff0c;即 ∣ s ∣ > ∣ t ∣ |s|>|t| ∣s∣>∣t∣和 ∣ s ∣…

如何让你的视频在 TikTok上变得火爆?

TikTok凭借巨大的用户量和商业价值&#xff0c;它从来不缺优质内容。如何在众多内容中脱颖而出获得关注&#xff0c;这并不简单。和泛流量账号不同&#xff0c;商业账号的目的更加明确&#xff0c;也就是说&#xff0c;商业账号并不一定要以高流量最为唯一的追求目标&#xff0…

动态规划之斐波拉契数列模型

斐波拉契数列模型 1. 第 N 个泰波那契数2. 三步问题&#xff08;easy&#xff09;3. 使⽤最⼩花费爬楼梯&#xff08;easy&#xff09;4. 解码⽅法&#xff08;medium&#xff09; 动态规划的介绍&#xff1a; 动态规划是一种在数学、管理科学、计算机科学、经济学和生物信息学…

最短路相关思想总结

dijkstra—所有边均为正权边 1.稠密图 算法思想 将所有的点读入邻接表 外层n次循环 每次找到最近的点&#xff0c;记录这个点的访问状态&#xff0c;使用这个点对其他的点进行更新&#xff0c;最后返回最短路 为什么要记录每个点的状态&#xff1f;我不能重复搜这个点吗&…

面对AI冲击,技术人才该如何考核?

一天下午&#xff0c;在与知名企业的技术交流会议室里&#xff0c;一位兄弟企业的CTO 小力苦笑着&#xff0c;分享了一个技术招聘的故事&#xff1a; “我们有个高级工程师&#xff0c;为了搞定MySQL三个表Join的问题&#xff0c;搞了一整天都研究不出来。结果他尝试将表结构扔…

【C++进阶】继承、多态的详解(继承篇)

【C进阶】继承、多态的详解&#xff08;继承篇&#xff09; 目录 【C进阶】继承、多态的详解&#xff08;继承篇&#xff09;继承的概念及定义继承的概念继承的定义定义格式继承关系和访问限定符继承基类成员访问方式的变化 基类和派生类对象赋值转换继承中的作用域派生类的默认…

你不能访问此共享文件夹因为你组织的安全策略

我在windows 10中尝试访问没有密码的共享文件只报错如下&#xff1a; 解决办法 运气中执行 gpedit.msc来启动本地组策略编辑器。 这样设置完成后&#xff0c;就可以在运行中输入 \\192.168.199.1\可以访问共享的文件了。 参考 https://zhuanlan.zhihu.com/p/164721714…

HashMap 二十一问

1&#xff1a;HashMap 的数据结构&#xff1f; A&#xff1a;哈希表结构&#xff08;链表散列&#xff1a;数组链表&#xff09;实现&#xff0c;结合数组和链表的优点。当链表长度超过 8 时&#xff0c;链表转换为红黑树。transient Node<K,V>[] table; 2&#xff1a;…

QT QtXlsx安装使用

QtXlsx介绍 QtXlsx是一个可以读取和写入Excel文件的库。它不需要Microsoft Excel&#xff0c;可以在Qt5支持的任何平台上使用。 这里一定是需要QT5支持的。 须知安装QtXlsx时&#xff0c;需要下载perl 1.安装perl 这里选择官网下载安装即可。 官网地址&#xff1a;https://p…

Android应用开发(35)SufaceView基本用法

Android应用开发学习笔记——目录索引 参考Android官网&#xff1a;https://developer.android.com/reference/android/view/SurfaceView 一、SurfaceView简介 SurfaceView派生自View&#xff0c;提供嵌入视图层次结构内部的专用绘图表面&#xff0c;SurfaceView可以在主线程之…