【K8S源码之Pod漂移】整体概况分析 controller-manager 中的 nodelifecycle controller(Pod的驱逐)

参考

  • k8s 污点驱逐详解-源码分析 - 掘金

  • k8s驱逐篇(5)-kube-controller-manager驱逐 - 良凯尔 - 博客园

  • k8s驱逐篇(6)-kube-controller-manager驱逐-NodeLifecycleController源码分析 - 良凯尔 - 博客园

  • k8s驱逐篇(7)-kube-controller-manager驱逐-taintManager源码分析 - 良凯尔 - 博客园

整体概况分析

  • 基于 k8s 1.19 版本分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h6S3bs1J-1692352728103)(img/nodelifecycle笔记/image-20230818164014721.png)]

TaintManager 与 非TaintManager

  1. TaintManager 模式
    • 发现 Node Unhealthy 后(也就是 Node Ready Condition = False 或 Unknown),会更新 Pod Ready Condition 为 False(表示 Pod 不健康),也会给 Node 打上 NoExecute Effect 的 Taint
    • 之后 TaintManager 根据 Pod 的 Toleration 判断,是否有设置容忍 NoExecute Effect Taint 的 Toleration
      • 没有 Toleration 的话,就立即驱逐
      • 有 Toleration ,会根据 Toleration 设置的时长,定时删除该 Pod
      • 默认情况下,会设置个 5min 的Toleration,也就是 5min 后会删除此 Pod
  2. 非 TaintManager 模式(默认模式)
    • 发现 Node Unhealthy 后,会更新 Pod Ready Condition 为 False(表示 Pod 不健康)
    • 之后会记录该 Node,等待 PodTimeout(5min) - nodegracePeriod(40s) 时间后,驱逐该 Node 上所有 Pod(Node级别驱逐),之后标记该 Node 为 evicted 状态(此处是代码中标记,资源上没有此状态)
    • 之后便只考虑单 Pod 的驱逐(可能考虑部分 Pod 失败等)
      • 若 Node 已经被标记为 evicted 状态,那么可以进行单 Pod 的驱逐
      • 若 Node 没有被标记为 evicted 状态,那将 Node 标记为 tobeevicted 状态,等待上面 Node 级别的驱逐

代码中的几个存储结构

nodeEvictionMap *nodeEvictionMap// nodeEvictionMap stores evictionStatus *data for each node.
*type nodeEvictionMap struct {
lock sync.Mutex
nodeEvictions map[string]evictionStatus
}
记录所有 node 的状态
1. 健康 unmarked
2. 等待驱逐 tobeevicted
3. 驱逐完成 evicted
zoneStates map[string]ZoneStatetype ZoneState string记录 zone 的健康状态
1. 新zone Initial
2. 健康的zone Normal
3. 部分健康zone PartialDisruption
4. 完全不健康 FullDisruption
这个是用于设置该zone 的驱逐速率
zonePodEvictor map[string]*scheduler.RateLimitedTimedQueue失联(不健康)的 Node 会放入此结构中,等待被驱逐,之后nodeEvictionMap 对应的状态记录会被设置为 evicted
1. 该结构,key 为zone,value 为限速队列处理(也就是上面驱逐效率起作用的地方)
2. 当一个 node 不健康,首先会计算出该 node 对应的zone
3. 然后放入该结构中
nodeHealthMap *nodeHealthMaptype nodeHealthMap struct {
lock sync.RWMutex
nodeHealths map[string]*nodeHealthData
}
type nodeHealthData struct {
probeTimestamp metav1.Time
readyTransitionTimestamp metav1.Time
status *v1.NodeStatus
lease *coordv1.Lease
}
记录每个node的健康状态,主要在 monitorHealth 函数中使用
1. 其中 probeTimestamp 最关键,该参数记录该 Node 最后一次健康的时间,也就是失联前最后一个 lease 的时间
2. 之后根据 probeTimestamp 和宽限时间 gracePeriod,判断该 node 是否真正失联,并设置为 unknown 状态

