中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Apollo客戶端設計原理的源碼解析是怎樣的

發布時間:2021-09-29 14:07:56 來源:億速云 閱讀:174 作者:柒染 欄目:編程語言

這期內容當中小編將會給大家帶來有關 Apollo客戶端設計原理的源碼解析是怎樣的,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

1. 設計原理

圖 1 簡要描述了 Apollo 客戶端的實現原理。

Apollo客戶端設計原理的源碼解析是怎樣的

  • 客戶端和服務端保持了一個長連接,編譯配置的實時更新推送。

  • 定時拉取配置是客戶端本地的一個定時任務,默認為每 5 分鐘拉取一次,也可以通過在運行時指定 System Property:apollo.refreshInterval 來覆蓋,單位是分鐘,推送+定時拉取=雙保險。

  • 客戶端從 Apollo 配置中心服務端獲取到應用的最新配置后,會保存在內存中。

  • 客戶端會把從服務端獲取到的配置在本地文件系統緩存一份,當服務或者網絡不可用時,可以使用本地的配置,也就是我們的本地開發模式 env=Local。

2. 和 Spring 集成的原理

Apollo 除了支持 API 方式獲取配置,也支持和 Spring/Spring Boot 集成,集成后可以直接通過 @Value 獲取配置,我們來分析下集成的原理。

Spring 從 3.1 版本開始增加了 ConfigurableEnvironment 和 PropertySource:

  • ConfigurableEnvironment 實現了 Environment 接口,并且包含了多個 Property-Source。

  • PropertySource 可以理解為很多個 Key-Value 的屬性配置,在運行時的結構形如圖 2 所示。

Apollo客戶端設計原理的源碼解析是怎樣的

需要注意的是,PropertySource 之間是有優先級順序的,如果有一個 Key 在多個 property source 中都存在,那么位于前面的 property source 優先。

集成的原理就是在應用啟動階段,Apollo 從遠端獲取配置,然后組裝成 PropertySource 并插入到第一個即可,如圖 3 所示。

Apollo客戶端設計原理的源碼解析是怎樣的

3. 啟動時初始化配置到 Spring

客戶端集成 Spring 的代碼分析,我們也采取簡化的方式進行講解。

首先我們來分析,在項目啟動的時候從 Apollo 拉取配置,是怎么集成到 Spring 中的。創建一個 PropertySourcesProcessor 類,用于初始化配置到 Spring PropertySource 中。具體代碼如下所示。

@Componentpublic class PropertySourcesProcessor implements BeanFactoryPostProcessor, EnvironmentAware {
    String APOLLO_PROPERTY_SOURCE_NAME = "ApolloPropertySources";private ConfigurableEnvironment environment;@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {// 啟動時初始化配置到Spring PropertySourceConfig config = new Config();
        ConfigPropertySource configPropertySource = new ConfigPropertySource("ap-plication", config);

        CompositePropertySource composite = new CompositePropertySource(APOLLO_PROPERTY_SOURCE_NAME);
        composite.addPropertySource(configPropertySource);

        environment.getPropertySources().addFirst(composite);
    }@Overridepublic void setEnvironment(Environment environment) {this.environment = (ConfigurableEnvironment) environment;
    }
}

實現 EnvironmentAware 接口是為了獲取 Environment 對象。實現 BeanFactory-Post-Processor 接口,我們可以在容器實例化 bean 之前讀取 bean 的信息并修改它。

Config 在 Apollo 中是一個接口,定義了很多讀取配置的方法,比如 getProperty:getIntProperty 等。通過子類去實現這些方法,在這里我們就簡化下,直接定義成一個類,提供兩個必要的方法,具體代碼如下所示。

public class Config {public String getProperty(String key, String defaultValue) {if (key.equals("bianchengName")) {return "C語言中文網";
        }return null;
    }public Set<String> getPropertyNames() {
        Set<String> names = new HashSet<>();
        names.add("bianchengName");return names;
    }
}

Config 就是配置類,配置拉取之后會存儲在類中,所有配置的讀取都必須經過它,我們在這里就平格定義需要讀取的 key 為 bianchengName。

然后需要將 Config 封裝成 PropertySource 才能插入到 Spring Environment 中。

定義一個 ConfigPropertySource 用于將 Config 封裝成 PropertySource,ConfigProperty-Source 繼承了 EnumerablePropertySource,EnumerablePropertySource 繼承了 PropertySource。具體代碼如下所示。

public class ConfigPropertySource extends EnumerablePropertySource<Config> {private static final String[] EMPTY_ARRAY = new String[0];

    ConfigPropertySource(String name, Config source) {super(name, source);
    }@Overridepublic String[] getPropertyNames() {
        Set<String> propertyNames = this.source.getPropertyNames();if (propertyNames.isEmpty()) {return EMPTY_ARRAY;
        }return propertyNames.toArray(new String[propertyNames.size()]);
    }@Overridepublic Object getProperty(String name) {return this.source.getProperty(name, null);
    }
}

需要做的操作還是重寫 getPropertyNames 和 getProperty 這兩個方法。當調用這兩個方法時,返回的就是 Config 中的內容。

最后將 ConfigPropertySource 添加到 CompositePropertySource 中,并且加入到 Confi-gu-rable-Environment 即可。

定義一個接口用來測試有沒有效果,具體代碼如下所示。

