写点什么

Kubernetes 中定时任务的实现

  • 2020-01-10
  • 本文字数:2073 字

    阅读完需:约 7 分钟

Kubernetes 中定时任务的实现

Kubernetes 中定时任务的实现

K8s 中有许多优秀的包都可以在平时的开发中借鉴与使用,比如,任务的定时轮询、高可用的实现、日志处理、缓存使用等都是独立的包,可以直接引用。


本文将介绍 K8s 中定时任务的实现,K8s 中定时任务都是通过 wait 包实现的。wait 包在 K8s 的多个组件中都有用到,以下是 wait 包在 kubelet 中的几处使用:


func run(s *options.KubeletServer, kubeDeps *kubelet.Dependencies, stopCh <-chan struct{}) (err error) {



// kubelet 每 5 分钟一次从 apiserver 获取证书


closeAllConns, err := kubeletcertificate.UpdateTransport(wait.NeverStop, clientConfig, clientCertificateManager, 5*time.Minute)


if err != nil {


return err


}


closeAllConns, err := kubeletcertificate.UpdateTransport(wait.NeverStop, clientConfig, clientCertificateManager, 5*time.Minute)


if err != nil {


return err


}



}



func startKubelet(k kubelet.Bootstrap, podCfg *config.PodConfig, kubeCfg *kubeletconfiginternal.KubeletConfiguration, kubeDeps *kubelet.Dependencies, enableServer bool) {


// 持续监听 pod 的变化


go wait.Until(func() {


k.Run(podCfg.Updates())


}, 0, wait.NeverStop)



}


golang 中可以通过 time.Ticker 实现定时任务的执行,但在 K8s 中用了更原生的方式,使用 time.Timer 实现的。time.Ticker 和 time.Timer 的使用区别如下:


  • ticker 只要定义完成,从此刻开始计时,不需要任何其他的操作,每隔固定时间都会自动触发。

  • timer 定时器是到了固定时间后会执行一次,仅执行一次

  • 如果 timer 定时器要每隔间隔的时间执行,实现 ticker 的效果,使用 func (t *Timer) Reset(d Duration) bool


一个示例:


package main


import (


“fmt”


“sync”


“time”


)func main() {


var wg sync.WaitGroup timer1 := time.NewTimer(2 * time.Second)


ticker1 := time.NewTicker(2 * time.Second) wg.Add(1)


go func(t *time.Ticker) {


defer wg.Done()


for {


<-t.C


fmt.Println(“exec ticker”, time.Now().Format(“2006-01-02 15:04:05”))


}


}(ticker1) wg.Add(1)


go func(t *time.Timer) {


defer wg.Done()


for {


<-t.C


fmt.Println(“exec timer”, time.Now().Format(“2006-01-02 15:04:05”))


t.Reset(2 * time.Second)


}


}(timer1) wg.Wait()


}01


wait 包中的核心代码


核心代码


k8s.io/apimachinery/pkg/util/wait/wait.go:


func JitterUntil(f func(), period time.Duration, jitterFactor float64, sliding bool, stopCh <-chan struct{}) {


var t *time.Timer


var sawTimeout bool


for {


select {


case <-stopCh:


return


default:


} jitteredPeriod := period


if jitterFactor > 0.0 {


jitteredPeriod = Jitter(period, jitterFactor)


} if !sliding {


t = resetOrReuseTimer(t, jitteredPeriod, sawTimeout)


} func() {


defer runtime.HandleCrash()


f()


}() if sliding {


t = resetOrReuseTimer(t, jitteredPeriod, sawTimeout)


} select {


case <-stopCh:


return


case <-t.C:


sawTimeout = true


}


}


}…


func resetOrReuseTimer(t *time.Timer, d time.Duration, sawTimeout bool) *time.Timer {


if t == nil {


return time.NewTimer(d)


}


if !t.Stop() && !sawTimeout {


<-t.C


}


t.Reset(d)


return t


}几个关键点的说明:


  1. 如果 sliding 为 true,则在 f() 运行之后计算周期。如果为 false,那么 period 包含 f() 的执行时间。

  2. 在 golang 中 select 没有优先级选择,为了避免额外执行 f(),在每次循环开始后会先判断 stopCh chan。


K8s 中 wait 包其实是对 time.Timer 做了一层封装实现。


02


wait 包常用的方法


定期执行一个函数,永不停止,可以使用 Forever 方法:


func Forever(f func(), period time.Duration)


在需要的时候停止循环,那么可以使用下面的方法,增加一个用于停止的 chan 即可,方法定义如下:


func Until(f func(), period time.Duration, stopCh <-chan struct{})