整体代码流程分析

// Run starts an asynchronous loop that monitors the status of cluster nodes.
func (nc *Controller) Run(stopCh <-chan struct{}) {defer utilruntime.HandleCrash()
​klog.Infof("Starting node controller")defer klog.Infof("Shutting down node controller")// 1.等待leaseInformer、nodeInformer、podInformerSynced、daemonSetInformerSynced同步完成。if !cache.WaitForNamedCacheSync("taint", stopCh, nc.leaseInformerSynced, nc.nodeInformerSynced, nc.podInformerSynced, nc.daemonSetInformerSynced) {return}// 2.如果enable-taint-manager=true,开启nc.taintManager.Runif nc.runTaintManager {go nc.taintManager.Run(stopCh)}// Close node update queue to cleanup go routine.defer nc.nodeUpdateQueue.ShutDown()defer nc.podUpdateQueue.ShutDown()// 3.执行doNodeProcessingPassWorker,这个是处理nodeUpdateQueue队列的node// Start workers to reconcile labels and/or update NoSchedule taint for nodes.for i := 0; i < scheduler.UpdateWorkerSize; i++ {// Thanks to "workqueue", each worker just need to get item from queue, because// the item is flagged when got from queue: if new event come, the new item will// be re-queued until "Done", so no more than one worker handle the same item and// no event missed.go wait.Until(nc.doNodeProcessingPassWorker, time.Second, stopCh)}// 4.doPodProcessingWorker,这个是处理podUpdateQueue队列的podfor i := 0; i < podUpdateWorkerSize; i++ {go wait.Until(nc.doPodProcessingWorker, time.Second, stopCh)}// 5. 如果开启了feature-gates=TaintBasedEvictions=true,执行doNoExecuteTaintingPass函数。否则执行doEvictionPass函数if nc.useTaintBasedEvictions {// Handling taint based evictions. Because we don't want a dedicated logic in TaintManager for NC-originated// taints and we normally don't rate limit evictions caused by taints, we need to rate limit adding taints.go wait.Until(nc.doNoExecuteTaintingPass, scheduler.NodeEvictionPeriod, stopCh)} else {// Managing eviction of nodes:// When we delete pods off a node, if the node was not empty at the time we then// queue an eviction watcher. If we hit an error, retry deletion.go wait.Until(nc.doEvictionPass, scheduler.NodeEvictionPeriod, stopCh)}// 6.一直监听node状态是否健康// Incorporate the results of node health signal pushed from kubelet to master.go wait.Until(func() {if err := nc.monitorNodeHealth(); err != nil {klog.Errorf("Error monitoring node health: %v", err)}}, nc.nodeMonitorPeriod, stopCh)<-stopCh
}

MonitorNodeHealth

在这里插入图片描述

