Kubernetes GoRoutineMap工具包代码详解
1、概述
GoRoutineMap 定义了一种类型,可以运行具有名称的 goroutine 并跟踪它们的状态。它防止创建具有相同名称的多个goroutine,并且在上一个具有该名称的 goroutine 完成后的一段退避时间内可能阻止重新创建 goroutine。
使用GoRoutineMap场景:
- 使用协程的方式运行函数逻辑,如果函数成功执行,则退出该协程;如果函数执行报错,在指数退避的时间内禁止再次执行该函数逻辑。
使用GoRoutineMap大体步骤如下:
1)通过goRoutineMap.NewGoRoutineMap(exponentialBackOffOnError bool) GoRoutineMap {....}方法创建GoRoutineMap结构体对象,用于管理goroutine 并跟踪它们的状态;
2)调用GoRoutineMap结构体对象Run(operationName, operation)方法,其能够防止创建具有相同名称的多个goroutine,并使用协程的方式运行函数逻辑,如果函数成功执行,则退出该协程;如果函数执行报错,在指数退避的时间内禁止再次执行该函数逻辑。
注意 1:本文代码基于Kubernetes 1.24.10版本,包路径kubernetes-1.24.10/pkg/util/goroutinemap/goroutinemap.go。
注意 2:概述中涉及的代码会在下文进行详细解释。
2、goroutinemap工具包代码详解
2.1 相关类型详解
GoRoutineMap工具包接口定义:
type GoRoutineMap interface {
// Run adds operation name to the list of running operations and spawns a
// new go routine to execute the operation.
// If an operation with the same operation name already exists, an
// AlreadyExists or ExponentialBackoff error is returned.
// Once the operation is complete, the go routine is terminated and the
// operation name is removed from the list of executing operations allowing
// a new operation to be started with the same operation name without error.
Run(operationName string, operationFunc func() error) error
// Wait blocks until operations map is empty. This is typically
// necessary during tests - the test should wait until all operations finish
// and evaluate results after that.
Wait()
// WaitForCompletion blocks until either all operations have successfully completed
// or have failed but are not pending. The test should wait until operations are either
// complete or have failed.
WaitForCompletion()
IsOperationPending(operationName string) bool
}
goRoutineMap结构体实现GoRoutineMap接口,定义如下:
// goRoutineMap结构体实现GoRoutineMap接口,
type goRoutineMap struct {
// 用于记录goRoutineMap维护协程的状态
operations map[string]operation
// 发生错误时是否指数级补偿
exponentialBackOffOnError bool
// 用在多个 Goroutine 等待,一个 Goroutine 通知(事件发生)的场景
cond *sync.Cond
lock sync.RWMutex
} // operation结构体对象维护单个goroutine的状态。
type operation struct {
// 是否操作挂起
operationPending bool
// 单个goroutine执行逻辑报错时,实现以指数退避方式
expBackoff exponentialbackoff.ExponentialBackoff
}
ExponentialBackoff结构体包含最后一次出现的错误、最后一次出现错误的时间以及不允许重试的持续时间。
// ExponentialBackoff contains the last occurrence of an error and the duration
// that retries are not permitted.
type ExponentialBackoff struct {
lastError error
lastErrorTime time.Time
durationBeforeRetry time.Duration
}
2.2 GoRoutineMap结构体对象初始化
通过goRoutineMap.NewGoRoutineMap方法创建GoRoutineMap结构体对象,用于管理goroutine 并跟踪它们的状态;
// NewGoRoutineMap returns a new instance of GoRoutineMap.
func NewGoRoutineMap(exponentialBackOffOnError bool) GoRoutineMap {
g := &goRoutineMap{
operations: make(map[string]operation),
exponentialBackOffOnError: exponentialBackOffOnError,
} g.cond = sync.NewCond(&g.lock)
return g
}
2.3 GoRoutineMap.Run方法代码详解
调用GoRoutineMap结构体对象Run(operationName, operation)方法,其能够防止创建具有相同名称的多个goroutine,并使用协程的方式运行函数逻辑,如果函数成功执行,则退出该协程;如果函数执行报错,在指数退避的时间内禁止再次执行该函数逻辑。
// Run函数是外部函数,是goRoutineMap核心方法,其能够防止创建具有相同名称的多个goroutine,并使用协程的方式运行函数逻辑
// 如果函数成功执行,则退出该协程;如果函数执行报错,在指数退避的时间内禁止再次执行该函数逻辑。
func (grm *goRoutineMap) Run(
operationName string,
operationFunc func() error) error {
grm.lock.Lock()
defer grm.lock.Unlock() // 判断grm.operations这个map中是否存在具有operationName名称的协程
existingOp, exists := grm.operations[operationName]
if exists {
// 如果grm.operations这个map中已经存在operationName名称的协程,并且existingOp.operationPending==true,说明grm.operations中operationName名称这个协程正在执行函数逻辑,在这期间又有一个同名的
// operationName希望加入grm.operations这个map,此时加入map失败并报AlreadyExistsError错误
if existingOp.operationPending {
return NewAlreadyExistsError(operationName)
} // 到这步说明名称为operationName名称的协程执行函数逻辑失败,此时判断此协程最后一次失败时间 + 指数退避的时间 >= 当前时间,如果不符合条件的话禁止执行该协程函数逻辑。
if err := existingOp.expBackoff.SafeToRetry(operationName); err != nil {
return err
}
} // 如果grm.operations这个map中不存在operationName名称的协程 或者 此协程最后一次失败时间 + 指数退避的时间 < 当前时间,则在grm.operations这个map中重新维护此协程(注意,operationPending=true)
grm.operations[operationName] = operation{
operationPending: true,
expBackoff: existingOp.expBackoff,
} // 以协程方式执行函数逻辑operationFunc()
go func() (err error) {
// 捕获崩溃并记录错误,默认不传参的话,在程序发送崩溃时,在控制台打印一下崩溃日志后再崩溃,方便技术人员排查程序错误。
defer k8sRuntime.HandleCrash() // 如果执行operationFunc()函数逻辑不报错或者grm.exponentialBackOffOnError=false的话,将从grm.operations这个map中移除此operationName名称协程;
// 如果执行operationFunc()函数逻辑报错并且grm.exponentialBackOffOnError=true,则将产生指数级补偿,到达补偿时间后才能再调用此operationName名称协程的函数逻辑
// Handle completion of and error, if any, from operationFunc()
defer grm.operationComplete(operationName, &err)
// 处理operationFunc()函数发生的panic错误,以便defer grm.operationComplete(operationName, &err)能执行
// Handle panic, if any, from operationFunc()
defer k8sRuntime.RecoverFromPanic(&err)
return operationFunc()
}() return nil
}
如果给定lastErrorTime的durationBeforeRetry周期尚未过期,则SafeToRetry返回错误。否则返回零。
// SafeToRetry returns an error if the durationBeforeRetry period for the given
// lastErrorTime has not yet expired. Otherwise it returns nil.
func (expBackoff *ExponentialBackoff) SafeToRetry(operationName string) error {
if time.Since(expBackoff.lastErrorTime) <= expBackoff.durationBeforeRetry {
return NewExponentialBackoffError(operationName, *expBackoff)
} return nil
}
operationComplete是一个内部函数,用于处理在goRoutineMap中已经运行完函数逻辑的协程。
// operationComplete是一个内部函数,用于处理在goRoutineMap中已经运行完函数逻辑的协程
// 如果执行operationFunc()函数逻辑不报错或者grm.exponentialBackOffOnError=false的话,将从grm.operations这个map中移除此operationName名称协程;
// 如果执行operationFunc()函数逻辑报错并且grm.exponentialBackOffOnError=true,则将产生指数级补偿,到达补偿时间后才能再调用此operationName名称协程的函数逻辑
// operationComplete handles the completion of a goroutine run in the
// goRoutineMap.
func (grm *goRoutineMap) operationComplete(
operationName string, err *error) {
// Defer operations are executed in Last-In is First-Out order. In this case
// the lock is acquired first when operationCompletes begins, and is
// released when the method finishes, after the lock is released cond is
// signaled to wake waiting goroutine.
defer grm.cond.Signal()
grm.lock.Lock()
defer grm.lock.Unlock() if *err == nil || !grm.exponentialBackOffOnError {
// 函数逻辑执行完成无错误或已禁用错误指数级补偿,将从grm.operations这个map中移除此operationName名称协程;
// Operation completed without error, or exponentialBackOffOnError disabled
delete(grm.operations, operationName)
if *err != nil {
// Log error
klog.Errorf("operation for %q failed with: %v",
operationName,
*err)
}
} else {
// 函数逻辑执行完成有错误则将产生指数级补偿,到达补偿时间后才能再调用此operationName名称协程的函数逻辑(注意,指数补充的协程,operationPending=false)
// Operation completed with error and exponentialBackOffOnError Enabled
existingOp := grm.operations[operationName]
existingOp.expBackoff.Update(err)
existingOp.operationPending = false
grm.operations[operationName] = existingOp // Log error
klog.Errorf("%v",
existingOp.expBackoff.GenerateNoRetriesPermittedMsg(operationName))
}
}
Update是一个外部函数,用于计算指数级别的退避时间。
func (expBackoff *ExponentialBackoff) Update(err *error) {
if expBackoff.durationBeforeRetry == 0 {
expBackoff.durationBeforeRetry = initialDurationBeforeRetry
} else {
expBackoff.durationBeforeRetry = 2 * expBackoff.durationBeforeRetry
if expBackoff.durationBeforeRetry > maxDurationBeforeRetry {
expBackoff.durationBeforeRetry = maxDurationBeforeRetry
}
}
expBackoff.lastError = *err
expBackoff.lastErrorTime = time.Now()
}
3、总结
本文对Kubernetes GoRoutineMap工具包代码进行了详解,通过 GoRoutineMap工具包能够防止创建具有相同名称的多个goroutine,并使用协程的方式运行函数逻辑,如果函数成功执行,则退出该协程;如果函数执行报错,在指数退避的时间内禁止再次执行该函数逻辑。使用Kubernetes GoRoutineMap包的好处包括以下几点:
减轻负载:当出现错误时,使用指数退避时间可以避免过于频繁地重新尝试操作,从而减轻系统的负载。指数退避时间通过逐渐增加重试之间的等待时间,有效地减少了对系统资源的过度使用。
提高稳定性:通过逐渐增加重试之间的等待时间,指数退避时间可以帮助应对瞬时的故障或错误。这种策略使得系统能够在短时间内自动恢复,并逐渐增加重试频率,直到操作成功为止。这有助于提高应用程序的稳定性和可靠性。
降低网络拥塞:当网络出现拥塞时,频繁地进行重试可能会加重拥塞问题并影响其他任务的正常运行。指数退避时间通过增加重试之间的等待时间,可以降低对网络的额外负载,有助于缓解网络拥塞问题。
避免过早放弃:某些错误可能是瞬时的或暂时性的,因此过早放弃重试可能会导致不必要的失败。指数退避时间确保了在错误发生时进行适当的重试,以便系统有更多机会恢复并成功完成操作。
综上所述,使用Kubernetes GoRoutineMap工具包以协程方式处理函数逻辑可以提高系统的可靠性、稳定性和性能,减轻负载并有效应对错误和故障情况。这是在Kubernetes中实施的一种常见的重试策略,常用于处理容器化应用程序中的操作错误。
Kubernetes GoRoutineMap工具包代码详解的更多相关文章
- BM算法 Boyer-Moore高质量实现代码详解与算法详解
Boyer-Moore高质量实现代码详解与算法详解 鉴于我见到对算法本身分析非常透彻的文章以及实现的非常精巧的文章,所以就转载了,本文的贡献在于将两者结合起来,方便大家了解代码实现! 算法详解转自:h ...
- ASP.NET MVC 5 学习教程:生成的代码详解
原文 ASP.NET MVC 5 学习教程:生成的代码详解 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 创建连接字符串 ...
- Github-karpathy/char-rnn代码详解
Github-karpathy/char-rnn代码详解 zoerywzhou@gmail.com http://www.cnblogs.com/swje/ 作者:Zhouwan 2016-1-10 ...
- 代码详解:TensorFlow Core带你探索深度神经网络“黑匣子”
来源商业新知网,原标题:代码详解:TensorFlow Core带你探索深度神经网络“黑匣子” 想学TensorFlow?先从低阶API开始吧~某种程度而言,它能够帮助我们更好地理解Tensorflo ...
- Docker Kubernetes 服务发现原理详解
Docker Kubernetes 服务发现原理详解 服务发现支持Service环境变量和DNS两种模式: 一.环境变量 (默认) 当一个Pod运行到Node,kubelet会为每个容器添加一组环境 ...
- JAVA类与类之间的全部关系简述+代码详解
本文转自: https://blog.csdn.net/wq6ylg08/article/details/81092056类和类之间关系包括了 is a,has a, use a三种关系(1)is a ...
- Java中String的intern方法,javap&cfr.jar反编译,javap反编译后二进制指令代码详解,Java8常量池的位置
一个例子 public class TestString{ public static void main(String[] args){ String a = "a"; Stri ...
- Kaggle网站流量预测任务第一名解决方案:从模型到代码详解时序预测
Kaggle网站流量预测任务第一名解决方案:从模型到代码详解时序预测 2017年12月13日 17:39:11 机器之心V 阅读数:5931 近日,Artur Suilin 等人发布了 Kaggl ...
- 基础 | batchnorm原理及代码详解
https://blog.csdn.net/qq_25737169/article/details/79048516 https://www.cnblogs.com/bonelee/p/8528722 ...
- 非极大值抑制(NMS,Non-Maximum Suppression)的原理与代码详解
1.NMS的原理 NMS(Non-Maximum Suppression)算法本质是搜索局部极大值,抑制非极大值元素.NMS就是需要根据score矩阵和region的坐标信息,从中找到置信度比较高的b ...
随机推荐
- 一次.net code中的placeholder导致的高cpu诊断
背景 最近一位朋友找到我,让我帮看他们的一个aspnet core service无端cpu高的问题.从描述上看,这个service之前没有出现过cpu高的情况,最近也没有改过实际的什么code.很奇 ...
- 搭建良好编写体验的webgl编程环境 vscode+vit
因为webgl代码是以字符串的形式嵌入在javascript代码中,这对于我们编写webgl代码的体验不友好,本文介绍如何搭建友好webgl编程环境: 需要安装的vscode插件 WebGL GLSL ...
- Kubeadm安装k8s集群升级100年证书时报错:Unable to connect to the server: EOF:求解决方法.
报错信息: 使用命令时: Kubelet服务报错: 报错情况,在更新完k8s100年证书的时候,到最后重新启动kubelet服务的时候,服务是可以重新启动的,但是kubectl的命令是无法使用的,会等 ...
- 垃圾回收之G1收集过程
G1 中提供了 Young GC.Mixed GC 两种垃圾回收模式,这两种垃圾回收模式,都是 Stop The World(STW) 的. G1 没有 fullGC 概念,需要 fullGC 时,调 ...
- 建木在 Rainbond 上使用实践
建木 是一个面向 DevOps 领域的极易扩展的开源无代码(图形化)/低代码(GitOps)工具,可以帮助用户轻松编排各种DevOps流程并分发到不同平台执行. 建木的图形化编排提供了多个节点,节点可 ...
- pysimplegui之第一个程序,包括回调函数,事件,阻塞等待内容
自定义窗口 API 调用(您的第一个窗口) 总结一下:我遇到的坑, 比如拿输入框的内容的时候可以直接通过value[key] 几种窗口模式就是什么时候用timeout这个参数 关闭窗口可以的一边形式 ...
- [SpringBoot/JavaEE]SpringBoot启动与停用的4种方式
SpringBoot版本: 2.1.6.RELEASE 1 启动 方式1 – IntelliJ IDEA - Windows 右键启动类SpringBootSampleApplication.java ...
- Python常见面试题017: Python中是否可以获取类的所有实例
017. Python中是否可以获取类的所有实例 转载请注明出处,https://www.cnblogs.com/wuxianfeng023 出处 https://docs.python.org/zh ...
- 浅谈对属性描述符__get__、__set__、__delete__的理解
1.属性描述符的基础介绍 1.1 何为属性描述符? 属性描述符是一种Python语言中的特殊对象,用于定义和控制类属性的行为.属性描述符可以通过定义__get__.__set__.__delete__ ...
- .Net Core工作流WorkFlowCore
前言 WorkFlowCore是一个针对.NetCore的轻量级的工作流引擎,提供了FluentAPI.多任务.持久化以及并行处理的功能,适合于小型工作流.责任链的需求开发.支持工作流长期运行,提供了 ...