kubernetes scheduler 源码解析及自定义资源调度算法实践
kubernetes scheduler 浅析
什么是kubernetes scheduler?
小到运行着几十个工作负载的 kubernetes 集群,大到运行成千上万个工作负载 kubernetes 集群,每个工作负载到底应该在哪里运行,这需要一个聪明的大脑进行指挥,kubernetes scheduler 就是这个聪明的大脑。从结果看,他的工作很简单,只是为 pod.spec.nodeName 填充上一个 node 的名字而已,从过程看,他又是极其复杂的,因为到底要选哪个节点才最合理,答案往往是和场景强相关的,几乎找不到一套适应各种场景的调度算法。因此,各式各样的算法插件也层出不穷,公司对调度算法的定制化开发也成了常见需求。
调度器如何运行的?
一个调度器主要是由这样两个大循环构成
- 第一个控制循环负责从 etcd 里读取未被调度的 pod,添加到调度队列。
- 第二个控制循环的主要逻辑,就是不断地从调度队列里出队一个 pod。然后,调用 Predicates 算法进行“过滤”。这一步“过滤”得到的一组 Node,就是所有可以运行这个 Pod 的宿主机列表。接下来,调度器就会再调用 Priorities 算法为上述列表里的 Node 打分,分数从 0 到 100。得分最高的 Node,就会作为这次调度的结果。调度算法执行完成后,调度器就需要将 Pod 对象的 nodeName 字段的值,修改为上述 Node 的名字。
调度器是如何保障同一个控制器的不同pod副本尽量不要在同一个node上的?
结合我们上面分析的调度器的结构,很容易可以联想到,调度器应该就是在打分阶段,根据节点的不同情况(是否存在相同副本?存在几个?)进行打分,来保证尽量分散 pod 的。没错,调度器确实就是这么做的,不过这个打分的过程又被分成了三个阶段,我们可以通过分析这部分的代码来了解其原理。 源码传送门
第一阶段:PreScore
func (pl *SelectorSpread) PreScore(ctx context.Context, cycleState *framework.CycleState, pod *v1.Pod, nodes []*v1.Node) *framework.Status {if skipSelectorSpread(pod) {return nil}var selector labels.Selectorselector = helper.DefaultSelector(pod,pl.services,pl.replicationControllers,pl.replicaSets,pl.statefulSets,)state := &preScoreState{selector: selector,}cycleState.Write(preScoreStateKey, state)return nil
}
复制代码
func DefaultSelector(pod *v1.Pod, sl corelisters.ServiceLister, cl corelisters.ReplicationControllerLister, rsl appslisters.ReplicaSetLister, ssl appslisters.StatefulSetLister) labels.Selector {labelSet := make(labels.Set)// Since services, RCs, RSs and SSs match the pod, they won't have conflicting// labels. Merging is safe.if services, err := GetPodServices(sl, pod); err == nil {for _, service := range services {labelSet = labels.Merge(labelSet, service.Spec.Selector)}}if rcs, err := cl.GetPodControllers(pod); err == nil {for _, rc := range rcs {labelSet = labels.Merge(labelSet, rc.Spec.Selector)}}selector := labels.NewSelector()if len(labelSet) != 0 {selector = labelSet.AsSelector()}if rss, err := rsl.GetPodReplicaSets(pod); err == nil {for _, rs := range rss {if other, err := metav1.LabelSelectorAsSelector(rs.Spec.Selector); err == nil {if r, ok := other.Requirements(); ok {selector = selector.Add(r...)}}}}if sss, err := ssl.GetPodStatefulSets(pod); err == nil {for _, ss := range sss {if other, err := metav1.LabelSelectorAsSelector(ss.Spec.Selector); err == nil {if r, ok := other.Requirements(); ok {selector = selector.Add(r...)}}}}return selector
}
复制代码
在方法签名里的 cycleState 作为调度不同阶段传递数据的一个仓库,这一阶段做的事情并不多,就是把所有包含 pod label 的控制器找出来,并保存到 cycleState,供下一阶段使用。需要注意的是 pod 的控制器必须是 Services、ReplicaSets 和 StatefulSets,replicationControllers 其中之一。
第二阶段:Score
func (pl *SelectorSpread) Score(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) {if skipSelectorSpread(pod) {return 0, nil}c, err := state.Read(preScoreStateKey)if err != nil {return 0, framework.NewStatus(framework.Error, fmt.Sprintf("Error reading %q from cycleState: %v", preScoreStateKey, err))}s, ok := c.(*preScoreState)if !ok {return 0, framework.NewStatus(framework.Error, fmt.Sprintf("%+v convert to tainttoleration.preScoreState error", c))}nodeInfo, err := pl.sharedLister.NodeInfos().Get(nodeName)if err != nil {return 0, framework.NewStatus(framework.Error, fmt.Sprintf("getting node %q from Snapshot: %v", nodeName, err))}count := countMatchingPods(pod.Namespace, s.selector, nodeInfo)return int64(count), nil
}
复制代码
func skipSelectorSpread(pod *v1.Pod) bool {return len(pod.Spec.TopologySpreadConstraints) != 0
}
复制代码
func countMatchingPods(namespace string, selector labels.Selector, nodeInfo *framework.NodeInfo) int {if len(nodeInfo.Pods) == 0 || selector.Empty() {return 0}count := 0for _, p := range nodeInfo.Pods {// Ignore pods being deleted for spreading purposes// Similar to how it is done for SelectorSpreadPriorityif namespace == p.Pod.Namespace && p.Pod.DeletionTimestamp == nil {if selector.Matches(labels.Set(p.Pod.Labels)) {count++}}}return count
}
复制代码
- 首先判断了 pod 是否设置了拓扑约束,如果设置了,直接就得0分。
- 如果没设置,就交给一个叫做 countMatchingPods 的函数来计算分数。计算方法就是看在一个 node 上,同命名空间下,并且不是处于删除状态的 pod,与当前要调度的 Pod 的 label 相同的有多少个,1个=1分,累积起来为当前节点的分。
第三阶段:NormalizeScore
这一阶段的作用为对 Score 阶段的得分做一个修正,修正后的结果才会作为节点的最终得分。具体代码如下。
func (pl *SelectorSpread) NormalizeScore(ctx context.Context, state *framework.CycleState, pod *v1.Pod, scores framework.NodeScoreList) *framework.Status {if skipSelectorSpread(pod) {return nil}countsByZone := make(map[string]int64, 10)maxCountByZone := int64(0)maxCountByNodeName := int64(0)for i := range scores {if scores[i].Score > maxCountByNodeName {maxCountByNodeName = scores[i].Score}nodeInfo, err := pl.sharedLister.NodeInfos().Get(scores[i].Name)if err != nil {return framework.NewStatus(framework.Error, fmt.Sprintf("getting node %q from Snapshot: %v", scores[i].Name, err))}zoneID := utilnode.GetZoneKey(nodeInfo.Node())if zoneID == "" {continue}countsByZone[zoneID] += scores[i].Score}for zoneID := range countsByZone {if countsByZone[zoneID] > maxCountByZone {maxCountByZone = countsByZone[zoneID]}}haveZones := len(countsByZone) != 0maxCountByNodeNameFloat64 := float64(maxCountByNodeName)maxCountByZoneFloat64 := float64(maxCountByZone)MaxNodeScoreFloat64 := float64(framework.MaxNodeScore)for i := range scores {// initializing to the default/max node score of maxPriorityfScore := MaxNodeScoreFloat64if maxCountByNodeName > 0 {fScore = MaxNodeScoreFloat64 * (float64(maxCountByNodeName-scores[i].Score) / maxCountByNodeNameFloat64)}// If there is zone information present, incorporate itif haveZones {nodeInfo, err := pl.sharedLister.NodeInfos().Get(scores[i].Name)if err != nil {return framework.NewStatus(framework.Error, fmt.Sprintf("getting node %q from Snapshot: %v", scores[i].Name, err))}zoneID := utilnode.GetZoneKey(nodeInfo.Node())if zoneID != "" {zoneScore := MaxNodeScoreFloat64if maxCountByZone > 0 {zoneScore = MaxNodeScoreFloat64 * (float64(maxCountByZone-countsByZone[zoneID]) / maxCountByZoneFloat64)}fScore = (fScore * (1.0 - zoneWeighting)) + (zoneWeighting * zoneScore)}}scores[i].Score = int64(fScore)}return nil
}
复制代码
- 首先还是判断了 pod 是否设置了拓扑约束,如果设置了,得分为0,这一点和上一阶段完全相同。
- 然后找出得分最高的节点和 zone
- 节点的最终得分为100 *(最大节点得分-当前节点得分)/ 最大节点得分,所以当前节点得分越高,最终得分就越低,这是符合尽量分散的预期的。
- 如果是 zone 的情况
- 当前 zone 的得分 = 100*(最大 zone 的得分-当前 zone 的得分)/最大 zone 的得分
- 最终节点维度得分 = 上一步的节点得分 * (1- zone 维度的权重)+ zone 维度的得分* zone 维度的权重
什么是zone?
这个概念在调度的过程中是为了适配某几个节点视为一个整体的情况,比如 nodeA 和 nodeB 同属于一个 zone, 那么在调度算法中,会在一定程度是同时考虑这两个节点是否存在相同副本。那么一个 node 到底属于哪个 zone 呢,我在源码中找到了这样一段代码。
func GetZoneKey(node *v1.Node) string {labels := node.Labelsif labels == nil {return ""}// TODO: prefer stable labels for zone in v1.18zone, ok := labels[v1.LabelZoneFailureDomain]if !ok {zone, _ = labels[v1.LabelZoneFailureDomainStable]}// TODO: prefer stable labels for region in v1.18region, ok := labels[v1.LabelZoneRegion]if !ok {region, _ = labels[v1.LabelZoneRegionStable]}if region == "" && zone == "" {return ""}// We include the null character just in case region or failureDomain has a colon// (We do assume there's no null characters in a region or failureDomain)// As a nice side-benefit, the null character is not printed by fmt.Print or glogreturn region + ":\x00:" + zone
}
复制代码
这说明两个 node 是否是在同一个 zone,取决于它的 label。
资源碎片问题怎么解决?
举个例子来说明一下这个问题,如同下图这样,我有两个节点分别剩余1GPU,但是当我想要创建一个 2GPU 的 pod 出来,却找不到能调度的节点。
解决思路
如果我们优先把一个 node 占满,再去往另一个 pod 上调度,就能在一定程度上解决这个问题。 具体算法就是如下。
实现方式
和调度器自身解决同一控制器下的 pod 尽量分散的方式一样,我们这个算法也是对 node 的打分进行干预。因此,可以很容易想到,把我们的算法插入进调度器就好了。那么这项工作该怎么进行呢?下面将介绍业内主流的方案。
扩展调度器的标准流程:Scheduling Framework
调度器扩展的方式可以总结为一下四种
- 直接改 kube-scheduler 的源码
- 额外部署完全独立的调度器
- 调度器扩展程序
- Scheduling Framework 前面三种已经不推荐使用了,因此这里只介绍最后一种。 还记得我们在文章开头的那两个控制循环吧,扩展的突破口,就在第二个循环内部。它可以放大成下面这张图。
○ 大致可分为两部分 ■ 调度过程:为 pod 选择出 node ● 绿色区域是同步的,同一时刻只能有一个 pod 在被调度 ■ 绑定过程:把 pod 放到选出的 node 上 ● 黄色区域是异步的,同一时刻可以有多个 pod 在被绑定 ○ 调度过程和绑定过程遇到如下情况时会中途退出,这个时候,该 Pod 将被放回到 待调度队列,并等待下次重试 ■ 调度程序认为当前没有该 Pod 的可选节点 ■ 内部错误 调度器一共为我们提供了12个接口,实现一个接口对应的是完成某一阶段的自定义调度逻辑,这12个接口的作用分别是。 - QueueSort:定义哪个 pod 先调度
- Pre-filter:强制筛选的一部分,信息预处理。
- Filter:强制筛选掉绝对不能调度的 node。
- Post-filter:可以用来做一些通知,因为此时已经筛选掉了绝对不能调度的节点。留下的都是能运行该 pod 的节点,只是此时还不能确定最佳节点是谁。
- PreScore:打分前的预处理,上面调度器源码的分析中,实现者在这里保存了 pod 控制器的数据,供后面的阶段使用。
- Score:为每个 node 进行打分。
- NormalizeScoring:修改上一步的得分,作为最终得分。需要注意的是,如果你在上一步的得分不在[0-100]这个区间,那么你必须实现这个接口。
- Reserve:在绑定之前为pod预留资源,因为 pod 的绑定过程是异步的,上图黄色部分,这样可以避免资源超出的情况。如果绑定失败,会触发 UnReserve 释放资源。
- Permit:阻止或者延迟 pod 的绑定。
- Pre-bind:绑定前的预处理,比如处理 pod 依赖的网络资源。
- bind:扩展用于将 pod 绑定到节点上。
- Unreserve:因为在 reserve 阶段为 pod 预留了资源,所以如果 pod 绑定失败,需要在该节点释放预留的资源。
代码里如何体现
下面我基于kube-scheduler 1.19做一个简单的demo
- 第一步实现调度器插件
package pkgimport (v1 "k8s.io/api/core/v1""k8s.io/apimachinery/pkg/runtime""context"framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
)type demoPlugin struct {}func NewDemoPlugin(_ runtime.Unknown, _ framework.FrameworkHandle) (framework.Plugin, error) {return &demoPlugin{}, nil
}
// Name returns name of the plugin. It is used in logs, etc.
func (d *demoPlugin) Name() string {return "demo-plugin"
}
func (d *demoPlugin) Score(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) {return 100,nil
}
// ScoreExtensions of the Score plugin.
func (d *demoPlugin) ScoreExtensions() framework.ScoreExtensions {return nil
}
复制代码
实现了 Name Score ScoreExtensions 这三个接口,就完成了一个最简单的调度器插件,这里我默认的就返回每个节点都是100分。
- 第二步,把这个插件注册到调度器
package mainimport ("git.cai-inc.com/devops/zcy-scheduler/pkg/demo""math/rand""os""time""k8s.io/component-base/logs""k8s.io/kubernetes/cmd/kube-scheduler/app")func main() {rand.Seed(time.Now().UnixNano())command := app.NewSchedulerCommand(app.WithPlugin(noderesources.AllocatableName, demo.NewDemoPlugin),)logs.InitLogs()defer logs.FlushLogs()if err := command.Execute(); err != nil {os.Exit(1)}
}
复制代码
这里我们可以直接引用 kubernetres/cmd/kube-scheduler/app 下的代码,里面有一个 NewSchedulerCommand, 在这里就可以把我们的自定义插件注册进去。
- 第三步,编写配置文件。
apiVersion: kubescheduler.config.k8s.io/v1beta1
kind: KubeSchedulerConfiguration
leaderElection:leaderElect: false
clientConnection:kubeconfig: "/Users/zcy/newgo/zcy-scheduler/scheduler.conf"
profiles:
- schedulerName: zcy-schedulerplugins:score:enabled:- name: demo-plugindisabled:- name: "*"
复制代码
在这里给你的调度器起一个名字,并且设定了在 score 阶段使用 demo-plugin 插件。以上就是实现一个调度器的基本方式了。
总结
今天的文章介绍了,scheduler 的结构,主要就是两个循环。怎么保证 pod 的副本分散到不同节点上,以及怎么解决资源碎片的问题。最后还介绍了如何开发自己的调度器,希望能给大家开发自定义调度器时提供一些思路。
参考资料
- www.qikqiak.com/post/custom…
- github.com/kubernetes-…
- time.geekbang.org/column/arti…
推荐阅读
Guava Cache实战—从场景使用到原理分析
详解 HTTP2.0 及 HTTPS 协议
初识 JVM(带你从不同的视角认识 JVM)
招贤纳士
政采云技术团队(Zero),一个富有激情、创造力和执行力的团队,Base 在风景如画的杭州。团队现有300多名研发小伙伴,既有来自阿里、华为、网易的“老”兵,也有来自浙大、中科大、杭电等校的新人。团队在日常业务开发之外,还分别在云原生、区块链、人工智能、低代码平台、中间件、大数据、物料体系、工程平台、性能体验、可视化等领域进行技术探索和实践,推动并落地了一系列的内部技术产品,持续探索技术的新边界。此外,团队还纷纷投身社区建设,目前已经是 google flutter、scikit-learn、Apache Dubbo、Apache Rocketmq、Apache Pulsar、CNCF Dapr、Apache DolphinScheduler、alibaba Seata 等众多优秀开源社区的贡献者。如果你想改变一直被事折腾,希望开始折腾事;如果你想改变一直被告诫需要多些想法,却无从破局;如果你想改变你有能力去做成那个结果,却不需要你;如果你想改变你想做成的事需要一个团队去支撑,但没你带人的位置;如果你想改变本来悟性不错,但总是有那一层窗户纸的模糊……如果你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的自己。如果你希望参与到随着业务腾飞的过程,亲手推动一个有着深入的业务理解、完善的技术体系、技术创造价值、影响力外溢的技术团队的成长过程,我觉得我们该聊聊。任何时间,等着你写点什么,发给 zcy-tc@cai-inc.com
相关文章:
![](https://img-blog.csdnimg.cn/img_convert/a6a34b14fc8afcd34a6be20459e794e7.webp?x-oss-process=image/format,png)
kubernetes scheduler 源码解析及自定义资源调度算法实践
kubernetes scheduler 浅析 什么是kubernetes scheduler? 小到运行着几十个工作负载的 kubernetes 集群,大到运行成千上万个工作负载 kubernetes 集群,每个工作负载到底应该在哪里运行,这需要一个聪明的大脑进行指挥,…...
![](https://www.ngui.cc/images/no-images.jpg)
MySQL插入数据
目录 一、怎么样插入数据 二、通过命令提示窗口插入数据 三、使用PHP脚本插入数据 一、怎么样插入数据 在MySQL 表中可以使用 INSERT INTO SQL语句来插入数据。 可以通过 mysql> 命令提示窗口中向数据表中插入数据,或者通过PHP脚本来插入数据。 下面是向MyS…...
![](https://img-blog.csdnimg.cn/img_convert/736743a343abe6b4ab738b2c751645bd.png)
从GPT-4、文心一言再到Copilot,AIGC卷出新赛道?
业内人都知道,上一周是戏剧性的,每一天,都是颠覆各个行业,不断 AI 化的新闻。 OpenAI发布GPT-4、百度发布文心一言、微软发布Microsoft 365 Copilot 三重buff叠加,打工人的命运可以说是跌宕起伏,命途多舛了…...
![](https://www.ngui.cc/images/no-images.jpg)
1.2 从0开始学Unity游戏开发--运行原理
在我开始学习游戏开发的时候,有了好多年的客户端开发经验,并且刚毕业那会还使用cocos2dx做过一点小的2d横版过关游戏,因此对我来说做游戏开发到底是做什么还是比较清晰的,但是如果从来没做过游戏开发,甚至连客户端开发也没怎么做过的人可能没那么好理解游戏到底是怎么运作…...
![](https://img-blog.csdnimg.cn/e5a9fa36f8f34169a76eefc1115b585e.gif)
【微信小程序】如何获得自己当前的定位呢?本文利用逆地址解析、uni-app带你实现
目录 前言 效果展示 一、在腾讯定位服务配置微信小程序JavaScript SDK 二、使用uni-app获取定位的经纬度 三、 逆地址解析,获取精确定位 四、小提示 前言 效果展示 一、在腾讯定位服务配置微信小程序JavaScript SDK 在浏览器搜索腾讯定位服务,找…...
![](https://img-blog.csdnimg.cn/c3f8d115c8e847dfa7dd0820ce54b11a.png)
92年程序员发帖晒薪资称自己很迷茫,网友:老弟你可以了
当下,是一个“向钱看,向厚赚”的社会。快节奏的生活下,家庭、工作各方面压力很容易使年轻人陷入迷茫和焦虑。 与其他行业相比,程序员的高薪让人羡慕。那么,对于那些真正达到这么多收入的人来说,他们是怎么…...
![](https://img-blog.csdnimg.cn/img_convert/5f7da0ae4f7176dcfb7707cf32b00aff.png)
阿里四面,居然栽在一道排序算法上
前言 算法是程序的灵魂,一个优秀的程序是可以在海量的数据中,仍保持高效计算。目前各大厂的面试要求也越来越高,算法肯定会要去。如果你不想去大厂,只想去小公司,或许并不需要要求算法。但是你永远只能当一个代码工人&…...
![](https://img-blog.csdnimg.cn/img_convert/29150ed4f4c2a553f5342c28f0e79130.jpeg)
macOS 13.3(22E252)/12.6.4/11.7.5正式版发布
系统介绍 3 月 28 日消息,苹果今日向 Mac 电脑用户推送了 macOS 13.3 更新(内部版本号:22E252)苹果今天还发布了macOS Monterey 12.6.4和macOS Big Sur 11.7.5,本次更新距离上次发布隔了 42 天。 macOS Ventura 带来…...
![](https://img-blog.csdnimg.cn/17bbe91d69d1443da9d0214f6d8b118d.png)
MPP数据库简介及架构分析
目录什么是MPP?特性并行处理超大规模数据仓库真正适合什么典型的分析工作量数据集中化线性可伸缩性MPP架构技术特性数据库架构分析Shared EverythingShared DiskShare MemoryShared NothingShared Nothing数据库架构优势什么是MPP? MPP (Massively Paral…...
![](https://img-blog.csdnimg.cn/ac3e684a15aa4b82ae51ed734eee62fe.png)
centos7配置pytorch和tensorflow
1、安装anaconda 1.1镜像源下载对应anaconda版本后传到服务器上 1.2进入对应文件夹 首先赋权再执行安装程序 chmod x Anaconda3-2022.10-Linux-x86_64.sh ./Anaconda3-2022.10-Linux-x86_64.sh chmod x Anaconda3-2022.10-Linux-x86_64.sh 1.3交互确认 确认许可协议&…...
![](https://img-blog.csdnimg.cn/6a19cbafe7f7489b9d41817539763e42.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5b-15YWu5Li6576O,size_20,color_FFFFFF,t_70,g_se,x_16)
Kafka在Mac下的安装与使用
mac 安装kafka安装kafka的原因安装kafka启动Zookeeper启动Kafka创建topic查看topic生产数据消费数据关闭zookeeper关闭kafka测试安装kafka的原因 用户微服务登录后需要向广告微服务中发送用户登录的信息以获取用户画像(这个过程是异步的),故…...
![](https://img-blog.csdnimg.cn/c7e845499d1949d4b9bbee21da2d9a5e.png)
AndroidStudio相对布局
目录 RelativeLayout常用属性(它们可以几个结合在一起使用): 相对于父容器居中 相对于父容器对齐 相对于其它控件位置 相对于其它控件对齐 标识符问题 实例演示 RelativeLayout类是ViewGroup的子类也就是相对布局 RelativeLayout常用属…...
![](https://img-blog.csdnimg.cn/8ed7071dd1d9406295fc697c404ebb0c.png)
如何用iOS自带摄像头进行拍摄获取视频流以及OpenCV图像处理实时显示
目录概述一、如何用Swift调用OpenCV库1.项目引入OpenCV库2.桥接OpenCV及Swift二、运用AVFoundation获取实时图像数据1.建立视频流数据捕获框架2.建立 Capture Session3.取得并配置 Capture Devices4.设定 Device Inputs5.配置Video Data Output输出6.工程隐私权限配置7.处理相机…...
![](https://img-blog.csdnimg.cn/847c6eda613643c48aef14781b01f872.png)
智安网络|为什么说防火墙是我们信息安全的第一道防线?
网络安全现状: ①攻击者需要的技术水平逐渐降低,手段更加灵活,联合攻击急你的剧增多:网络蠕虫具有隐蔽性、传染性、破坏性、自主攻击能力,新一代网络蠕虫和黑客攻击、计算机病毒之间的界限越来越模糊 ②网络攻击趋利…...
![](https://img-blog.csdnimg.cn/img_convert/610f2c2a637738bb96239646ead2c275.png)
Android多媒体功能开发(8)——使用VideoView控件播放视频
Android播放视频类主要有两种方式: VideoView控件SurfaceView控件MediaPlayer VideoView是SurfaceView的子类,实际上VideoView相当于SurfaceView MediaPlayer。SurfaceView支持的功能VideoView都支持。也可用VideoViewMediaPlayer的方式播放。 视频播放…...
![](https://www.ngui.cc/images/no-images.jpg)
python调用CC++
python调用C程序 一般来说在python调用C/C程序主要可以分为3步: 1、编写C/C实现程序。2、将C/C程序编译成动态库。-3、在Python中调用编译生成的库。Python在调用C/C程序时有一些不同,需要注意。 Python调用C语言程序比较简单,将C语言程序…...
![](https://img-blog.csdnimg.cn/efa06ed2380f49659e18e77cf652bd59.png)
[golang gin框架] 10.Gin 商城项目介绍
一.商城项目介绍 1.详细功能介绍图 2.数据库 ER 图 需要用到的数据表举例 二.MVC架构搭建以及执行流程分析 1.关于 MVC 模式的简单介绍 Gin 不是一个 MVC 的框架,所有的代码都可以写在 main.go 中。当我们的项目比较大的时候, 所有代码写在一个文件里面…...
![](https://img-blog.csdnimg.cn/89e65476163f4e1cb7e76f0dcdc1b73f.png)
Endor Labs:2023年十大开源安全风险
近日,Endor Labs发布了一份新报告,确定了2023年的十大开源安全风险。报告显示,许多软件公司依赖于开源软件代码,但在如何衡量和处理与开源软件相关的风险和漏洞方面缺乏一致性。调查发现,在应用程序中超过80%的代码可能…...
![](https://www.ngui.cc/images/no-images.jpg)
关于Error和Exception的一些思考 小结
目录 1. ERROR 2. Exception 2.1 checked Exception 2.2 unchecked Exception 2.3 区别 3. 内存溢出 3.1 堆溢出 3.2 永久代/元空间溢出 3.3 方法栈溢出 Java中,所有的异常都有一个共同的父类:Throwable类。 Throwable类有两个重要的子类&#…...
![](https://img-blog.csdnimg.cn/8c5801c2970843beaa1fe85a5d102f63.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBAbmdfZWx6YQ==,size_20,color_FFFFFF,t_70,g_se,x_16)
Mac环境变量配置(Java)
1.打开终端: 2.输入命令:【/usr/libexec/java_home -V】,查看默认的jdk下载地址(绿色下划线的就是jdk默认路径)(注意⚠️:命令行终端是区分大小写的【-v 是不对的,必须是大写 -V】) …...
![](https://img-blog.csdnimg.cn/9a082b093214405cb7fcd6c90a779a05.png)
通过这三个文件彻底搞懂rocketmq的存储原理
前言 RocketMQ是阿里开发的一个高性能的消息队列,支持各种消息类型,而且支持事务消息,可以说是现在的很多系统中的香饽饽了,所以呢,怎么使用大家肯定是要学习的 我们作为一个有梦想的程序员,在学习一门技…...
![](https://img-blog.csdnimg.cn/c82aef8133bd43969467d2e71db51344.png)
Linux安装Nvidia显卡驱动
使用的Linux系统为 Ubuntu 18.04,显卡为GeForce RTX 3060 。 查看ubuntu版本号命令:sudo lsb_release -a 查看显卡型号命令:lspci | grep -i vga (详细查看方法: 查看显卡型号)。 下面是安装显卡驱动步…...
![](https://img-blog.csdnimg.cn/img_convert/4ba23be9bcc32644dec3998b2a4f3c2a.png)
GPT-4 介绍
1 简介 本文根据openAI的2023年3月的《GPT-4 Technical Report 》翻译总结的。 原文地址:https://arxiv.org/pdf/2303.08774.pdf 原文确实没有GPT-4 具体的模型结构,openAI向盈利组织、非公开方向发展了。也没透露硬件、训练成本、训练数据、训练方法等…...
![](https://www.ngui.cc/images/no-images.jpg)
Ubuntu下单机安装Hadoop详细教程(附所需安装包下载)
目录 前言 一、创建Hadoop用户 二、更新apt和安装Vim编辑器 三、安装SSH和配置SSH无密码登录 四、安装Java环境 1. 安装JDK 2. 配置JDK环境 3. 检验安装 五、安装单机Hadoop 1. 下载安装Hadoop 2. 运行示例 总结 前言 本文安装的 Hadoop 及 Java 环境基于林子雨老…...
![](https://csdnimg.cn/release/blog_editor_html/release2.2.4/ckeditor/plugins/CsdnLink/icons/icon-default.png?t=N2N8)
【嵌入式烧录/刷写文件】-2.1-详解Intel Hex格式文件
目录 1 什么是Intel Hex 2 Intel Hex的格式 2.1 Intel Hex的Record结构 2.1.1 “Record type记录类型”的说明 2.1.2 “Record length记录长度”的说明 2.1.3 如何计算“Checksum校验和” 2.2 Record order记录顺序 2.3 Text line terminators文本行终止符 3 Hex文件的…...
![](https://img-blog.csdnimg.cn/3b6b4986142d43b583f46d00ab94ca48.png)
【云原生】初识 Kubernetes — pod 的前世今生
目录标题前言🐳 Kubernetes到底是什么?🐬 K8s 的由来🐬K8s 的工作方式🐬 K8s 主要组件🐋Master 组件🐋Node 组件🐳 pod 是什么?🐬pod 的概念🐬控制…...
![](https://www.ngui.cc/images/no-images.jpg)
【基础篇】Java类加载器详解
类加载过程详解 类的生命周期 类从被加载到虚拟机内存到开始卸载出内存为止,生命周期可以简单概括为7个阶段:加载(Loading)、验证(Verification)、准备(Preparation)、解析ÿ…...
![](https://img-blog.csdnimg.cn/105ee2af248a463a8dbb476ae38dc4b7.png#pic_center)
Pytorch动手实现Transformer机器翻译
Pytorch动手实现Transformer机器翻译前言一、环境配置1. torchtextMethod1:Method2:2. Spacy以en包下载为例:手动安装语言包到spacy3. NLTKMethod1:Method2:二、运行结果1. 模型训练(train)2. 翻…...
![](https://img-blog.csdnimg.cn/img_convert/5004cadee3468695fe0b25a19573876e.png)
宝塔面板部署node+vue项目注意事项
宝塔面板部署nodevue项目注意事项 宝塔连接云服务器 如果服务器上没有安装宝塔面板,需要先安装,安装流程如下: 从宝塔官网主页进去,点击下载安装,然后点击在线安装 输入服务器IP和密码在服务器上安装宝塔面板 等待一…...
![](https://img-blog.csdnimg.cn/46d233e410104aeeae96c760f381b94a.png)
【LeetCode】剑指 Offer 39. 数组中出现次数超过一半的数字 p205 -- Java Version
题目链接:https://leetcode.cn/problems/shu-zu-zhong-chu-xian-ci-shu-chao-guo-yi-ban-de-shu-zi-lcof/ 1. 题目介绍(39. 数组中出现次数超过一半的数字) 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。 你可…...
![](/images/no-images.jpg)
临沂网站建设制作/推广竞价托管公司
对于学习ARM的朋友来说,中断处理是一块硬骨头,尤其是中断向量表以及中断的跳转。下面主要对中断向量表的建立问题及重映射问题进行探讨。近来做一些东西用到中断时,总会出现一些问题。各种Bootloader的初始化相关代码摘要,首先来看下面的程序…...
![](/images/no-images.jpg)
免费的wordpress主题/怎么成为百度推广代理商
Microsoft Secure Content Downloader我们这边网速本来非常非常慢,可是用它这个专用的下载工具,今天早晨竟然达到了2Mbps。即便在现在的网速最慢的先后,它也是500Kbps。相当不错,想下VS2008Beta2的同志们建议用它来下载࿰…...
![](/images/no-images.jpg)
大型网站开发 书籍/推广一次多少钱
/* author simon */例:数据库:NCDB2用户 :DB2ADMIN/DB2ADMIN备份库路径:D:/bank一.恢复数据库1.启动数据库运行-》db2cmd-》db2Db2>start db managerDb2>force application allDb2>drop database tjnsdb2 >2.创建数据…...
![](https://pic002.cnblogs.com/images/2012/256729/2012011222414274.jpg)
怎么样做企业网站/windows优化大师是病毒吗
今天尝试了SharePoint2010网站的备份和还原,从网上搜一些文档看看,然后自己试试,感觉和2007的操作没什么太大的区别,跟大家分享下自己尝试的命令行方式和数据库的方式,2007自己常用的也是这两种方式。 1、 命令行的方式…...
![](/images/no-images.jpg)
e福州怎么代缴医保/seo搜索引擎推广
灵动微MM32SPIN系列MCU产品是针对直流无刷电机驱动量身打造,由灵动微电机团队提供专业的定制化电机驱动芯片创新技术和精准控制马达方案,可帮助客户提升产品竞争力,搭配客户不同的应用方案及环境的需求,还能提供高集成度、高整合度…...
![](/images/no-images.jpg)
深圳手机网站公司/推广是什么意思
// 前导0 function padLeftZero(str) {return (00${str}).substring(str.length) }/*** 日期格式化为本地日期* param {str} time 时间戳* param {str} fmt 格式* return {str} 格式后日期*/ export const formatDate (time, fmt) > {const date new Date(time);const yea…...