此部分有如下几个作用

  1. 读取 Node 的 Label,用于确定 Node 属于哪个 zone;若该 zone 是新增的,就注册到 zonePodEvictor 或 zoneNoExecuteTainter (TaintManager 模式)

    • zonePodEvictor 后续用于该 zone 中失联的 Node,用于 Node 级别驱逐(就是驱逐 Node 上所有 Pod,并设置为 evicted 状态,此部分参见)

    • // pkg/controller/nodelifecycle/node_lifecycle_controller.go
      // addPodEvictorForNewZone checks if new zone appeared, and if so add new evictor.
      // dfy: 若出现新的 zone ,初始化 zonePodEvictor 或 zoneNoExecuteTainter
      func (nc *Controller) addPodEvictorForNewZone(node *v1.Node) {nc.evictorLock.Lock()defer nc.evictorLock.Unlock()zone := utilnode.GetZoneKey(node)// dfy: 若出现新的 zone ,初始化 zonePodEvictor 或 zoneNoExecuteTainterif _, found := nc.zoneStates[zone]; !found {// dfy: 没有找到 zone value,设置为 Initialnc.zoneStates[zone] = stateInitial// dfy: 没有 TaintManager,创建一个 限速队列,不太清楚有什么作用???if !nc.runTaintManager {// dfy: zonePodEvictor 负责将 pod 从无响应的节点驱逐出去nc.zonePodEvictor[zone] =scheduler.NewRateLimitedTimedQueue(flowcontrol.NewTokenBucketRateLimiter(nc.evictionLimiterQPS, scheduler.EvictionRateLimiterBurst))} else {// dfy: zoneNoExecuteTainter 负责为 node 打上污点 taintnc.zoneNoExecuteTainter[zone] =scheduler.NewRateLimitedTimedQueue(flowcontrol.NewTokenBucketRateLimiter(nc.evictionLimiterQPS, scheduler.EvictionRateLimiterBurst))}// Init the metric for the new zone.klog.Infof("Initializing eviction metric for zone: %v", zone)evictionsNumber.WithLabelValues(zone).Add(0)}
      }func (nc *Controller) doEvictionPass() {nc.evictorLock.Lock()defer nc.evictorLock.Unlock()for k := range nc.zonePodEvictor {// Function should return 'false' and a time after which it should be retried, or 'true' if it shouldn't (it succeeded).nc.zonePodEvictor[k].Try(func(value scheduler.TimedValue) (bool, time.Duration) {// dfy: 此处 value.Value 存储的是 Cluster Namenode, err := nc.nodeLister.Get(value.Value)if apierrors.IsNotFound(err) {klog.Warningf("Node %v no longer present in nodeLister!", value.Value)} else if err != nil {klog.Warningf("Failed to get Node %v from the nodeLister: %v", value.Value, err)}nodeUID, _ := value.UID.(string)// dfy: 获得分配到该节点上的 Podpods, err := nc.getPodsAssignedToNode(value.Value)if err != nil {utilruntime.HandleError(fmt.Errorf("unable to list pods from node %q: %v", value.Value, err))return false, 0}// dfy: 删除 Podremaining, err := nodeutil.DeletePods(nc.kubeClient, pods, nc.recorder, value.Value, nodeUID, nc.daemonSetStore)if err != nil {// We are not setting eviction status here.// New pods will be handled by zonePodEvictor retry// instead of immediate pod eviction.utilruntime.HandleError(fmt.Errorf("unable to evict node %q: %v", value.Value, err))return false, 0}// dfy: 在nodeEvictionMap设置node的状态为evictedif !nc.nodeEvictionMap.setStatus(value.Value, evicted) {klog.V(2).Infof("node %v was unregistered in the meantime - skipping setting status", value.Value)}if remaining {klog.Infof("Pods awaiting deletion due to Controller eviction")}if node != nil {zone := utilnode.GetZoneKey(node)evictionsNumber.WithLabelValues(zone).Inc()}return true, 0})}
      }
      
  2. 监听 Node 健康状态(通过监听 Node Lease 进行判别)

    • 若 Lease 不更新,且超过了容忍时间 gracePeriod,认为该 Node 失联(更新 Status Ready Condition 为 Unknown)

    • // tryUpdateNodeHealth checks a given node's conditions and tries to update it. Returns grace period to
      // which given node is entitled, state of current and last observed Ready Condition, and an error if it occurred.
      func (nc *Controller) tryUpdateNodeHealth(node *v1.Node) (time.Duration, v1.NodeCondition, *v1.NodeCondition, error) {// 省略一大部分 probeTimestamp 更新逻辑// dfy: 通过 lease 更新,来更新 probeTimestampobservedLease, _ := nc.leaseLister.Leases(v1.NamespaceNodeLease).Get(node.Name)if observedLease != nil && (savedLease == nil || savedLease.Spec.RenewTime.Before(observedLease.Spec.RenewTime)) {nodeHealth.lease = observedLeasenodeHealth.probeTimestamp = nc.now()}// dfy: 注意此处, Lease 没更新,导致 probeTimestamp 没变动,因此 现在时间超过了容忍时间,将此 Node 视作失联 Nodeif nc.now().After(nodeHealth.probeTimestamp.Add(gracePeriod)) {// NodeReady condition or lease was last set longer ago than gracePeriod, so// update it to Unknown (regardless of its current value) in the master.nodeConditionTypes := []v1.NodeConditionType{v1.NodeReady,v1.NodeMemoryPressure,v1.NodeDiskPressure,v1.NodePIDPressure,// We don't change 'NodeNetworkUnavailable' condition, as it's managed on a control plane level.// v1.NodeNetworkUnavailable,}nowTimestamp := nc.now()// dfy: 寻找 node 是否有上面几个异常状态for _, nodeConditionType := range nodeConditionTypes {// dfy: 具有异常状态,就进行记录_, currentCondition := nodeutil.GetNodeCondition(&node.Status, nodeConditionType)if currentCondition == nil {klog.V(2).Infof("Condition %v of node %v was never updated by kubelet", nodeConditionType, node.Name)node.Status.Conditions = append(node.Status.Conditions, v1.NodeCondition{Type:               nodeConditionType,Status:             v1.ConditionUnknown,Reason:             "NodeStatusNeverUpdated",Message:            "Kubelet never posted node status.",LastHeartbeatTime:  node.CreationTimestamp,LastTransitionTime: nowTimestamp,})} else {klog.V(2).Infof("node %v hasn't been updated for %+v. Last %v is: %+v",node.Name, nc.now().Time.Sub(nodeHealth.probeTimestamp.Time), nodeConditionType, currentCondition)if currentCondition.Status != v1.ConditionUnknown {currentCondition.Status = v1.ConditionUnknowncurrentCondition.Reason = "NodeStatusUnknown"currentCondition.Message = "Kubelet stopped posting node status."currentCondition.LastTransitionTime = nowTimestamp}}}// We need to update currentReadyCondition due to its value potentially changed._, currentReadyCondition = nodeutil.GetNodeCondition(&node.Status, v1.NodeReady)if !apiequality.Semantic.DeepEqual(currentReadyCondition, &observedReadyCondition) {if _, err := nc.kubeClient.CoreV1().Nodes().UpdateStatus(context.TODO(), node, metav1.UpdateOptions{}); err != nil {klog.Errorf("Error updating node %s: %v", node.Name, err)return gracePeriod, observedReadyCondition, currentReadyCondition, err}nodeHealth = &nodeHealthData{status:                   &node.Status,probeTimestamp:           nodeHealth.probeTimestamp,readyTransitionTimestamp: nc.now(),lease:                    observedLease,}return gracePeriod, observedReadyCondition, currentReadyCondition, nil}}return gracePeriod, observedReadyCondition, currentReadyCondition, nil
      }
      
  3. 根据 zone 设置驱逐速率

    • 每个 zone 有不同数量的 Node,根据该 zone 中 Node 失联数量的占比,设置不同的驱逐速率

    • // dfy: 1. 计算 zone 不健康程度; 2. 根据 zone 不健康程度设置不同的驱逐速率
      func (nc *Controller) handleDisruption(zoneToNodeConditions map[string][]*v1.NodeCondition, nodes []*v1.Node) {newZoneStates := map[string]ZoneState{}allAreFullyDisrupted := truefor k, v := range zoneToNodeConditions {zoneSize.WithLabelValues(k).Set(float64(len(v)))// dfy: 计算该 zone 的不健康程度(就是失联 node 的占比)// nc.computeZoneStateFunc = nc.ComputeZoneStateunhealthy, newState := nc.computeZoneStateFunc(v)zoneHealth.WithLabelValues(k).Set(float64(100*(len(v)-unhealthy)) / float64(len(v)))unhealthyNodes.WithLabelValues(k).Set(float64(unhealthy))if newState != stateFullDisruption {allAreFullyDisrupted = false}newZoneStates[k] = newStateif _, had := nc.zoneStates[k]; !had {klog.Errorf("Setting initial state for unseen zone: %v", k)nc.zoneStates[k] = stateInitial}}allWasFullyDisrupted := truefor k, v := range nc.zoneStates {if _, have := zoneToNodeConditions[k]; !have {zoneSize.WithLabelValues(k).Set(0)zoneHealth.WithLabelValues(k).Set(100)unhealthyNodes.WithLabelValues(k).Set(0)delete(nc.zoneStates, k)continue}if v != stateFullDisruption {allWasFullyDisrupted = falsebreak}}// At least one node was responding in previous pass or in the current pass. Semantics is as follows:// - if the new state is "partialDisruption" we call a user defined function that returns a new limiter to use,// - if the new state is "normal" we resume normal operation (go back to default limiter settings),// - if new state is "fullDisruption" we restore normal eviction rate,//   - unless all zones in the cluster are in "fullDisruption" - in that case we stop all evictions.if !allAreFullyDisrupted || !allWasFullyDisrupted {// We're switching to full disruption modeif allAreFullyDisrupted {klog.V(0).Info("Controller detected that all Nodes are not-Ready. Entering master disruption mode.")for i := range nodes {if nc.runTaintManager {_, err := nc.markNodeAsReachable(nodes[i])if err != nil {klog.Errorf("Failed to remove taints from Node %v", nodes[i].Name)}} else {nc.cancelPodEviction(nodes[i])}}// We stop all evictions.for k := range nc.zoneStates {if nc.runTaintManager {nc.zoneNoExecuteTainter[k].SwapLimiter(0)} else {nc.zonePodEvictor[k].SwapLimiter(0)}}for k := range nc.zoneStates {nc.zoneStates[k] = stateFullDisruption}// All rate limiters are updated, so we can return early here.return}// We're exiting full disruption modeif allWasFullyDisrupted {klog.V(0).Info("Controller detected that some Nodes are Ready. Exiting master disruption mode.")// When exiting disruption mode update probe timestamps on all Nodes.now := nc.now()for i := range nodes {v := nc.nodeHealthMap.getDeepCopy(nodes[i].Name)v.probeTimestamp = nowv.readyTransitionTimestamp = nownc.nodeHealthMap.set(nodes[i].Name, v)}// We reset all rate limiters to settings appropriate for the given state.for k := range nc.zoneStates {// dfy: 设置 zone 的驱逐速率nc.setLimiterInZone(k, len(zoneToNodeConditions[k]), newZoneStates[k])nc.zoneStates[k] = newZoneStates[k]}return}// We know that there's at least one not-fully disrupted so,// we can use default behavior for rate limitersfor k, v := range nc.zoneStates {newState := newZoneStates[k]if v == newState {continue}klog.V(0).Infof("Controller detected that zone %v is now in state %v.", k, newState// dfy: 设置 zone 的驱逐速率nc.setLimiterInZone(k, len(zoneToNodeConditions[k]), newState)nc.zoneStates[k] = newState}}
      }// ComputeZoneState returns a slice of NodeReadyConditions for all Nodes in a given zone.
      // The zone is considered:
      // - fullyDisrupted if there're no Ready Nodes,
      // - partiallyDisrupted if at least than nc.unhealthyZoneThreshold percent of Nodes are not Ready,
      // - normal otherwise
      func (nc *Controller) ComputeZoneState(nodeReadyConditions []*v1.NodeCondition) (int, ZoneState) {readyNodes := 0notReadyNodes := 0for i := range nodeReadyConditions {if nodeReadyConditions[i] != nil && nodeReadyConditions[i].Status == v1.ConditionTrue {readyNodes++} else {notReadyNodes++}}switch {case readyNodes == 0 && notReadyNodes > 0:return notReadyNodes, stateFullDisruptioncase notReadyNodes > 2 && float32(notReadyNodes)/float32(notReadyNodes+readyNodes) >= nc.unhealthyZoneThreshold:return notReadyNodes, statePartialDisruptiondefault:return notReadyNodes, stateNormal}
      }// dfy: 根据该 zone 健康状态(也就是健康比例),设置驱逐效率(频率)
      func (nc *Controller) setLimiterInZone(zone string, zoneSize int, state ZoneState) {switch state {case stateNormal:if nc.runTaintManager {nc.zoneNoExecuteTainter[zone].SwapLimiter(nc.evictionLimiterQPS)} else {nc.zonePodEvictor[zone].SwapLimiter(nc.evictionLimiterQPS)}case statePartialDisruption:if nc.runTaintManager {nc.zoneNoExecuteTainter[zone].SwapLimiter(nc.enterPartialDisruptionFunc(zoneSize))} else {nc.zonePodEvictor[zone].SwapLimiter(nc.enterPartialDisruptionFunc(zoneSize))}case stateFullDisruption:if nc.runTaintManager {nc.zoneNoExecuteTainter[zone].SwapLimiter(nc.enterFullDisruptionFunc(zoneSize))} else {nc.zonePodEvictor[zone].SwapLimiter(nc.enterFullDisruptionFunc(zoneSize))}}
      }
      
  4. 进行 Pod 驱逐的处理 proceeNoTaintBaseEviction

TaintManger.Run

  • TainManager 的驱逐逻辑,看代码不难理解,大概说明

    1. 若开启 TaintManager 模式,所有 Pod、Node 的改变都会被放入,nc.tc.podUpdateQueue 和 nc.tc.nodeUpdateQueue 中

    2. 当 Node 失联时,会被打上 NoExecute Effect Taint(不在此处,在 main Controller.Run 函数中)

    3. 此处会先处理 nc.tc.nodeUpdateQueue 的驱逐

      • 首先会检查 Node 是否有 NoExecute Effect Taint;没有就取消驱逐

      • 有的话,进行 Pod 的逐个驱逐,检查 Pod 是否有该 Taint 的 toleration,有的话,就根据 toleration 设置 pod 的定时删除;没有 Toleration,就立即删除

    4. 接下来处理 nc.tc.podUpdateQueue 的驱逐

      • 进行 Pod 的逐个驱逐,检查 Pod 是否有该 Taint 的 toleration,有的话,就根据 toleration 设置 pod 的定时删除;没有 Toleration,就立即删除

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OUQO1kpN-1692352728105)(img/nodelifecycle笔记/image-20230818160542117.png)]