@RestControllerpublic class ConfigController {@Value("${bianchengName:zhangsan}")private String name;@GetMapping("/get")private String bianchengUrl;@GetMapping("/get")public String get() {return name + bianchengUrl;
    }
}

在配置文件中增加對應的配置:

bianchengName=xxx
bianchengUrl=http://minglisoft.cn/cloud

在沒有增加上面講的代碼之前,訪問 /get 接口返回的是 xxxhttp://c.biancheng.net。加上上面講解的代碼之后,返回的內容就變成了猿天地 http://c.biancheng.net。

這是因為我們在 Config 中對應 bianchengName 這個 key 的返回值是猿天地,也間接證明了在啟動的時候可以通過這種方式來覆蓋本地的值。這就是 Apollo 與 Spring 集成的原理。

4. 運行中修改配置如何刷新

在這一節中,我們來講解下在項目運行過程中,配置發生修改之后推送給了客戶端,那么這個值如何去更新 Spring 當中的值呢?

原理就是把這些配置都存儲起來,當配置發生變化的時候進行修改就可以。Apollo 中定義了一個 SpringValueProcessor 類,用來處理 Spring 中值的修改。下面只貼出一部分代碼,如下所示。

@Componentpublic class SpringValueProcessor implements BeanPostProcessor, BeanFactoryAware {private PlaceholderHelper placeholderHelper = new PlaceholderHelper();private BeanFactory beanFactory;public SpringValueRegistry springValueRegistry = new SpringValueRegistry();@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Class clazz = bean.getClass();for (Field field : findAllField(clazz)) {
            processField(bean, beanName, field);
        }return bean;
    }private void processField(Object bean, String beanName, Field field) {// register @Value on fieldValue value = field.getAnnotation(Value.class);if (value == null) {return;
        }
        Set<String> keys = placeholderHelper.extractPlaceholderKeys(value.value());if (keys.isEmpty()) {return;
        }for (String key : keys) {
            SpringValue springValue = new SpringValue(key, value.value(), bean, beanName, field, false);
            springValueRegistry.register(beanFactory, key, springValue);
        }
    }
}

通過實現 BeanPostProcessor 來處理每個 bean 中的值,然后將這個配置信息封裝成一個 SpringValue 存儲到 springValueRegistry 中。

SpringValue 代碼如下所示。

public class SpringValue {private MethodParameter methodParameter;private Field field;private Object bean;private String beanName;private String key;private String placeholder;private Class<?> targetType;private Type genericType;private boolean isJson;
}

SpringValueRegistry 就是利用 Map 來存儲,代碼如下所示。

public class SpringValueRegistry {private final Map<BeanFactory, Multimap<String, SpringValue>> registry = Maps.newConcurrentMap();private final Object LOCK = new Object();public void register(BeanFactory beanFactory, String key, SpringValue springValue) {if (!registry.containsKey(beanFactory)) {
            synchronized (LOCK) {if (!registry.containsKey(beanFactory)) {
                    registry.put(beanFactory, LinkedListMultimap.<String, SpringValue>create());
                }
            }
        }
        registry.get(beanFactory).put(key, springValue);
    }public Collection<SpringValue> get(BeanFactory beanFactory, String key) {
        Multimap<String, SpringValue> beanFactorySpringValues = registry.get(beanFactory);if (beanFactorySpringValues == null) {return null;
        }return beanFactorySpringValues.get(key);
    }
}

寫個接口用于模擬配置修改,具體代碼如下所示。

@RestControllerpublic class ConfigController {@Autowiredprivate SpringValueProcessor springValueProcessor;@Autowiredprivate ConfigurableBeanFactory beanFactory;@GetMapping("/update")public String update(String value) {
        Collection<SpringValue> targetValues = springValueProcessor.springValueRegistry.get(beanFactory,"bianchengName");for (SpringValue val : targetValues) {try {val.update(value);
            } catch (IllegalAccessException | InvocationTargetException e) {
                e.printStackTrace();
            }
        }return name;
    }
}

當我們調用 /update接口后,在前面的 /get 接口可以看到猿天地的值改成了你傳入的那個值,這就是動態修改。

通過閱讀 Apollo 的源碼,可以學到很多的東西。

  • Apollo 用的是 Mysql。Apollo 是多個庫,通過庫來區分不同環境下的配置。

  • Apollo 基于 Http 長連接實現推送,還有容災的定時拉取邏輯。

  • Apollo 也有自己的原生獲取值的對象,同時還集成到了 Spring 中,可以兼容老項目的使用方式。

  • Apollo 中可以直接使用注解來進行監聽,非常方便。

上述就是小編為大家分享的 Apollo客戶端設計原理的源碼解析是怎樣的了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

大安市| 南溪县| 柳河县| 安西县| 双柏县| 牟定县| 常州市| 陇西县| 浮梁县| 临潭县| 桂阳县| 塘沽区| 黑山县| 开封县| 临泽县| 寻甸| 政和县| 米易县| 山东| 二连浩特市| 凌源市| 阳谷县| 安庆市| 广宁县| 洞头县| 无为县| 鹿泉市| 英吉沙县| 抚顺县| 定州市| 灵宝市| 华容县| 许昌县| 壶关县| 太谷县| 丰宁| 溧水县| 宣汉县| 菏泽市| 桐梓县| 河东区|