当前位置: 首页 > news >正文

CNI设计解读

何为cni?

kubernetes在设计网络方案的时候并没有设计统一的网络方案,只提供了统一的容器网络接口也就是所谓cni,这么做的目的就是为了遵循kubernets的核心理念OutOfTree,简单来讲就是专注于自身核心能力,将其他能力类似csi cni cri交给社区以及领域专家,这样一方面可以降低软件自身使用的复杂度,减小稳定性风险。

flannel cni设计

在一个pod生命周期中,cni主要调用3个方法分别是cmdAdd,cmdDel, cmdCheck,分别代表:创建容器时调用cmdAdd,销毁容器时调用cmdDel, 以及销毁前的检测cmdCheck,但是cmdCheck是在0.4.0之后添加的,对于目前常用的cni版本0.3.1来说并不支持。

整体链路为flnnel-cni->bridge(创建设备)->host-local(申请ip)->bridge(申请到的ip写入到网卡上并配置路由)

大致调用流程图如下:

流程详解

第一部分(kubelet)

创建流程

1. kubelet解析/etc/cni/net.d/10-flannel.conflist文件之后 根据文件里的plugins里的对象逐一执行插件并把结果传递给下一个插件继续执行 最后将结果缓存到/var/lib/cni/cache目录。

/etc/cni/net.d/10-flannel.conflist配置如下 分别调用两个插件:

      a. flannel插件主流程依次调用bridge以及host-local 创建虚拟网卡以及路由

      b.portmap插件主要针对配置hostPort的pod 为该pod通过iptables配置端口映射

{"name": "cbr0","cniVersion": "0.3.1","plugins": [{"type": "flannel","delegate": {"hairpinMode": true,"isDefaultGateway": true}},{"type": "portmap","capabilities": {"portMappings": true}}]
}
func (c *CNIConfig) AddNetworkList(ctx context.Context, list *NetworkConfigList, rt *RuntimeConf) (types.Result, error) {var err errorvar result types.Resultfor _, net := range list.Plugins {result, err = c.addNetwork(ctx, list.Name, list.CNIVersion, net, result, rt)if err != nil {return nil, err}}if err = setCachedResult(result, list.Name, rt); err != nil {return nil, fmt.Errorf("failed to set network %q cached result: %v", list.Name, err)}return result, nil
}

2. 通过10-flannel.conflist我们可以看到调用的第一个插件为flannel,那么kubelet将插件目录/opt/cni/bin/flannel传递给invoke.ExecPluginWithResult函数.

func (c *CNIConfig) addNetwork(ctx context.Context, name, cniVersion string, net *NetworkConfig, prevResult types.Result, rt *RuntimeConf) (types.Result, error) {c.ensureExec()pluginPath, err := c.exec.FindInPath(net.Network.Type, c.Path)if err != nil {return nil, err}newConf, err := buildOneConfig(name, cniVersion, net, prevResult, rt)if err != nil {return nil, err}return invoke.ExecPluginWithResult(ctx, pluginPath, newConf.Bytes, c.args("ADD", rt), c.exec)
}

3. invoke.ExecPluginWithResult函数里调用exec.ExecPlugin并传递参数执行相应的二进制文件

func ExecPluginWithResult(ctx context.Context, pluginPath string, netconf []byte, args CNIArgs, exec Exec) (types.Result, error) {if exec == nil {exec = defaultExec}stdoutBytes, err := exec.ExecPlugin(ctx, pluginPath, netconf, args.AsEnv())if err != nil {return nil, err}// Plugin must return result in same version as specified in netconfversionDecoder := &version.ConfigDecoder{}confVersion, err := versionDecoder.Decode(netconf)if err != nil {return nil, err}return version.NewResult(confVersion, stdoutBytes)
}

三个参数如下示例:

 pluginPath: /opt/cni/bin/flannelNetConf: types.NetConf{CNIVersion: "0.3.1",Name: "cbr0",Type: "flannel",IPAM: types.IPAM{Type: "",},DNS: types.DNS{Nameservers: nil,Domain: "",Search: nil,Options: nil,},},Delegate: map[string]interface {}{"hairpinMode": true,"isDefaultGateway": true,},
}
args.AsEnv():  设置CNI_COMMAND,CNI_CONTAINERID,CNI_NETNS,CNI_ARGS,CNI_IFNAME,CNI_PATH几个环境变量给到插件
二进制程序通过PluginMain里的getCmdArgsFromEnv函数将环境变量解析成CmdArgs之后传递给cmdAdd,cmdDel,cmdCheck