Node Pod 的处理

  • 此处就是 nc.podUpdateQueue 和 nc.NodeUpdateQueue 的一些驱逐逻辑
  • 比如给 Node 打上 NoSchedule Taint
  • 检测到 Node 不健康,给 Pod 打上 Ready Condition = False 的 Status Condition
  • 进行 Pod 驱逐的处理 proceeNoTaintBaseEviction

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ogwwC6Jx-1692352728105)(img/nodelifecycle笔记/image-20230818160649929.png)]

驱逐

  • 此处 TaintManager 模式,只是打上 NoExecute Effect Taint —— doNoExecuteTaintingPass 函数
  • 非 TaintManager 模式,会清理 zonePodEvicotr 记录的 Node 上的所有 Pod( Node 级别驱逐)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lHU7oJde-1692352728105)(img/nodelifecycle笔记/image-20230818160708127.png)]

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

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

相关文章

【Maven教程】(一)入门介绍篇:Maven基础概念与其他构建工具:理解构建过程与Maven的多重作用,以及与敏捷开发的关系 ~

Maven入门介绍篇 1️⃣ 基础概念1.1 构建1.2 maven对构建的支持1.3 Maven的其他作用 2️⃣ 其他构建工具2.1 IDE2.2 Make2.3 Ant2.4 Jenkins 3️⃣ Maven与敏捷开发&#x1f33e; 总结 1️⃣ 基础概念 "Maven"可以翻译为 “知识的积累者” 或 “专家”。这个词源于波…

