这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos
pod | 语言类型(language) | 服务类型(business-service-type) |
---|---|---|
nginx | c | web |
tomcat | c | web |
mysql | java | storage |
type Indexers map[string]IndexFunc
type Indexer interface {
// 存储相关的,不在本章讨论
Store
// indexName表示分类方式,obj表示用来查询的对象,
// 例如indexName等于BY_LANGUAGE,obj等于nginx的pod对象,
// 那么Index方法就会根据BY_LANGUAGE去获取pod对象的语言类型,即c语言,再返回所有c语言类型的对象
// 简而言之就是:查找和obj同一个语言类型的所有对象
Index(indexName string, obj interface{}) ([]interface{}, error)
// indexName表示分类方式,indexedValue表示分类的值,
// 例如indexName等于BY_LANGUAGE,indexedValue等于c,
// 那么IndexKeys方法就会返回所有语言类型等于c的对象的key
IndexKeys(indexName, indexedValue string) ([]string, error)
// indexName表示分类方式,
// 例如indexName等于BY_LANGUAGE,
// ListIndexFuncValues返回的就是java和c
ListIndexFuncValues(indexName string) []string
// indexName表示分类方式,indexedValue表示分类的值,
// 例如indexName等于BY_LANGUAGE,indexedValue等于c,
// 那么ByIndex方法就会返回所有语言类型等于c的对象
ByIndex(indexName, indexedValue string) ([]interface{}, error)
// Indexers是个map,key是分类方式,
// 本文中key有两个,分别是BY_LANGUAGE和BY_SERVICE,
// value则是个方法,
// key等于BY_LANGUAGE的时候,该方法的入参是个对象pod,返回值是这个pod的语言,
// key等于BY_SERVICE的时候,该方法的入参是个对象pod,返回值是这个pod的服务类型,
GetIndexers() Indexers
// 添加Indexers
AddIndexers(newIndexers Indexers) error
}
type Store interface {
// Add adds the given object to the accumulator associated with the given object's key
Add(obj interface{}) error
// Update updates the given object in the accumulator associated with the given object's key
Update(obj interface{}) error
// Delete deletes the given object from the accumulator associated with the given object's key
Delete(obj interface{}) error
// List returns a list of all the currently non-empty accumulators
List() []interface{}
// ListKeys returns a list of all the keys currently associated with non-empty accumulators
ListKeys() []string
// Get returns the accumulator associated with the given object's key
Get(obj interface{}) (item interface{}, exists bool, err error)
// GetByKey returns the accumulator associated with the given key
GetByKey(key string) (item interface{}, exists bool, err error)
// Replace will delete the contents of the store, using instead the
// given list. Store takes ownership of the list, you should not reference
// it after calling this function.
Replace([]interface{}, string) error
// Resync is meaningless in the terms appearing here but has
// meaning in some implementations that have non-trivial
// additional behavior (e.g., DeltaFIFO).
Resync() error
}
type cache struct {
// cacheStorage bears the burden of thread safety for the cache
cacheStorage ThreadSafeStore
// keyFunc is used to make the key for objects stored in and retrieved from items, and
// should be deterministic.
keyFunc KeyFunc
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Wed, 14 Jun 2023 22:56:37 GMT
Content-Length: 219
Connection: close
{
"language": [
"indexer-tutorials/mysql-556b999fd8-22hqh",
"indexer-tutorials/nginx-deployment-696cc4bc86-2rqcg",
"indexer-tutorials/nginx-deployment-696cc4bc86-bkplx",
"indexer-tutorials/nginx-deployment-696cc4bc86-m7wwh"
]
}
func NewIndexer(keyFunc KeyFunc, indexers Indexers) Indexer {
return &cache{
cacheStorage: NewThreadSafeStore(indexers, Indices{}),
keyFunc: keyFunc,
}
}
func NewIndexerInformer(
lw ListerWatcher,
objType runtime.Object,
resyncPeriod time.Duration,
h ResourceEventHandler,
indexers Indexers,
) (Indexer, Controller) {
// This will hold the client state, as we know it.
clientState := NewIndexer(DeletionHandlingMetaNamespaceKeyFunc, indexers)
return clientState, newInformer(lw, objType, resyncPeriod, h, clientState)
}
func MetaNamespaceKeyFunc(obj interface{}) (string, error) {
if key, ok := obj.(ExplicitKey); ok {
return string(key), nil
}
meta, err := meta.Accessor(obj)
if err != nil {
return "", fmt.Errorf("object has no meta: %v", err)
}
if len(meta.GetNamespace()) > 0 {
return meta.GetNamespace() + "/" + meta.GetName(), nil
}
return meta.GetName(), nil
}
func (c *controller) processLoop() {
for {
obj, err := c.config.Queue.Pop(PopProcessFunc(c.config.Process))
if err != nil {
if err == ErrFIFOClosed {
return
}
if c.config.RetryOnError {
// This is the safe way to re-enqueue.
c.config.Queue.AddIfNotPresent(obj)
}
}
}
}
只有把上述事情都做了,Indexer接口定义的那些方法才能正常工作,例如IndexKeys方法,输入按语言分类和java这两个参数,返回的就是所有符合条件的pod的对象key
猜到Indexer在新增对象时要做的事情,接下来读代码就非常轻松了,代码入口位置如下图所示,先算出对象key,然后调用方法
再打开上图中的c.cacheStorage.Add方法的源码,我滴个乖乖,真的好清晰整齐:先把旧数据暂存在一个变量中,再把新数据放入真正存数据的地方items,然后才去更新各种和该数据有关的索引信息,这么简洁清晰的代码读起来是享受
展开updateIndices方法,一切都是那么顺理成章,前面的推测都在此被印证
// updateIndices modifies the objects location in the managed indexes, if this is an update, you must provide an oldObj
// updateIndices must be called from a function that already has a lock on the cache
func (c *threadSafeMap) updateIndices(oldObj interface{}, newObj interface{}, key string) {
// if we got an old object, we need to remove it before we add it again
// 如果有旧数据,那么此刻应该也有旧数据的索引信息(例如java对应的pod的对象key),此处应该清理掉
if oldObj != nil {
c.deleteFromIndices(oldObj, key)
}
// c.indexers就是所有分类方式,在本文中就是按语言和按服务类型两种方式分类,
// 这里用每一种分类方式分别对新增的对象做处理
for name, indexFunc := range c.indexers {
// indexFunc是我们按照业务需求自己写的,例如按照语言分类的时候,就是取出pod的language这个label的值
indexValues, err := indexFunc(newObj)
if err != nil {
panic(fmt.Errorf("unable to calculate an index entry for key %q on index %q: %v", key, name, err))
}
// c.indices是个map,key是分类方式(例如按语言分类,indexer_language),value是Index对象
// Index对象也是个map,key是该分类下的某个值,例如java,value是个集合,所有pod语言类型是java的pod,它们的对象key组成了这个集合
index := c.indices[name]
if index == nil {
index = Index{}
c.indices[name] = index
}
// 每一种分类方式都有一个index,例如按语言分类,有自己的index,
// 在语言分类的index中,每种语言都有一个集合,这里面是所有该语言的pod的对象key,
// 这里的index应该有c和java两个key,c这个key对应的集合里面有nginx和mysql的pod的对象key,jva这个key对应的集合里面有tomcat这个pod的对象key,
// 下面的代码就是把pod的对象key放入对应的集合中
for _, indexValue := range indexValues {
set := index[indexValue]
if set == nil {
set = sets.String{}
index[indexValue] = set
}
set.Insert(key)
}
}
}
func (c *cache) IndexKeys(indexName, indexKey string) ([]string, error) {
return c.cacheStorage.IndexKeys(indexName, indexKey)
}
func (c *threadSafeMap) IndexKeys(indexName, indexedValue string) ([]string, error) {
c.lock.RLock()
defer c.lock.RUnlock()
indexFunc := c.indexers[indexName]
// 通过indexFunc来判断这个分类方式是否存在
if indexFunc == nil {
return nil, fmt.Errorf("Index with name %s does not exist", indexName)
}
// 拿到按照语言分类的Index对象
index := c.indices[indexName]
// index对象中有每一种语言对于的pod的对象key,取出来直接返回即可
set := index[indexedValue]
return set.List(), nil
}