【agollo源码分析】

最近运维在全量推配置项上apollo,看了一下,golang的apollo使用,大概记录了一下;

感觉上apollo在spring上直接@enableapolloconfig注解比go的方便;

golang的需要自己流程式的client拉配置;还要自己实现OnNewestChange的实现;

golang如何获取apollo中保存的配置呢,eg

package main
​
import (
    "fmt"
​
    "github.com/apolloconfig/agollo/v4"
    "github.com/apolloconfig/agollo/v4/component/log"
    "github.com/apolloconfig/agollo/v4/env/config"
)
​
func main() {
    //注意golang默认本地配置文件为app.properties
    //也可通过设置环境变量AGOLLO_CONF设置路径
    c := &config.AppConfig{
        AppID:          "testApplication_yang",
        Cluster:        "dev",
        IP:             "http://106.54.227.205:8080",
        NamespaceName:  "dubbo",
        IsBackupConfig: true,
        Secret:         "6ce3ff7e96a24335a9634fe9abca6d51",
    }
​
    agollo.SetLogger(&log.DefaultLogger{})
​
    client, _ := agollo.StartWithConfig(func() (*config.AppConfig, error) {
        return c, nil
    })
    fmt.Println("初始化Apollo配置成功")
​
    //Use your apollo key to test
    cache := client.GetConfigCache(c.NamespaceName)
    value, _ := cache.Get("key")
    fmt.Println(value)
}

agollo源码过程


1 开始初始化apollo


此处会初始化各种默认控制器

func init() {
    extension.SetCacheFactory(&memory.DefaultCacheFactory{})
    extension.SetLoadBalance(&roundrobin.RoundRobin{})
    extension.SetFileHandler(&jsonFile.FileHandler{})
    extension.SetHTTPAuth(&sign.AuthSignature{})
​
    // file parser
    extension.AddFormatParser(constant.DEFAULT, &normal.Parser{})
    extension.AddFormatParser(constant.Properties, &properties.Parser{})
    extension.AddFormatParser(constant.YML, &yml.Parser{})
    extension.AddFormatParser(constant.YAML, &yaml.Parser{})
}

1.1 代码初始化

    c := &config.AppConfig{
        AppID:          "testApplication_yang",
        Cluster:        "dev",
        IP:             "http://106.54.227.205:8080",
        NamespaceName:  "dubbo",
        IsBackupConfig: true,
        Secret:         "6ce3ff7e96a24335a9634fe9abca6d51",
    }
    
    client, _ := agollo.StartWithConfig(func() (*config.AppConfig, error) {
        return c, nil
    })

1.2 如果依据默认配置初始化

agollo.Start()
// Start 根据默认文件启动
func Start() (Client, error) {
    return StartWithConfig(nil)
}
其中,如果设置了配置文件的环境变量AGOLLO_CONF,会获取对应的AGOLLO_CONF的值作为配置文件;
如果没有设置AGOLLO_CONF,默认本地配置文件为app.properties;
其中app.properties需为json格式,不然默认json解析有问题;
{
    "appId": "test",
    "cluster": "dev",
    "namespaceName": "application,abc1",
    "ip": "localhost:8888",
    "backupConfigPath":""
}

1.3 StartWithConfig根据配置启动

开始根据配置启动

c := create()创建internalClient客户端

storage.CreateNamespaceConfig,创建storage.Cache结构,其中Cache{apolloConfigCache sync.Map , changeListeners *list.List},apolloConfigCache为namespace,默认缓存的map结构,changeListeners是一个监听链表结构;

// StartWithConfig 根据配置启动

func StartWithConfig(loadAppConfig func() (*config.AppConfig, error)) (Client, error) {

    c := create()
    //c := create()创建internalClient客户端
​
    c.cache = storage.CreateNamespaceConfig(appConfig.NamespaceName)
    //创建storage.Cache结构
    其中Cache{apolloConfigCache sync.Map  , changeListeners *list.List},
        apolloConfigCache为namespace,默认缓存的map结构,
        changeListeners是一个监听链表结构
        
    appConfig.Init()
​
    serverlist.InitSyncServerIPList(c.getAppConfig)
    
    
​
    //Sync开始同步apollo远端保存的配置
    configs := syncApolloConfig.Sync(c.getAppConfig)
    if len(configs) == 0 && appConfig != nil && appConfig.MustStart {
        return nil, errors.New("start failed cause no config was read")
    }
​
    for _, apolloConfig := range configs {
        c.cache.UpdateApolloConfig(apolloConfig, c.getAppConfig)
    }
​
    log.Debug("init notifySyncConfigServices finished")
​
    //start long poll sync config
    configComponent := &notify.ConfigComponent{}
    configComponent.SetAppConfig(c.getAppConfig)
    configComponent.SetCache(c.cache)
    go component.StartRefreshConfig(configComponent)
​
    log.Info("agollo start finished ! ")
​
    return c, nil
}