Qt应用开发(基础篇)——MDI窗口 QMdiArea QMdiSubWindow

一、前言 QMdiArea类继承于QAbstractScrollArea&#xff0c;QAbstractScrollArea继承于QFrame&#xff0c;是Qt用来显示MDI窗口的部件。 滚屏区域基类 QAbstractScrollAreahttps://blog.csdn.net/u014491932/article/details/132245486 框架类 QFramehttps://blog.csdn.net/u01…

FANUC机器人加减速倍率指令ACC的使用方法说明

FANUC机器人加减速倍率指令ACC的使用方法说明 单位有一台FANUC机器人(型号:M-900iB 360kg),偶尔会在启动的瞬间会报SRVO-050碰撞检测报警,而事实上机器人并没有开始移动或和其他工件产生碰撞,一直查了很长时间,也没有查到具体的原因,也尝试过重新进行负载推算,但是偶尔…

恒运资本:CPO概念发力走高,兆龙互联涨超10%,华是科技再创新高

CPO概念15日盘中发力走高&#xff0c;截至发稿&#xff0c;华是科技涨超15%再创新高&#xff0c;兆龙互联涨逾11%&#xff0c;中贝通讯涨停&#xff0c;永鼎股份、太辰光涨超5%&#xff0c;天孚通讯涨逾4%。 消息面上&#xff0c;光通讯闻名咨询机构LightCounting近日发布的202…