4. 所有插件执行完成之后调用setCachedResult将结果写入到/var/lib/cni/cache/results目录下

删除流程

1.类似于创建流程,整体流程如下:

TearDownPod -> plugin.deleteFromNetwork -> cniNet.DelNetworkList -> delNetwork -> invoke.ExecPluginWithoutResult

从这里我们看到基本的流程和创建流程类似 唯一不一致的地方是我们在c.args("DEL", rt) 传入的是DEL而不是ADD。也就是说我们在这里判断我们执行插件的函数是cmdAdd还是cmdDel

2. 最后删除/var/lib/cni/cache/results下的对应文件,/var/lib/cni/cache/results的目的是为了提供cmdcheck用于检测是否合规

第二部分(flannel-cni)

        flannel-cni总共实现了两个方法cmdAdd以及cmdDel,通过PluginMain注册cmdAdd,cmdDel两个方法,并在PluginMain里讲传递过来的参数解析成cmdargs传递给cmdAdd,cmdDel其实现的能力如下:

cmdAdd:

1. 通过loadFlannelNetConf解析cmdargs里的StdinData,内容为NetConf结构体,并配置SubnetFile(/run/flannel/subnet.env)以及DataDir(/var/lib/cni/flannel)

2. 通过loadFlannelSubnetEnv解析由flannel生成的/run/flannel/subnet.env文件

3. 将/run/flannel/subnet.env里面的参数 渲染到由loadFlannelNetConf生成的出来的结构体中,也就是hairpinMode(发夹模式 可以让数据流量从同一个点位进出)以及isDefaultGateway(是否生成pod容器的默认网关)两个参数:

types.NetConf{CNIVersion: "0.3.1",Name: "cbr0",Type: "bridge",Mtu: "1450",HairpinMode: true,IpMasq: false,IsDefaultGateway: true,IsGateway: true,IPAM: types.IPAM{"type":   "host-local","subnet": 10.244.0.1/24,"routes": []types.Route{types.Route{Dst: 10.244.0.0/16,},},}
}

 4. delegateAdd通过type字段找到后续需要执行的插件名称 并通过saveScratchNetConf方法将以上结果保存到/var/lib/cni/flannel目录下并在invoke.DelegateAddDelegate里通过type字段来判断执行下一个插件的名称(bridge),并将之前的结果以及args.env传递给下一个插件(bridge),该结果通过下一个插件的StdinData获取到.

func delegateAdd(cid, dataDir string, netconf map[string]interface{}) error {netconfBytes, err := json.Marshal(netconf)if err != nil {return fmt.Errorf("error serializing delegate netconf: %v", err)}// save the rendered netconf for cmdDelif err = saveScratchNetConf(cid, dataDir, netconfBytes); err != nil {return err}result, err := invoke.DelegateAdd(netconf["type"].(string), netconfBytes)if err != nil {return err}return result.Print()
}

cmdDEL:

1.通过loadFlannelNetConf解析cmdargs里的StdinData,内容为NetConf结构体,并配置SubnetFile(/run/flannel/subnet.env)以及DataDir(/var/lib/cni/flannel)

2. 通过CNI_ARGS获取到容器的id 通过consumeScratchNetConf函数读取/var/lib/cni/flannel/$containerId里面的配置类似,读取之后移除该文件。

# cat 0e39bc1c61f18e1af93bc6f455097fcfe04872d590c313eccf4d3a4f7de224d2
{"cniVersion":"0.3.1","hairpinMode":true,"ipMasq":false,"ipam":{"routes":[{"dst":"10.220.0.0/16"}],"subnet":"10.220.9.0/24","type":"host-local"},"isDefaultGateway":true,"isGateway":true,"mtu":1450,"name":"cbr0","type":"bridge"}

3. 将从/var/lib/cni/flannel/$containerId读取出来的内容 发给下一个插件(bridge)的CmdDel函数

第三部分(bridge cni)

cmdAdd部分

1. loadNetConf函数作用是读取flannel cni传递过来的StdinData 并设置BrName参数为cni0 组合成新的NetConf对象

2. 通过setupBridge函数里的ensureBridge函数创建名称为cni0的bridge虚拟桥接网卡

