Golang 和 Kubernetes finalizer使用方式
Golang 垃圾回收 finalizer
Golang finalizer
直白点理解,是Go语言中的析构函数
. 可通过runtime.SetFinalizer
将对象object
和 finalizer函数
进行绑定。
举个例子:
func main() {
var i = 3
// 这func 就是 finalizer 析构函数
runtime.SetFinalizer(&i, func(i *int) {
fmt.Println(i, *i, "set finalizer")
})
var k = 5
i = k
time.Sleep(time.Second * 5)
// 解绑 finalizer 析构函数
runtime.SetFinalizer(&i, nil)
}
从golang runtime 中可以得出: runtime SetFinalizer 说明
runtime.SetFinalizer
是在一个
runtime协程中处理的。如果全局设置的的finalizer
过多 或者 过于复杂,会影响 GC 性能;runtime.SetFinalizer
是在对象被 GC 选中并被回收之前,SetFinalizer 都不会执行, 所以禁止
在SetFinalizer中执行将内存中的内容flush到磁盘这种操作。runtime.SetFinalizer
最大的问题是延长了对象生命周期. 对于大量对象分配、删除等场景不建议设置;- 指针构成的 “循环引⽤” 加上
runtime.SetFinalizer
会导致内存泄露;
使用场景
1. CGO场景下,对非golang开辟对象,进行析构传递
package main
// #include <stdio.h>
// typedef struct {
// char *msg;
// } myStruct;
// void myFunc(myStruct *strct) {
// printf("Hello %s!\n", strct->msg);
// }
import "C"
func main() {
msg := C.myStruct{C.CString("world")}
runtime.SetFinalizer(&msg, func(t *C.myStruct) {
C.free(unsafe.Pointer(t.msg))
})
C.myFunc(msg.msg)
runtime.KeepAlive(&msg)
}
由于垃圾回收时调用finalizer很有可能是在另一个线程中执行的, 但有些资源可能不是线程安全的, 需要确保绑定到同一个协程定时释放
2. 优雅关闭后台goroutine协程
通常显示调用方式:
func watch() {
ticker := time.NewTicker(time.Second)
defer ticker.Stop()
for {
select {
case <-stop:
// do something
return
case <-ticker.C:
// do something
}
}
}
func Close(i *interface) {
if i != nil {
close(i.object) //底层对象清理
}
// 设置对象为 nil
i = nil
}
这种方式需要循环,并显示调用close方法;
可以通过 runtime.SetFinalizer
实现优雅关闭;
type Wrapper struct {
obj *interface
stop chan struct{}
}
var i = 3
result := &Wrapper{&i}
runtime.SetFinalizer(result, func(w *Wrapper) {
w.stop <- true
w.evicterTerminated.Wait()
})
在基础库中SetFinalzer
主要的使用场景是减少用户错误使用导致的资源泄露,比如 os.NewFile()
和 net.FD()
都注册了 finalizer 来避免用户由于忘记调用 Close
导致的 fd leak
.
Kubernetes finalizer机制, 理念和 golang finalizer一致
Kubernetes Finalizer
在Kubernetes资源删除过程中,异步删除/状态同步
的一种控制机制
。
Finalizer
主要是实现多个资源对象之间可能会有依赖关系,随意删除一个对象可能会对依赖它的其他对象产生影响。 在删除其中一个之前,有一个钩子函数
可执行相关资源删除关系;
使用场景
Kubernetes
的一些原生资源对象会自动被加上一些finalizers
.
原生场景: PVC和PV
PVC
和PV
分别原生自带 kubernetes.io/pvc-protection
和kubernetes.io/pv-protection
的finalizers
标签的。
顾名思义,其目的在于保护持久化存储不被误删,避免挂载了存储的工作负载产生问题
。
原生场景: namespace
Namespace
也是自带一个kubernetes
的finalizers标签的.
自定义Controller
kubebuilder Finalizers
允许控制器实现异步预删除钩子.
为 API 类型的每个对象创建一个外部资源(例如存储桶),并且您希望在从 Kubernetes 删除对象时删除关联的外部资源,您可以使用终结器来执行此操作。
func (r *CronJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := r.Log.WithValues("cronjob", req.NamespacedName)
cronJob := &batchv1.CronJob{}
if err := r.Get(ctx, req.NamespacedName, cronJob); err != nil {
log.Error(err, "unable to fetch CronJob")
// we'll ignore not-found errors, since they can't be fixed by an immediate
// requeue (we'll need to wait for a new notification), and we can get them
// on deleted requests.
return ctrl.Result{}, client.IgnoreNotFound(err)
}
// name of our custom finalizer
myFinalizerName := "batch.tutorial.kubebuilder.io/finalizer"
// examine DeletionTimestamp to determine if object is under deletion
if cronJob.ObjectMeta.DeletionTimestamp.IsZero() {
// The object is not being deleted, so if it does not have our finalizer,
// then lets add the finalizer and update the object. This is equivalent
// registering our finalizer.
if !controllerutil.ContainsFinalizer(cronJob, myFinalizerName) {
controllerutil.AddFinalizer(cronJob, myFinalizerName)
if err := r.Update(ctx, cronJob); err != nil {
return ctrl.Result{}, err
}
}
} else {
// The object is being deleted
if controllerutil.ContainsFinalizer(cronJob, myFinalizerName) {
// our finalizer is present, so lets handle any external dependency
if err := r.deleteExternalResources(cronJob); err != nil {
// if fail to delete the external dependency here, return with error
// so that it can be retried
return ctrl.Result{}, err
}
// remove our finalizer from the list and update it.
controllerutil.RemoveFinalizer(cronJob, myFinalizerName)
if err := r.Update(ctx, cronJob); err != nil {
return ctrl.Result{}, err
}
}
// Stop reconciliation as the item is being deleted
return ctrl.Result{}, nil
}
// Your reconcile logic
return ctrl.Result{}, nil
}
func (r *Reconciler) deleteExternalResources(cronJob *batch.CronJob) error {
//
// delete any external resources associated with the cronJob
//
// Ensure that delete implementation is idempotent and safe to invoke
// multiple times for same object.
}
其他
「如果这篇文章对你有用,请随意打赏」
如果这篇文章对你有用,请随意打赏
使用微信扫描二维码完成支付