上面的第三个参数 stopCh 就是用于退出无限循环的标志,停止的时候我们 close 掉这个 chan 就可以了。


有时候,我们还会需要在运行前去检查先决条件,在条件满足的时候才去运行某一任务,这时候可以使用 Poll 方法:


func Poll(interval, timeout time.Duration, condition ConditionFunc)


这个函数会以 interval 为间隔,不断去检查 condition 条件是否为真,如果为真则可以继续后续处理;如果指定了 timeout 参数,则该函数也可以只常识指定的时间。


PollUntil 方法和上面的类似,但是没有 timeout 参数,多了一个 stopCh 参数,如下所示:


PollUntil(interval time.Duration, condition ConditionFunc, stopCh <-chan struct{}) error


此外还有 PollImmediate 、 PollInfinite 和 PollImmediateInfinite 方法。


03


总结


本文主要讲了 K8s 中定时任务的实现与对应包(wait)中方法的使用。


通过阅读 K8s 的源代码,可以发现 K8s 中许多功能的实现也都是我们需要在平时工作中用的,其大部分包的性能都是经过大规模考验的,通过使用其相关的工具包不仅能学到大量的编程技巧也能避免自己造轮子。


原文:https://dwz.cn/5zxd9ZqU


2020-01-10 13:531584

评论

发布
暂无评论
发现更多内容

透过关键基础设施安全事件谈SBOM

安势信息

Gartner SCA 软件物料清单 SBOM 清源CleanSource SCA

JavaScript刷LeetCode拿offer-双指针技巧

Geek_07a724

JavaScript LeetCode

使用Vmware创建Centos7虚拟机(安装和配置网络环境、xshell连接、防火墙、yum仓库、磁盘挂载、重启命令)

A-刘晨阳

Linux 运维 vmware 11月月更

TOGAF架构框架3-ADM架构开发技术

马说IT

架构 TOGAF ADM架构开发方法

前端工程师leetcode算法面试必备-二分搜索算法(中)

js2030code

JavaScript LeetCode

FFmpeg-ffplay播放器分析(1).md

Changing Lin

音视频 ffmpeg 安卓

云原生生态 我们选择了哪些

Rayzh

Docker Kubernetes, 云原生, eBPF

Vue基础知识整理【建议收藏】

何极光

Vue 基础

融云通信云服务,助力医疗招聘平台构建行业护城河

融云 RongCloud

通信 医疗 融云

JavaScript刷LeetCode拿offer-双指针技巧Medium篇

Geek_07a724

JavaScript LeetCode

前端工程师leetcode算法面试必备-二分搜索算法(下)

js2030code

JavaScript LeetCode

云渲染是CG的最后一道工序,四个特性让你的渲染更高效

Finovy Cloud

云渲染 云渲染农场

云栖大会,一场边缘云计算的「超前瞻」之约

阿里云CloudImagine

云栖大会 边缘云

Nginx配置中root和alias分不清?本文3分钟帮你解惑!

wljslmz

nginx 服务器 root 11月月更 alias

Echarts实现全国地图展示

格斗家不爱在外太空沉思

JavaScript eCharts 11月月更

BNBDao三三复制公排dapp系统开发

开发微hkkf5566

如何构建并提高自己的核心竞争力?

老张

核心竞争力

JavaScript刷LeetCode拿offer-滑动窗口

Geek_07a724

JavaScript LeetCode

Go语言入门11—接口

良猿

Go golang 后端 11月月更

Baklib经验分享 | 一些搭建帮助中心的攻略

Baklib

帮助中心

一比一手写迷你版vue,彻底搞懂vue运行机制

hellocoder2029

JavaScript

决策树-分类树

烧灯续昼2002

机器学习 决策树 sklearn 11月月更

前端工程师leetcode算法面试必备-二分搜索算法(上)

js2030code

JavaScript LeetCode

TOGAF企业架构框架4-内容框架

马说IT

架构 TOGAF 企业架构框架 内容框架

发布自己的第一个npm包

格斗家不爱在外太空沉思

npm nodejs 11月月更

Linux常用基础命令(巨全)

A-刘晨阳

Linux 运维 11月月更 基础命令

如何写成高性能的代码(三):巧用稀疏矩阵节省内存占用

葡萄城技术团队

前端 稀疏矩阵

一文读懂Js中的this指向

hellocoder2029

JavaScript

怎么从容地拿捏时间?来自富兰克林的启示

无人之路

时间管理

一个非常常见的问题:var、let和const

肥晨

11月月更 js6 js基础

Baklib知识分享|企业知识管理难,该如何解决?

Baklib

Kubernetes 中定时任务的实现_语言 & 开发_华为云原生团队_InfoQ精选文章