【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.pswp.cn/news/42935.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/42935.shtml
英文地址,請注明出處:http://en.pswp.cn/news/42935.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

esp32C3 micropython oled 恐龍快跑游戲

目錄 簡介 效果展示 源代碼 main.py ssd1306.py 實現思路 血量值 分數 恐龍 障礙物 得分與血量值的計算 簡介 使用合宙esp32c3模塊&#xff0c;基于micropython平臺開發的一款oled小游戲&#xff0c;恐龍快跑&#xff0c;所有代碼已經給出&#xff0c;將兩個py文件…

【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…

面試算法編程題

面試算法編程題記錄 題目 : 羊圈里的狼 題目背景 : 一到了晚上&#xff0c;草原牧民的羊就會被趕進羊圈里。這時&#xff0c;野外的狼群就會打羊羔的主意。為了保護羊羔&#xff0c;牧民需要將羊圈里的狼趕走或殺死。由于來的狼很多&#xff0c;他需要快速甄別哪些狼在羊圈里面…

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 算法的基本原理是和很簡單的。假設我們有一堆…

Failed to init API, possibly an invalid tessdata path: ./ ubuntu

1、問題描述 Failed to init API, possibly an invalid tessdata path: ./2、解決方案&#xff1a; 添加“TESSDATA_PREFIX”到系統環境變量中&#xff0c;值為testdata的父路徑&#xff08;一般就是 Tesseract-OCR 的安裝路徑&#xff09;亦可解決。在~/.bashrc中添加 expo…

【學習日記】【FreeRTOS】空閑任務與阻塞延時

寫在前面 本文是基于野火 RTOS 教程對空閑任務和阻塞延時的詳解。 一、什么是任務中的阻塞延時 說到阻塞延時&#xff0c;筆者的第一反應就是在單片機的 while 循環中&#xff0c;使用一個 for 循環不斷遞減一個大數&#xff0c;通過 CPU 不斷執行一條指令的耗時進行延時。這…

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,必須有此…

Flink流批一體計算(14):PyFlink Tabel API之SQL查詢

舉個例子 查詢 source 表&#xff0c;同時執行計算 # 通過 Table API 創建一張表&#xff1a; source_table table_env.from_path("datagen") # 或者通過 SQL 查詢語句創建一張表&#xff1a; source_table table_env.sql_query("SELECT * FROM datagen&quo…

QT實現天氣預報

1. MainWindow類設計的成員變量和方法 public: MainWindow(QWidget* parent nullptr); ~MainWindow(); protected: 形成文本菜單來用來右鍵關閉窗口 void contextMenuEvent(QContextMenuEvent* event); 鼠標被點擊之后此事件被調用 void mousePressEvent(QMouseEv…