日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

服務端

1、服務端實時推送設計

新配置發布的大致流程

  • 用戶在 Portal 中進行配置的編輯和發布。
  • Portal 會調用 Admin Service 提供的接口進行發布操作。
  • Admin Service 收到請求后,發送 ReleaseMessage 給各個 Config Service,通知 Config Service 配置發生變化。
  • Config Service 收到 ReleaseMessage 后,通知對應的客戶端,基于 Http 長連接實現。

 

2. 發送 ReleaseMessage 的實現方式

ReleaseMessage 消息是通過 MySQL 實現了一個簡單的消息隊列。之所以沒有采用消息中間件,是為了讓 Apollo 在部署的時候盡量簡單,盡可能減少外部依賴

發送 ReleaseMessage 的實現方式

  • Admin Service 在配置發布后會往 ReleaseMessage 表插入一條消息記錄。
  • Config Service 會啟動一個線程定時掃描 ReleaseMessage 表,來查看是否有新的消息記錄。
  • Config Service 發現有新的消息記錄,就會通知到所有的消息監聽器。
  • 消息監聽器得到配置發布的信息后,就會通知對應的客戶端。

3. Config Service 通知客戶端的實現方式

通知采用基于 Http 長連接實現,主要分為下面幾個步驟:

  • 客戶端會發起一個 Http 請求到 Config Service 的 notifications/v2 接口。
  • notifications/v2 接口通過 Spring DeferredResult 把請求掛起,不會立即返回。
  • 如果在 60s 內沒有該客戶端關心的配置發布,那么會返回 Http 狀態碼 304 給客戶端。
  • 如果發現配置有修改,則會調用 DeferredResult 的 setResult 方法,傳入有配置變化的 namespace 信息,同時該請求會立即返回。
  • 客戶端從返回的結果中獲取到配置變化的 namespace 后,會立即請求 Config Service 獲取該 namespace 的最新配置。

客戶端

1、設計原理

Apollo 客戶端的實現原理

  • 客戶端和服務端保持了一個長連接,編譯配置的實時更新推送。
  • 定時拉取配置是客戶端本地的一個定時任務,默認為每 5 分鐘拉取一次,也可以通過在運行時指定 System Property:apollo.refreshInterval 來覆蓋,單位是分鐘,推送+定時拉取=雙保險
  • 客戶端從 Apollo 配置中心服務端獲取到應用的最新配置后,會保存在內存中。
  • 客戶端會把從服務端獲取到的配置在本地文件系統緩存一份,當服務或者網絡不可用時,可以使用本地的配置,也就是我們的本地開發模式 env=Local。

2、與 Spring 集成后通過 @Value 獲取配置原理

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

  • ConfigurableEnvironment 實現了 Environment 接口,并且包含了多個 PropertySource
  • PropertySource 可以理解為很多個 Key-Value 的屬性配置,在運行時的結構形如下圖所示。

Spring 配置結構

需要注意的是,PropertySource 之間是有優先級順序的,如果有一個 Key 在多個 property source 中都存在,那么位于前面的 property source 優先。
集成的原理就是在應用啟動階段,Apollo 從遠端獲取配置,然后組裝成 PropertySource 并插入到第一個即可,如下圖所示。

 

3、動態刷新配置的實現原理

Apollo Client 中定義了 SpringValueProcessor 類,其實現了 BeanPostProcessor 用于處理值修改。

public class SpringValueProcessor extends ApolloProcessor implements BeanFactoryPostProcessor, BeanFactoryAware {
  
  private PlaceholderHelper placeholderHelper = new PlaceholderHelper();
	private BeanFactory beanFactory;
  public SpringValueRegistry springValueRegistry = new SpringValueRegistry();

    @Override
    public 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 field
        Value 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);
      			logger.debug("Monitoring {}", 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);
    }
}


AutoUpdateConfigChangeListener 監聽到變化就會調用 onChange 更新值

public class AutoUpdateConfigChangeListener implements ConfigChangeListener{
  
    private final SpringValueRegistry springValueRegistry;
  
  	@Override
    public void onChange(ConfigChangeEvent changeEvent) {
      Set<String> keys = changeEvent.changedKeys();
      if (CollectionUtils.isEmpty(keys)) {
        return;
      }
      for (String key : keys) {
        // 1. check whether the changed key is relevant
        Collection<SpringValue> targetValues = springValueRegistry.get(beanFactory, key);
        if (targetValues == null || targetValues.isEmpty()) {
          continue;
        }

        // 2. update the value 更新值
        for (SpringValue val : targetValues) {
          updateSpringValue(val);
        }
      }
    }

}

最后

本文參考

http://c.biancheng.NET/view/5480.html

http://c.biancheng.net/view/5482.html

分享到:
標簽:Apollo
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定