InitSyncServerIPList 初始化同步服务器信息列表,其中InitSyncServerIPList如下,component.StartRefreshConfig真实调用func (s *SyncServerIPListComponent) Start() ,SyncServerIPList同步远程apollo ip及其上服务信息;并持续更新

func InitSyncServerIPList(appConfig func() config.AppConfig) {
    go component.StartRefreshConfig(&SyncServerIPListComponent{appConfig})
}
​
//SyncServerIPListComponent set timer for update ip list
//interval : 20m
type SyncServerIPListComponent struct {
    appConfig func() config.AppConfig
}
​
//Start 启动同步服务器列表
func (s *SyncServerIPListComponent) Start() {
    SyncServerIPList(s.appConfig)
    log.Debug("syncServerIpList started")
​
    t2 := time.NewTimer(refreshIPListInterval)
    for {
        select {
        case <-t2.C:
            SyncServerIPList(s.appConfig)
            t2.Reset(refreshIPListInterval)
        }
    }
}

SyncServerIPList获得远端apollo ip,服务主页,服务信息对应map

func SyncServerIPList(appConfigFunc func() config.AppConfig) (map[string]*config.ServerInfo, error) {
    if appConfigFunc == nil {
        panic("can not find apollo config!please confirm!")
    }
​
    appConfig := appConfigFunc()
    c := &env.ConnectConfig{
        AppID:  appConfig.AppID,
        Secret: appConfig.Secret,
    }
    if appConfigFunc().SyncServerTimeout > 0 {
        duration, err := time.ParseDuration(strconv.Itoa(appConfigFunc().SyncServerTimeout) + "s")
        if err != nil {
            return nil, err
        }
        c.Timeout = duration
    }
    serverMap, err := http.Request(appConfig.GetServicesConfigURL(), c, &http.CallBack{
        SuccessCallBack: SyncServerIPListSuccessCallBack,
        AppConfigFunc:   appConfigFunc,
    })
    if serverMap == nil {
        return nil, err
    }
​
    m := serverMap.(map[string]*config.ServerInfo)
    server.SetServers(appConfig.GetHost(), m)
    return m, err
}

其中appConfig.GetServicesConfigURL()为获取远程服务信息url,eg格式为“”,

Request 建立网络请求,可以简单的看出在http.StatusOK下读取返回值,使用回调函数callBack.SuccessCallBack(responseBody, *callBack),解析数据到([]config.ServerInfo)中,即得到远端apollo各服务主页对应的服务信息

func SyncServerIPListSuccessCallBack(responseBody []byte, callback http.CallBack) (o interface{}, err error) {
    log.Debug("get all server info:", string(responseBody))
​
    tmpServerInfo := make([]*config.ServerInfo, 0)
​
    err = json.Unmarshal(responseBody, &tmpServerInfo)
    ...
}

2 Sync开始同步apollo远端保存的配置


2.1 获取远端保持的配置至 []*config.ApolloConfig

var syncApolloConfig = remote.CreateSyncApolloConfig() 创建syncApolloConfig

SyncWithNamespace获取对应namespace的apolloConfig;

func (a *syncApolloConfig) Sync(appConfigFunc func() config.AppConfig) []*config.ApolloConfig {
    appConfig := appConfigFunc()
    configs := make([]*config.ApolloConfig, 0, 8)
    config.SplitNamespaces(appConfig.NamespaceName, func(namespace string) {
        apolloConfig := a.SyncWithNamespace(namespace, appConfigFunc)
        if apolloConfig != nil {
            configs = append(configs, apolloConfig)
            return
        }
        configs = append(configs, loadBackupConfig(appConfig.NamespaceName, appConfig)...)
    })
    return configs
}