3. 通过GetNS获取该pod容器所在的网络名称空间id,args.Netns类似pid所在的net文件类似/proc/18649/ns/net

4. 通过setupVeth在改网络namespace里创建veth网卡对,网络namespace端为eth0 宿主机端为veth***(该名称由RandomVethName函数生成,并将veth加入到宿主机端namespace下。之后配置宿主机端端veth网卡hairpin mode。

5. 之后通过ipam.ExecAdd往下ipam(IP地址管理器)里获取ip地址,这里ipam类型为host-local,host-local的插件的规则从第四部分详解。

6. 获取到ip的结构为:

{"ip4": {"ip": "10.244.0.2","gateway": "10.244.0.1"},"dns": {}
}

7. 通过calcGatewy来获取网关详情 这里主要以IsDefaultGW参数来判断是否给pod容器加默认网关

8. 之后在ConfigureIface函数里进入到上述的网络namespace配置eth0网卡的ip地址并根据calcGatewy的结果增加默认路由

9. 之后在ensureBridgeAddr函数里配置cni0网卡的ip地址,网卡mac地址,并在enableIPForward开启ipv4转发(/proc/sys/net/ipv4/ip_forward)。

10.之后在SetupIPMasq函数里配置地址伪装的iptables规则

CmdDEL部分

1. 通过loadNetConf读取flannel插件传递的过来的StdinData

2. 将flannel插件传递的过来的StdinData传递给IPAM插件去释放分配给该pod的ip地址,这里的IPAM从StdinData里可以看到 调用的是host-local插件。

3. 切换到该容器的network namespace之后删除虚拟网卡信息,并且如果开启了地址伪装功能,删除对应的iptables规则

第四部分(host-local cni)

cmdAdd部分

1.通过LoadIPAMConfig函数 将bridge传过来的StdinData以及args.env生成IPAMConfig对象

2. 之后通过allocator.Get根据传递过来的range进行ip地址分配,这里也可以指定ip地址分配,如果指定ip地址那么会校验ip地址是否合规,如果合规将分配的地址存储到本地磁盘下(/var/lib/cni/networks/cbr0/)以ip为文件名称 内容为容器id,并把最后获取到的ip地址存储到/var/lib/cni/networks/cbr0/last_reserved_ip.0,这么做的目的是保证分配的地址不冲突。

3. 迭代器GetIter的主要逻辑是基于LastReservedIP(这个数据保存在一个文件中)和ip range,找到下一个可分配的IP并返回,获取规则是避免LastReservedIP里的ip 在LastReservedIP的基础上+1 作为分配的ip 当然也会通过range检测分配的ip是否在合规范围内。

4. 核心函数GetIter 生成出迭代器对象,并通过lastReservedIP来配置cur这个参数的值,Next函数根据cur的值来判断下一个可分配的ip地址

5.之后通过Reserve方法将ip地址和容器id进行绑定存储到/var/lib/cni/networks/cbr0/并更新last_reserved_ip.0文件,最后将结果返回给bridge插件。

func (a *IPAllocator) GetIter() (*RangeIter, error) {iter := RangeIter{rangeset: a.rangeset,}// Round-robin by trying to allocate from the last reserved IP + 1startFromLastReservedIP := false// We might get a last reserved IP that is wrong if the range indexes changed.// This is not critical, we just lose round-robin this one time.lastReservedIP, err := a.store.LastReservedIP(a.rangeID)if err != nil && !os.IsNotExist(err) {log.Printf("Error retrieving last reserved ip: %v", err)} else if lastReservedIP != nil {startFromLastReservedIP = a.rangeset.Contains(lastReservedIP)}// Find the range in the set with this IPif startFromLastReservedIP {for i, r := range *a.rangeset {if r.Contains(lastReservedIP) {iter.rangeIdx = iiter.startRange = i// We advance the cursor on every Next(), so the first call// to next() will return lastReservedIP + 1iter.cur = lastReservedIPbreak}}} else {iter.rangeIdx = 0iter.startRange = 0iter.startIP = (*a.rangeset)[0].RangeStart}return &iter, nil
}
func (i *RangeIter) Next() (*net.IPNet, net.IP) {r := (*i.rangeset)[i.rangeIdx]// If this is the first time iterating and we're not starting in the middle// of the range, then start at rangeStart, which is inclusiveif i.cur == nil {i.cur = r.RangeStarti.startIP = i.curif i.cur.Equal(r.Gateway) {return i.Next()}return &net.IPNet{IP: i.cur, Mask: r.Subnet.Mask}, r.Gateway}// If we've reached the end of this range, we need to advance the range// RangeEnd is inclusive as wellif i.cur.Equal(r.RangeEnd) {i.rangeIdx += 1i.rangeIdx %= len(*i.rangeset)r = (*i.rangeset)[i.rangeIdx]i.cur = r.RangeStart} else {i.cur = ip.NextIP(i.cur)}if i.startIP == nil {i.startIP = i.cur} else if i.rangeIdx == i.startRange && i.cur.Equal(i.startIP) {// IF we've looped back to where we started, give upreturn nil, nil}if i.cur.Equal(r.Gateway) {return i.Next()}return &net.IPNet{IP: i.cur, Mask: r.Subnet.Mask}, r.Gateway
}

cmdDel部分

1. 通过LoadIPAMConfig读取bridge传递的StdinData内容

2. disk.New出生后存储,默认的目录是/var/lib/cni/networks/{name},后续通过store操作数据

3. 通过ipAllocator.Release释放ip 并通过ReleaseByID函数循环读取/var/lib/cni/networks/cbr0目录下的文件,如果文件内容匹配容器id那么就移除该文件。

func (s *Store) ReleaseByID(id string) error {err := filepath.Walk(s.dataDir, func(path string, info os.FileInfo, err error) error {if err != nil || info.IsDir() {return nil}data, err := ioutil.ReadFile(path)if err != nil {return nil}if strings.TrimSpace(string(data)) == strings.TrimSpace(id) {if err := os.Remove(path); err != nil {return nil}}return nil})return err
}

第五部分(portmap cni)

cmdAdd部分

最后flannel-cni返回给kubelet插件的结构体如下 并把结果放到prevResult里交给portmap插件 配置案例如下:

{"name": "cbr0","cniVersion": "0.3.1","runtimeConfig": {"portMappings": [{"hostPort": 801,"containerPort": 80,"protocol": "tcp"}]},"prevResult": {"cniVersion": "1.0.0","interfaces": [{"name": "eth0","sandbox": "/proc/20202/ns/net"}],"ips": [{"interface": 2,"address": "10.244.0.136/24","version": "4"}],"routes": [{"dst": "0.0.0.0/0","gw": "10.244.0.1"}],"dns": {}}
}

2. parseConfig函数解析上面的结构体以及接口名称通常为eth0,在这里主要判断结构体内容是否合规 是否需要进行端口映射

3. 通过forwardPorts生成并在宿主机加入iptables规则映射规则DNT以及SNAT默认情况下地址伪装是需要配置snat规则,这里配置dnat主要是为了对需要地址伪装的流量进行打标记

cmdDEL部分

1. 通过parseConfig解析StdinData以及ifname

2. 获取args.ContainerID

3. 通过unforwardPorts删除对应的iptables端口映射规则dnat以及snat

相关文章:

使用kubeadm搭建高可用集群-k8s相关组件及1.16版本的安装部署

本文是向大家分享k8s相关组件及1.16版本的安装部署,它能够让大家初步了解k8s核心组件的原理及k8s的相关优势,有兴趣的同学可以部署安装下。 什么是kubernetes kubernetes是Google 开源的容器集群管理系统,是大规模容器应用编排系统&#xff…...

为了摸鱼,我开发了一个工具网站

🏡 博客首页:派 大 星 ⛳️ 欢迎关注 🐳 点赞 🎒 收藏 ✏️ 留言 🎢 本文由派大星原创编撰 🚧 系列专栏:《开源专栏》 🎈 本系列主要输出作者自创的开源项目 🔗 作品&…...

CodeForces - 545E Paths and Trees 最短路建树

题目链接:点击查看 Little girl Susie accidentally found her elder brothers notebook. She has many things to do, more important than solving problems, but she found this problem too interesting, so she wanted to know its solution and decided to a…...

koa + pug模板引擎

模板引擎 模板引擎:模板引擎是web应用中动态生成html的工具,负责将数据和模板结合。常见模板引擎有:ejs、jade(现更名为pug)、Handlebars、Nunjucks、Swig等;使用模板引擎可以是项目结构更加清晰&#xff…...

数字集成电路设计(二、Verilog HDL基础知识)

文章目录1. 语言要素1.1 空白符1.2 注释符1.3 标识符1.3.1 转义标识符1.4 关键字1.5 数值1.5.1 整数及其表示方式1.5.2 实数及其表示方式1.5.3 字符串及其表示方式2. 数据类型2.1 物理数据类型2.1.1 连线型2.1.2 寄存器型2.2 连线型和寄存器型数据类型的声明2.2.1 连线型数据类…...

蓝桥杯刷题(三)

蓝桥杯刷题一.等差素数列(较难)二.货物摆放(思路新奇)三.既约分数四.跳跃五.数值求值(坑题)蓝桥杯题库一.等差素数列(较难) 这道题有公式,其等差就是它长度内所有素数的乘…...

【工具使用】Visual Studio Code远程调试

VS Code的其中一个关键的特征就是它极好的调试支持。VS Code的内置调试器帮助加速你的编辑、编译和调试循环。 调试扩展 VS Code有Node.js运行的内置的调试支持,并且能够调试Java脚本或者任何其他可以转译为JavaScript的语言。为了调试其他语言(包括P…...

【Flutter】【widget】Table 表格widget

文章目录前言一、Table 是什么?二、使用步骤1.Table 基础使用2.宽度3.设置边框4.TableCell设置单元格式widget等其他设置总结前言 Table 表格widget,其实很少使用到的,等有需要的时候在查看该widget 一、Table 是什么? 表格widg…...

PreScan快速入门到精通第三十九讲基于车道线识别传感器的车道保持辅助算法Demo讲解

车道保持辅助系统介绍: 什么是车道保持辅助系统? 疲劳和分心是无意中偏离车辆行驶车道线的最常见原因。车道保持辅助系统主动帮助驾驶者将其车辆保持在车道内,避免或者降低事故的发生。 车道保持辅助系统使用一个前置的摄像头,一般安装在车内后视镜附近,用来检测车辆前方…...

ADB学习笔记

简介: ADB的全称为Android Debug Bridge(调试桥), 它是一个客户端-服务器端程序,其中客户端是你用来操作的电脑, 服务器端是android设备。作用显而易见,能方便我们在PC上对手机进行调试的一些工作。 原理…...

专利如果申请?成功率如何提高?

问题一:专利如何申请?​ 1、整理技术方案,整理一套属于你的创新技术方案; 2、专利检索,确保你的创新没有被别人申请过; 3、撰写专利申请书,为了快速审查,做好长期专利布局&#x…...

http load介绍

前几天工作中要对项目的接口做简单压测,就使用了http load做了简单测试,下面介绍一下这款工具的使用说明。简介:http_load是基于linux平台的性能测试工具,它体积非常小,仅100KB。它以并行复用的方式运行,可…...

执行 sh 的时候会找 config/ora2pg.conf

拷贝配置文件&#xff0c;注意路径&#xff0c;后面执行 sh 的时候会找 config/ora2pg.conf。 cp <your_install_dir>/etc/ora2pg/ora2pg.conf.dist <source_code_dir>/config/ora2pg.conf ORACLE 相关参数&#xff1a; ORACLE_HOME /u01/app/oracle/product/11.…...

DPDK之PMD原理

PMD是Poll Mode Driver的缩写&#xff0c;即基于用户态的轮询机制的驱动。本文将介绍PMD的基本原理。 在不考虑vfio的情况下&#xff0c;PMD的结构图如下&#xff1a; 图1. PMD结构图 虽然PMD是在用户态实现设备驱动&#xff0c;但还是依赖于内核提供的策略。其中uio模块&…...

Linux shell脚本之回顾及实用笔记

一、前言 我们从事运维的小伙伴,除了自动化运维外,在没有自动化条件下,借助shell脚本/Python脚本来提升运维效率,无疑是一个必选项,当前也可以自建自动化运维平台,我们这里还是以Linux shell脚本为主,来汇总一些常用的运维脚本,对于有基础的同学,也随本文一起回顾下相…...

TestNG使用总结

TestNG简介&#xff1a; TestNG是一个测试框架&#xff0c;其灵感来自JUnit和NUnit&#xff0c;但同时引入了一些新的功能&#xff0c;使其功能更强大&#xff0c;使用更方便。 TestNG相较于Junit的优点&#xff1a; 可指定执行顺序&#xff0c; dependsOnMethods 属性来应对…...

面向对象编程的弊端

英文原文&#xff1a;What’s Wrong with OOP and FP 我不理解为什么人们会对面向对象编程和函数式编程做无休无止的争论。就好象这类问题已经超越了人类智力极限&#xff0c;所以你可以几个世纪的这样讨论下去。经过这些年对编程语言的研究&#xff0c;我已经清楚的看到了问题…...

5.Servlet

一、Servlet快速入门 1.创建web项目&#xff0c;导入Servlet依赖坐标&#xff08;scope范围为provided因为上传后tomcat也有这个&#xff0c;可能会冲突&#xff09;pom.xml <dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-a…...

(续)SSM整合之springmvc笔记(@RequestMapping注解)(P124-130)还没完

RequestMapping注解 一.准备工作 1 新建spring_mvc_demo com.atguigu 2. 导入依赖 <packaging>war</packaging><dependencies><!-- SpringMVC --><dependency><groupId>org.springframework</groupId><artifactId>sprin…...

c++入门必学算法 质数筛

文章目录一、什么是质数筛二、暴力枚举1、暴力枚举基本思想&#xff1a;2、模板代码3、运行结果三、埃氏筛1、埃氏筛基本思想&#xff1a;2、模板代码3、运行结果四、欧拉筛1、对比埃氏筛2、欧拉筛的基本思想3、模板代码3、运行结果五、总结一、什么是质数筛 质数筛也叫素数筛…...

适用于C/C++开发人员的HOOPS

1.编译和运行时信息 1.1编制和执行 编译和运行基于C的应用程序需要以下步骤&#xff1a; 编译&#xff1a; 所有3DGS应用: hoops.lib 使用HOOPS/MVO的应用: hoops_mvo.lib 使用HOOPS/Stream的应用: hoops_stream.lib 执行&#xff1a;确保以下本地DLL位于应用程序的目录或…...

Verilog结构语句和函数、任务语句

目录 结构说明语句 initial说明语句 always说明语句 task和function说明语句 task说明语句 function说明语句 关于使用任务和函数的小结 结构说明语句 Verilog语言中的任何过程模块都从属于以下4种结构的说明语句&#xff1a; initial说明语句 一个模块种可以有多个i…...

String 创建字符串对象和字符串常量池的联系推理

文章目录String 创建字符串对象和字符串常量池的联系推理ref前提intern方法String s "abc";字符串相加String 创建字符串对象和字符串常量池的联系推理 可能有错误理解 ref String s1 new String(“abc”);这句话创建了几个字符串对象&#xff1f; 我提的issue …...

flex 计算指定日期是本年度第几周

/** * 计算指定日期是本年度第几周 *传日年月日&#xff0c;返回number */ private function weekOfYear(yyyy:Number,mm:Number,dd:Number):Number{ var myDate:Date new Date(yyyy, mm - 1, dd); var startDate:Date new Date(yyyy,0,1); v…...

SpringCloud Zuul(四)之工作原理

一、筛选器概述 Zuul的中心是一系列过滤器&#xff0c;这些过滤器能够在HTTP请求和响应的路由期间执行一系列操作。 以下是Zuul过滤器的主要特征&#xff1a; 类型&#xff1a;通常定义路由流程中应用过滤器的阶段&#xff08;尽管它可以是任何自定义字符串&#xff09;执行…...

【毕业设计】大数据分析的航空公司客户价值分析 - python

文章目录0 前言1 数据分析背景2 分析策略2.1 航空公司客户价值分析的LRFMC模型2.2 数据2.3 分析模型3 开始分析3.1 数据预处理3.1.1 数据预览3.1.2 数据清洗3.2 变量构建3.3 建模分析4 数据分析结论4.1 整体结论4.2 重要保持客户4.3 重要挽留客户4.4 一般客户与低价值客户5 最后…...

软件工程毕业设计课题(80)微信小程序毕业设计PHP电影视频播放小程序系统设计与实现

项目背景和意义 目的&#xff1a;本课题主要目标是设计并能够实现一个基于微信电影播放小程序系统&#xff0c;前台用户使用小程序&#xff0c;小程序使用微信开发者工具开发&#xff1b;后台管理使用基PPMySql的B/S架构&#xff0c;开发工具使用phpstorm&#xff1b;通过后台添…...

PyTorch搭建基于图神经网络(GCN)的天气推荐系统(附源码和数据集)

需要源码和数据集请点赞关注收藏后评论区留言~~~ 一、背景 极端天气情况一直困扰着人们的工作和生活。部分企业或者工种对极端天气的要求不同&#xff0c;但是目前主流的天气推荐系统是直接将天气信息推送给全部用户。这意味着重要的天气信息在用户手上得不到筛选&#xff0c;…...

Python 对象保存优化机制

Python 为了减少开销与内存的使用而设置一些规则: * 1. 但凡是不可变对象, 在同一个代码块中的对象, 只要是值相同的对象, 就不会重复创建, 而是直接引用已经存在的对象.交互环境下: 不写在一行, 字符类型数据指向一个内存地址, 整型超出小整数则执指向不同的地址. 代码块缩进相…...

隐式转换这个概念你听说过没?

世界上最遥远的距离不是生与死&#xff0c;而是你亲手制造的BUG就在你眼前&#xff0c;你却怎么都找不到她。 目录 1、隐式转换是什么意思 1.1整型截断 1.2整形提升 2、char的取值范围 2.1有符号char取值范围 2.2无符号char取值范围 前言&#xff1a; 大家好&#xff0c;…...

Python编程 字典创建map与Zip

作者简介&#xff1a;一名在校计算机学生、每天分享Python的学习经验、和学习笔记。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​​ 目录 前言 字典(dict) 字典创建(拓展) 拓展&#xff1a; 前言 本章将会扩展Python…...

Web 性能优化:TLS

个人博客 Web 性能优化&#xff1a;TCP&#x1f3af; Web 性能优化&#xff1a;TLSWeb 性能优化&#xff1a;HTTP “do it, do it work, do it better … and secure ☠️” 随着追逐利益而来的恶意参与者越来越多&#xff0c;当前的 Web 应用&#xff0c;已经从野蛮生长转而…...

2022亚太杯C题思路代码分析

C题就是数学比较开放的题目了&#xff0c;属于一个数据分析类题目&#xff0c;跟前两年的华为杯差不多&#xff0c;考察的也是全球变暖问题。更多内容文末名片查看 问题1.你同意有关全球气温的说法吗&#xff1f;使用2022_APMCM_C_Data。附件中的csv和其他您的团队收集的数据集…...

力扣113题引发的关于DFS和回溯的一点思考

最近刚学回溯和DFS&#xff0c;刷力扣遇到一道题&#xff08;113题&#xff09;&#xff0c;如下&#xff1a; 我们不细究回溯和DFS的区别联系。关于这道题的2种写法&#xff0c;我把第一种称为回溯。 class Solution {List<List<Integer>> res new LinkedList&l…...

Go 语言报错 StackGuardMultiplier redeclared in this block

前言 最近在 GitHub 刷到了 GitHub - golang-china/gopl-zh: Go语言圣经中文版&#xff0c; 然后又是周末&#xff0c;就起了玩心。搞一个 Go 玩玩&#xff0c;去 The Go Programming Language下载了 Go 语言安装包&#xff0c;一路默认安装。然后打开 VS Code 安装 Extensio…...

C\C++刷题ADY3

题目来源&#xff1a;力扣 1.第一题 203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09; 思路分析:&#xff08;不带哨兵位的头节点&#xff09; 每次去分析一个节点&#xff0c; 如果节点不是存的是6&#xff0c;就拿节点来尾插 如果节点存的不是6&#xff0c;就把节…...

解决elementui 的省市区级联选择器数据不回显问题

上周写了一个省市区三级联动的地址选择组件&#xff0c;今天测试发现了一个大问题&#xff0c;那就是我可以正常提交地址是没错&#xff0c;可是当我后端返回了数据&#xff0c;我要点击编辑的时候&#xff0c;它并不会自动就给我绑定上去。 vue实现省市区三级联动地址选择组件…...

[CSS]圆角边框与阴影

前言 系列文章目录&#xff1a; [目录]HTML CSS JS 根据视频和PPT整理视频及对应资料&#xff1a;HTML CSS 老师笔记&#xff1a; https://gitee.com/xiaoqiang001/html_css_material.git视频&#xff1a;黑马程序员pink老师前端入门教程&#xff0c;零基础必看的h5(html5)css3…...

微信小程序原理

前言 微信小程序采用JavaScript. WXML. WXSS三种技术进行开发&#xff0c;从技术讲和现有的前端开发差不多&#xff0c;但深入挖掘的话却又有所不同。 一、原理 JavaScript&#xff1a;首先JavaScript的代码是运行在微信App中的&#xff0c;并不是运行在浏览器中&#xff0c;…...

Neo4j 开发者大会 NODES 2022 活动日程已发布 - 11.16 ~ 11.17

各位 Graphistas&#xff1a; Neo4j 开发者大会 NODES 2022 将在 2022 年 11 月 16&#xff5e;17 日召开&#xff0c;不要错过这连续 24 小时跨越 3 个主要时区的大型在线活动&#xff0c;欢迎加入我们一起庆祝来自全球图技术社区的隆重分享。 现在访问官方网站注册活动: ht…...

生成者(建造者)模式

思考生成者模式 生成者模式就是将对象构建和对象内部构建分离 对象构建&#xff1a;手机的构建 对象内部构建&#xff1a;手机中屏幕和电池的构建 1.生成者模式的本质 生成器模式的本质:分离整体对象构建算法和对象的部件构造。 构建一个复杂的对象&#xff0c;本来就有构建的过…...

Google Swift 与 DC 传输

网络拥塞&#xff0c;默认指转发节点出现了严重的排队现象&#xff0c;甚至队列溢出而丢包。、 但接收端也是一个统计复用系统(通用 OS 均为统计复用系统&#xff0c;比如 Linux)&#xff0c;但凡统计复用系统就是潜在拥塞点&#xff0c;即可套用排队论模型。 人们很少将最后…...

webservice学习记录笔记(一)

一、先理解什么是服务 现在的应用程序变得越来越复杂&#xff0c;甚至只靠单一的应用程序无法完成全部的工作。更别说只使用一种语言了。 写应用程序查询数据库时&#xff0c;并没有考虑过为什么可以将查询结果返回给上层的应用程序&#xff0c;甚至认为&#xff0c;这就是数…...

[附源码]SSM计算机毕业设计-东湖社区志愿者管理平台JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…...

spring Cloud微服务 security+oauth2认证授权中心自定义令牌增强,并实现登录和退出

文章目录认证授权中心自定义令牌增强自定义认证端点返回结果登录逻辑调整&#xff0c;增强令牌返回参数测试验证用户微服务构建配置类构建相关实体类登录退出登录在之前的博客我写了 SpringCloud整合spring security oauth2Redis实现认证授权&#xff0c;本文对返回的token实现…...

接口测试那些事儿

什么是接口&#xff1f; 首先&#xff0c;在讲接口测试之前&#xff0c;我们先要搞清楚接口类型的概念。 接口&#xff1a;可能是系统与系统&#xff08;包括服务与服务&#xff09;之间的调用&#xff0c;像A系统&#xff08;服务&#xff09;给B系统&#xff08;服务&#x…...

CodeForces - 1084C The Fair Nut and String 思维

The Fair Nut found a string s. The string consists of lowercase Latin letters. The Nut is a curious guy, so he wants to find the number of strictly increasing sequences p1,p2,…,pk , such that: For each i (1≤i≤k), spi a.For each i(1≤i<k), there is…...

高级测试工程师必备技术:用Git版本控制自动化测试代码

初识Git版本控制 自动化测试代码反复执行&#xff0c;如果借用持续集成工具会提高测试效率&#xff0c;那么需要我们把自动化测试代码发布到正式环境中&#xff0c;这时候用Git版本控制工具高效、稳定、便捷。 分布式版本控制 Git可以把代码仓库完整地镜像下来&#xff0c;有…...

【晶振专题】案例:为什么对着STM32低速32.768K晶振哈口气就不起振了?

本案例发现在一个工装产品上,首批一共做了10几台样机。发现有的样机在开机的时候读取不到RTC,有的样机却可以。读不到RTC是概率性出现的,发生在第一次上电的情况。开始他怀疑是环境问题,会不会和温度有关,于是同事在家做了大量的实验,发现对晶振吹口气就能让晶振不起振,…...

Gym - 101986B Parallel Lines dfs暴力

链接&#xff1a;点击查看 题意&#xff1a;偶数个点&#xff0c;两点可连成一条线&#xff0c;求平行线最大对数 题解&#xff1a;当时想的时候傻逼了&#xff0c;想成了每次选两个点就是16*15/2 * 14*13/2 ..... 其实不需要这样&#xff0c;因为每个点必须要匹配一个的&…...