国产之光:讯飞星火最新大模型V2.0

大家好&#xff0c;我是herosunly。985院校硕士毕业&#xff0c;现担任算法研究员一职&#xff0c;热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名&#xff0c;CCF比赛第二名&#xff0c;科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的…

每天一道leetcode:1466. 重新规划路线(图论中等广度优先遍历)

今日份题目&#xff1a; n 座城市&#xff0c;从 0 到 n-1 编号&#xff0c;其间共有 n-1 条路线。因此&#xff0c;要想在两座不同城市之间旅行只有唯一一条路线可供选择&#xff08;路线网形成一颗树&#xff09;。去年&#xff0c;交通运输部决定重新规划路线&#xff0c;以…

OpenCV-Python中的图像处理-视频分析

OpenCV-Python中的图像处理-视频分析 视频分析Meanshift算法Camshift算法光流Lucas-Kanade Optical FlowDense Optical Flow 视频分析 学习使用 Meanshift 和 Camshift 算法在视频中找到并跟踪目标对象: Meanshift算法 Meanshift 算法的基本原理是和很简单的。假设我们有一堆…

python优雅地爬虫!

背景 我需要获得新闻&#xff0c;然后tts&#xff0c;在每天上班的路上可以听一下。具体的方案后期我也会做一次分享。先看我喜欢的万能的老路&#xff1a;获得html内容-> python的工具库解析&#xff0c;获得元素中的内容&#xff0c;完成。 好家伙&#xff0c;我知道我爬…