RequestRecovery中loadBalance(appConfig)负载均衡的获取apollo主页url;组成requestURL,发起Request得到response结果,使用回调处理函数得到最后结果;其中回调函数调用的是func processJSONFiles(b []byte, callback http.CallBack)函数,解析返回值至config.ApolloConfig.Configurations

//RequestRecovery 可以恢复的请求

func RequestRecovery(appConfig config.AppConfig,
    connectConfig *env.ConnectConfig,
    callBack *CallBack) (interface{}, error) {
    format := "%s%s"
    var err error
    var response interface{}
​
    for {
        host := loadBalance(appConfig)
        if host == "" {
            return nil, err
        }
​
        requestURL := fmt.Sprintf(format, host, connectConfig.URI)
        response, err = Request(requestURL, connectConfig, callBack)
        if err == nil {
            return response, nil
        }
​
        if host == appConfig.GetHost() {
            return response, err
        }
​
        server.SetDownNode(host, appConfig.GetHost())
    }
}

发起Request得到response结果

func Request(requestURL string, connectionConfig *env.ConnectConfig, callBack *CallBack) (interface{}, error) {
    client := &http.Client{}
    for {
        retry++
​
        if retry > retries {
            break
        }
        req, err := http.NewRequest("GET", requestURL, nil)
​
        res, err := client.Do(req)
​
        //not modified break
        switch res.StatusCode {
        case http.StatusOK:
            responseBody, err := ioutil.ReadAll(res.Body)
            if err != nil {
                log.Errorf("Connect Apollo Server Fail,url : %s ,Error: %s ", requestURL, err)
                // if error then sleep
                time.Sleep(onErrorRetryInterval)
                continue
            }
        ...
}

2.2 根据远端换回内容更新内存;并写备份

UpdateApolloConfigCache更新新的配置至cache缓存,并在返回changeList中标记哪些是create,Modify,Deleted;

GetNotificationsMap返回对应空间的监听值;

pushNewestChanges 推送最新的消息至listener.OnNewestChange(e);其中listener是自己写的listener插件;

createConfigChangeEvent创建事件类型,并将事件event推送至listener.OnChange;

如果需要备份文件appConfig.GetIsBackupConfig(),func (fileHandler *FileHandler) WriteConfigFile到对应configPath,如果configPath没有设置,则将apolloConfig写入到"appID-namespace.json"文件中;

func (c *Cache) UpdateApolloConfig(apolloConfig *config.ApolloConfig, appConfigFunc func() config.AppConfig) {
    appConfig := appConfigFunc()
    appConfig.SetCurrentApolloConfig(&apolloConfig.ApolloConnConfig)
​
    // get change list
    changeList := c.UpdateApolloConfigCache(apolloConfig.Configurations, configCacheExpireTime, apolloConfig.NamespaceName)
​
    notify := appConfig.GetNotificationsMap().GetNotify(apolloConfig.NamespaceName)
​
    // push all newest changes
    c.pushNewestChanges(apolloConfig.NamespaceName, apolloConfig.Configurations, notify)
​
    if len(changeList) > 0 {
        // create config change event base on change list
        event := createConfigChangeEvent(changeList, apolloConfig.NamespaceName, notify)
​
        // push change event to channel
        c.pushChangeEvent(event)
    }
​
    if appConfig.GetIsBackupConfig() {
        // write config file async
        apolloConfig.AppID = appConfig.AppID
        go extension.GetFileHandler().WriteConfigFile(apolloConfig, appConfig.GetBackupConfigPath())
    }
}

3 监控配置变化


定时拉取远程配置,并更新至缓存cache,推送消息至OnChange,OnNewestChange

OnChange,OnNewestChange方法的实现需要根据自身配置而定

configComponent := &notify.ConfigComponent{}

configComponent.SetAppConfig(c.getAppConfig)

configComponent.SetCache(c.cache)

go component.StartRefreshConfig(configComponent)

持久获取配置更新

//Start 启动配置组件定时器

func (c *ConfigComponent) Start() {
    t2 := time.NewTimer(longPollInterval)
    instance := remote.CreateAsyncApolloConfig()
    //long poll for sync
    for {
        select {
        case <-t2.C:
            configs := instance.Sync(c.appConfigFunc)
            for _, apolloConfig := range configs {
                c.cache.UpdateApolloConfig(apolloConfig, c.appConfigFunc)
            }
            t2.Reset(longPollInterval)
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值