### 1 client-go结构原理图示
---
![图示0.png](http://blog.zs-fighting.cn/upload/2021/07/%E5%9B%BE%E7%A4%BA-0-83347f0e10ae48beb78bf504dad87903.png)
![图示1.png](http://blog.zs-fighting.cn/upload/2021/07/%E5%9B%BE%E7%A4%BA-1-51178d85a03a4a6badac4547f5c35672.png)
kubernetes里面的apiserver只负责数据的CRUD接口实现,并不负责业务逻辑的处理,所以k8s中就通过外挂controller通过对应资源的控制器来负责事件的处理。而controller和apiserver之间的桥梁就是informer。
apiserver本质上就是一个http的rest接口实现,watch机制也是基于http协议,不过不同于一般的get请求,其通过chunk机制,来实现消息的通知。
当客户端调用`watch API`时,`apiserve`r 在`response`的`HTTP Header`中设置`Transfer-Encoding`的值为`chunked`,表示采用`分块传输`编码,客户端收到该信息后,便和服务端该链接,并等待下一个数据块,即资源的事件信息。
> HTTP 分块传输编码允许服务器为动态生成的内容维持 HTTP 持久链接。通常,持久链接需要服务器在开始发送消息体前发送Content-Length消息头字段,但是对于动态生成的内容来说,在内容创建完之前是不可知的。使用分块传输编码,数据分解成一系列数据块,并以一个或多个块发送,这样服务器可以发送数据而不需要预先知道发送内容的总大小。
![图示2.png](http://blog.zs-fighting.cn/upload/2021/07/%E5%9B%BE%E7%A4%BA-2-20f0f6d37c1b4257a33467f5c71709ca.png)
### 2 informer
---
Informer模块是Kubernetes中的基础组件,负责各组件与Apiserver的资源与事件同步。
List/Watch机制是Kubernetes中实现集群控制模块最核心的设计之一,它采用统一的异步消息处理机制,保证了消息的实时性、可靠性、顺序性和性能等,为声明式风格的API奠定了良好的基础。
Informer依赖Kubernetes的List/Watch API。通过Lister()对象来List/Get对象时,Informer不会去请求Kubernetes API,而是直接查询本地缓存,减少对Kubernetes API的直接调用。
Informer只会调用Kubernetes List和Watch两种类型的API。Informer在初始化的时候,先调用Kubernetes List API获得某种resource的全部Object,缓存在内存中;然后,调用Watch API去watch这种resource,去维护这份缓存;最后,Informer就不再调用Kubernetes 的任何API。
#### 2.1 informer组件
- Controller:并不是Kubernetes Controller,这两个Controller并没有任何联系
- Reflector:通过Kubernetes Watch API监听resource下的所有事件
- Lister:用来被调用List/Get方法
- Processor:记录并触发回调函数,Processor中记录了所有的回调函数实例(即 ResourceEventHandler实例)
- DeltaFIFO:一个增量队列,将Reflector监控变化的对象形成一个FIFO队列。`informer`首先通过`list API`罗列资源,然后调用`watch API`监听资源的变更事件,并将结果放入到一个`FIFO 队列`,队列的另一头有协程从中取出事件,并调用对应的注册函数处理事件。
- LocalStore:就是Informer的cache,这里面缓存的是apiserver中的对象(其中一部分可能还在DeltaFIFO中),此时使用者再查询对象的时候就直接从cache中-查找,减少了apiserver的压力,LocalStore只会被Lister的List/Get方法访问。
#### 2.2 informer整体工作流程
- Reflector使用ListAndWatch方法,先从apiserver中list某类资源的所有实例,拿到对象的最新版本,然后用watch方法监听该resourceVersion之后的所有变化,若中途出现异常,reflector则会从断开的resourceVersion处重新监听所有变化,一旦有Add、Del、Update动作,Reflector会收到更新通知,该事件及它所对应的API对象这个组合,被称为增量Delta,它会被放进DeltaFIFO中
- Informer会不断从这个DeltaFIFO中读取增量,Informer就会判断这个增量的事件类型,然后创建或更新本地的缓存。
- DeltaFIFO再pop这个事件到controller中,controller会调用事先注册到ResourceEventHandler回调函数进行处理。
![图示3.png](http://blog.zs-fighting.cn/upload/2021/07/%E5%9B%BE%E7%A4%BA-3-7cb27cc28c78401c92ce02647c9e2bcc.png)
在k8s中一些控制器可能会关注多种资源,比如Deployment可能会关注Pod和replicaset,replicaSet可能还会关注Pod,为了避免每个控制器都独立的去与apiserver建立连接,k8s中抽象了sharedInformer的概念,即共享informer,针对同一资源只建立一个连接。由工厂方法 shredInformerFactor 创建,内部维护了一个informer的map,当存在某种资源的informer时,会直接返回。
因为彼此共用informer,但是每个组件的处理逻辑可能各不相同,在informer中通过观察者模式,各个组件可以注册一个EventHandler来实现业务逻辑的注入
![图示4.png](http://blog.zs-fighting.cn/upload/2021/07/%E5%9B%BE%E7%A4%BA-4-ec9084d5302346b0ade60a81e2666744.png)
#### 2.3 流程实例
以Pod为例,详细说明一下Informer的关键逻辑:
1. Informer在初始化时,Reflector会先List API获得所有的Pod
2. Reflect拿到全部Pod后,会将全部Pod放到Store中
3. 如果有人调用Lister的List/Get方法获取Pod,那么Lister会直接从Store中拿数据
4. Informer初始化完成后,Reflector开始Watch Pod,监听Pod相关的所有事件;如果此时pod-01被删除,那么Reflector会监听这个事件
5. Reflector将pod-01被删除的这个事件发送到DeltaFIFO
6. DeltaFIFO首先会将这个事件存储在自己的数据结构中(实际上是一个queue),然后会直接操作Store中的数据,删除Store中的pod-01
7. DeltaFIFO再Pop这个事件到Controller中
8. Controller收到这个事件,会触发Processor的回调函数
![图示5.png](http://blog.zs-fighting.cn/upload/2021/07/%E5%9B%BE%E7%A4%BA-5-1c3e4700f8c644ecb12cce9a7175dc0b.png)
client-go理解