视频云存储/安防监控/视频汇聚EasyCVR平台新增设备经纬度选取

视频云存储/安防监控EasyCVR视频汇聚平台基于云边端智能协同&#xff0c;支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发、视频集中存储等。音视频流媒体视频平台EasyCVR拓展性强&#xff0c;视频能力丰富&#xff0c;具体可实现视频监控直播、视频轮播、视频录像、…

公网远程连接Redis数据库「内网穿透」

文章目录 1. Linux(centos8)安装redis数据库2. 配置redis数据库3. 内网穿透3.1 安装cpolar内网穿透3.2 创建隧道映射本地端口 4. 配置固定TCP端口地址4.1 保留一个固定tcp地址4.2 配置固定TCP地址4.3 使用固定的tcp地址连接 前言 洁洁的个人主页 我就问你有没有发挥&#xff0…

蓝牙资讯|苹果Apple Watch可手势操控Mac和Apple TV等设备

根据美国商标和专利局&#xff08;USPTO&#xff09;公示的清单&#xff0c;苹果公司近日获得了一项技术专利&#xff0c;概述了未来的 Apple Watch 手表&#xff0c;使用手势等操控 Mac 和 Apple TV 等设备。 该专利描述未来 Apple Watch 可以交互实现编辑图像、绘图、处理文…

