可以先设想一下如果自己去实现的话,该如何设计。Client 和 Server 端都要去适配这是必然的,因为 Informer 现在是 ListWatch 机制,服务端并不支持流式 List。因此可以有个初步的方向:
成都创新互联-专业网站定制、快速模板网站建设、高性价比昆都仑网站开发、企业建站全套包干低至880元,成熟完善的模板库,直接使用。一站式昆都仑网站制作公司更省心,省钱,快速模板网站建设找我们,业务覆盖昆都仑地区。费用合理售后完善,10余年实体公司更值得信赖。
客户端的适配相对简单,重点还是放在 Server 端如何实现。先回顾下之前 List 的逻辑,在前一篇 Stale Read 里面已经介绍过了。
为方面描述,下文统一使用 RV 代指 Resourceversion,本节逻辑均基于 v1.26.9 版本,且忽略分页查询,因为分页是直接走 Etcd 的。
无论是 List 还是 Watch 请求,其 query 均支持传入 RV,服务端会根据请求的 RV 的不同做相应的处理,根据 RV 的值可以分为三种情况
- 未设置或者显示设置 RV=""
- RV = "0"
- RV = "非 0 值"
对于前两种情况,List 会直接返回 WatchCache Store 中的内容,即服务端缓存好的 Etcd 的全部相关数据。
对于第三种情况,会等待服务端缓存数据的最大版本要超过传入的 RV 之后再返回缓存内的数据,如果等待了一段时间(3s)后缓存中的数据仍然没有达到指定版本,则会报错返回 "Too large resource version",并告诉客户端可以在 1s 之后重试。
新版中已经修复了 List Stale Read 的问题,对于前两种情况,其会先从 kube-apiserver 获取 Etcd 最新的 RV,等待 WatchCache Store 内容追平 RV 后再一次性的返回。
也就是说服务端是可以知道自己是否已经包含最新全量数据的,在这个基础上再以流式方式返回即可。当前已有的流式 API 就是 Watch,所以可以在此基础上支持 List 的效果。为什么不直接在 List 请求基础上改呢,因为改 List 的话,会涉及到太多的客户端侧的适配,List 会经常单独使用,而 Watch 基本是在 Informer 里面使用。
所以最终的工作就会变成如何使用 Watch API 实现 List 的效果,但数据仍然以流式返回给客户端,同时 Informer 修改 ListWatch 方式为只使用 Watch API 实现之前的效果。下文以详细介绍服务端实现为主,客户端适配的部分会比较简单的介绍下。
通过为 Watch API 添加一个 SendInitialEvents=true 参数来支持 List 的效果。Server 端接收到 Watch 请求后判断哪些数据是应该作为 InitEvents 发送给客户端,同时在发送完这些数据之后发送一个特定的 BOOKMARK Event(带特定 Annotation 的 BOOKMARK,其 RV 对应下文的 bookmarkAfterRV)给客户端作为服务端通知客户端 InitEvents 发送完毕的标志,客户端在接收到指定 BOOKMARK Event 后,将之前接收到的所有 InitEvent 作为 List 的结果处理。
下面是基于 v1.29 代码的分析,此时 v1.29 还在 alpha 状态,提到的旧版代表 1.27 之前的版本,新版代表 v1.29。如果你看到的代码和下面描述的不一致,有可能是代码版本导致的。
图片
从 WatchCache 开始右面四个蓝色的是在 kube-apiserver 启动的时候开始执行的,G1 G2 代表两个 goroutine,分别用来从 Etcd 获取数据,以及发送数据给客户端 CacheWatcher 的 input chan
上述过程描述了服务端启动时的数据处理流程,接下来看有客户端请求时的处理流程
2a 开始从 WatchCache Store 中获取需要返回的数据,此时的处理逻辑旧版本相同,返回 Store 中的全部数据,并记录 Store 数据的最大 RV 供下一步使用;
2b 消费 input chan 中的事件,对比其 RV 是否比 2a 传入的 RV 大,或者如果是 BOOKMARK 类型并且 RV 等与 2a 传入的 RV,且尚未发送 bookmarkAfterRV 的事件,则此 BOOKMARK 事件就会被当做 List 结束的标志,为其设置 Annotation: k8s.io/initial-events-end,最后发送给客户端;
至此,服务端的主要流程已经介绍完,客户端 Informer 也做了对应的适配,如果开启 WathList 功能的话,会发送 Watch 请求来获取一遍全量数据,等到接收到携带 Annotation: k8s.io/initial-events-end 的 BOOKMARK 事件后,记录其 RV,将在此期间接受并处理后的对象作为 List 的结果。最后再次以上述 RV 作为参数调用 Watch 请求,从这一步开始就是 Informer 传统意义上的 Watch 逻辑了。
图片
图片来自 KEP 3157 watch-list,其实里面也包含时序图,不过里面的书序图画的有一些问题,和代码不一致,所以这里并没有直接使用他的时序图,而是重新画了。
可以结合上面两个图理解整个过程,上图中的 a 对应时序图中的 2a,b 对应时序图中的 2b,c 对应时序图中的 G2.1。最下面白色部分对应时序图中 G1 的逻辑,即从 Etcd 获取数据,客户端请求的处理是自上到下的,而数据返回是自下而上的。
上述处理逻辑中存在很多的细节,需要额外注意下
本篇主要分析了 WatchList 的实现原理和逻辑,其中不乏一些细节处理,后续也会和社区就有关细节进一步讨论。在此 KEP 中同时还介绍了另外两个用来降低 kube-apiserver 内存压力的修改,篇幅有限,将会在下一篇中进行介绍,同时也会给出所有优化工作做完前后的效果对比。敬请期待~
当前文章:从ListWatch到WatchList
地址分享:http://www.stwzsj.com/qtweb/news41/15041.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联