【Soul網關探祕】http數據同步-Admin通知前處理

引言

本篇開始研究 Soul 網關 http 數據同步,將分爲三篇進行分析:java

  • 《Admin通知前處理》
  • 《變動通知機制》
  • 《Bootstrap處理變動通知》

但願三篇完結後能對 Soul 的 http 數據同步策略有所收穫。spring

本篇旨在探究 soul-admin 端在發起變動通知前所作的處理。json

不一樣數據變動的處理模式應當是一致的,故本篇以 selector 配置變動爲切入點進行深刻。緩存

1、配置變動入口

找到 SelectorController,這是 selector 配置變動的入口app

image-20210129065312321

其持有一個 SelectorService 引用,經過 SelectorService 實現 selector 配置變動。ide

2、配置變動服務

再來看看 SelectorService,實現了配置變動的具體處理。微服務

image-20210129065816692

其內部持有5個 mapper、1個 eventPublisher和1個 upstreamCheckService,對外提供一系列對 selector 的crud方法性能

注意 createOrUpdate 方法ui

public int createOrUpdate(final SelectorDTO selectorDTO) {
    int selectorCount;
    SelectorDO selectorDO = SelectorDO.buildSelectorDO(selectorDTO);
    List<SelectorConditionDTO> selectorConditionDTOs = selectorDTO.getSelectorConditions();
    // 數據落庫
    if (StringUtils.isEmpty(selectorDTO.getId())) {
        selectorCount = selectorMapper.insertSelective(selectorDO);
        selectorConditionDTOs.forEach(selectorConditionDTO -> {
            selectorConditionDTO.setSelectorId(selectorDO.getId());
            selectorConditionMapper.insertSelective(SelectorConditionDO.buildSelectorConditionDO(selectorConditionDTO));
        });
    } else {
        selectorCount = selectorMapper.updateSelective(selectorDO);
        //delete rule condition then add
        selectorConditionMapper.deleteByQuery(new SelectorConditionQuery(selectorDO.getId()));
        selectorConditionDTOs.forEach(selectorConditionDTO -> {
            selectorConditionDTO.setSelectorId(selectorDO.getId());
            SelectorConditionDO selectorConditionDO = SelectorConditionDO.buildSelectorConditionDO(selectorConditionDTO);
            selectorConditionMapper.insertSelective(selectorConditionDO);
        });
    }
    // 發佈 spring 事件
    publishEvent(selectorDO, selectorConditionDTOs);
    // 更新 divide 上游服務
    updateDivideUpstream(selectorDO);
    return selectorCount;
}

處理策略是先落庫,再發布 spring 事件,最後更新 divide 上游服務this

3、spring 事件通知機制

此處涉及 spring 的事件通知機制,在此簡要說明:

ApplicationContext經過ApplicationEvent類和ApplicationListener接口提供事件處理。

若是一個bean實現ApplicationListener接口在容器中,每次一個ApplicationEvent被髮布到ApplicationContext中,這類bean就會收到這些通知。

實現Spring事件機制主要有4個類:

  • ApplicationEvent:事件,每一個實現類表示一類事件,可攜帶數據。
  • ApplicationListener:事件監聽器,用於接收事件處理時間。
  • ApplicationEventMulticaster:事件管理者,用於事件監聽器的註冊和事件的廣播。
  • ApplicationEventPublisher:事件發佈者,委託ApplicationEventMulticaster完成事件發佈。

4、soul 實現事件通知

下面咱們看看 Soul 是如何使用 spring 的時間通知機制。

事件定義

image-20210129072813542

DataChangedEvent 繼承 ApplicationEvent,提供了 DataChangedEvent(groupKey, type, source) 事件構造方法

事件監聽器

image-20210129072918112

DataChangedEventDispatcher 實現了 ApplicationListener接口,藉助 onApplicationEvent 方法監聽事件

public void onApplicationEvent(final DataChangedEvent event) {
    for (DataChangedListener listener : listeners) {
        switch (event.getGroupKey()) {
            case APP_AUTH:
                listener.onAppAuthChanged((List<AppAuthData>) event.getSource(), event.getEventType());
                break;
            case PLUGIN:
                listener.onPluginChanged((List<PluginData>) event.getSource(), event.getEventType());
                break;
            case RULE:
                listener.onRuleChanged((List<RuleData>) event.getSource(), event.getEventType());
                break;
            case SELECTOR:
                listener.onSelectorChanged((List<SelectorData>) event.getSource(), event.getEventType());
                break;
            case META_DATA:
                listener.onMetaDataChanged((List<MetaData>) event.getSource(), event.getEventType());
                break;
            default:
                throw new IllegalStateException("Unexpected value: " + event.getGroupKey());
        }
    }
}

該方法內按事件類型分別處理,DataChangedEventDispatcher 同時實現了 InitializingBean 接口,在初始化後完成 listeners 的注入。

5、響應數據變動事件

上面的事件監聽處理用到 soul 的 DataChangedListener 接口

image-20210129073522328

DataChangedListener 實現了不一樣類型事件的事件響應方法用於響應 DataChangedEvent 事件。

1)AbstractDataChangedListener 的 onSelectorChanged 實現:

public void onSelectorChanged(final List<SelectorData> changed, final DataEventTypeEnum eventType) {
    if (CollectionUtils.isEmpty(changed)) {
        return;
    }
    // 更新 selector 緩存
    this.updateSelectorCache();
    // selector 變動後處理,實現具體的變動通知
    this.afterSelectorChanged(changed, eventType);
}

能夠看到 selector 變動處理是先更緩存後發通知。

2)AbstractDataChangedListener 的 updateSelectorCache 實現:

protected void updateSelectorCache() {
    this.updateCache(ConfigGroupEnum.SELECTOR, selectorService.listAll());
}

3)AbstractDataChangedListener 的 updateCache 實現:

protected <T> void updateCache(final ConfigGroupEnum group, final List<T> data) {
    String json = GsonUtils.getInstance().toJson(data);
    ConfigDataCache newVal = new ConfigDataCache(group.name(), json, Md5Utils.md5(json), System.currentTimeMillis());
    ConfigDataCache oldVal = CACHE.put(newVal.getGroup(), newVal);
    log.info("update config cache[{}], old: {}, updated: {}", group, oldVal, newVal);
}

能夠看到最終是建立對應的 ConfigDataCache 存入 CACHE。

總結

本篇梳理了 soul-admin 在真正發出數據變動通知前的處理脈絡,其策略是:先寫庫後更緩存,最後發出數據變動通知。

先寫庫保證數據不丟,另外在集羣部署時,其餘 soul-admin 節點也可經過瀏覽頁面時查庫保證數據一致。

意外學到 spring 的事件通知機制,soul 中的設計果然小巧精妙。

下篇,將探究 http 同步策略的變動通知機制,期待驚喜。

我的知識庫

高性能微服務API網關-Soul

相關文章
相關標籤/搜索