02:STM32--EXTI外部中断

目录 一:中断 1:简历 2:AFIO 3:EXTI ​编辑 4:NVIC基本结构 5:使用步骤 二:中断的应用 A:对外式红外传感计数器 1:连接图​编辑 2:函数介绍 3:硬件介绍 4:计数代码 B;旋转编码计数器 1:连接图 2:硬件介绍 3:旋转编码器代码: 一:中断 1:简历 中断&#xff1a;在主程…

Flutter 测试小结

Flutter 项目结构 pubspec.yaml 类似于 RN 的 package.json&#xff0c;该文件分别在最外层及 example 中有&#xff0c;更新该文件后&#xff0c;需要执行的 Pub get lib 目录下的 dart 文件为 Flutter 插件封装后的接口源码&#xff0c;方便在其他 dart 文件中调用 example 目…

python通过S7协议读取西门子200smart数据

发现网上很多关于python通过s7协议控制200smart的代码都失败&#xff0c;我猜应该是版本的问题。自己捣鼓了半天&#xff0c;终于测试成功 from snap7 import util,clientmy_plc client.Client() #建立一个客户端对象 my_plc.set_connection_type(3) #如果是200smart,必须有此…

QT实现天气预报

1. MainWindow类设计的成员变量和方法 public: MainWindow(QWidget* parent nullptr); ~MainWindow(); protected: 形成文本菜单来用来右键关闭窗口 void contextMenuEvent(QContextMenuEvent* event); 鼠标被点击之后此事件被调用 void mousePressEvent(QMouseEv…

Leetcode每日一题:1444. 切披萨的方案数(2023.8.17 C++)

目录 1444. 切披萨的方案数 题目描述&#xff1a; 实现代码与解析&#xff1a; 二维后缀和 动态规划 原理思路&#xff1a; 1444. 切披萨的方案数 题目描述&#xff1a; 给你一个 rows x cols 大小的矩形披萨和一个整数 k &#xff0c;矩形包含两种字符&#xff1a; A …

Spring(三):Spring中Bean的生命周期和作用域

前言 在 Spring 中&#xff0c;那些组成应用程序的主体及由 Spring IOC 容器所管理的对象&#xff0c;被称之为 bean。简单地讲&#xff0c;bean 就是由 IOC 容器初始化、装配及管理的对象&#xff0c;除此之外&#xff0c;bean 就与应用程序中的其他对象没有什么区别了。而 b…

LeetCode 热题 100(四):48. 旋转图像、240. 搜索二维矩阵 II、234. 回文链表

一.48. 旋转图像 题目要求&#xff1a;就是一个顺时针的旋转过程。 思路&#xff1a;观察矩阵&#xff0c;得出翻转前第i行的第J个元素 等于 翻转后倒数第i列的第J个元素&#xff0c;举例说明&#xff0c;第1行第2个元素为“2”&#xff0c;翻转后到了 倒数第1列的第2个元素…

MAC环境,在IDEA执行报错java: -source 1.5 中不支持 diamond 运算符

Error:(41, 51) java: -source 1.5 中不支持 diamond 运算符 (请使用 -source 7 或更高版本以启用 diamond 运算符) 进入设置 修改java版本 pom文件中加入 <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin&l…

vue项目预览pdf功能(解决动态文字无法显示的问题)

最近&#xff0c;因为公司项目需要预览pdf的功能&#xff0c;开始的时候找了市面上的一些pdf插件&#xff0c;都能用&#xff0c;但是&#xff0c;后面因为pdf变成了需要根据内容进行变化的&#xff0c;然后&#xff0c;就出现了需要动态生成的文字不显示了。换了好多好多的插件…