乐趣区

关于java:Soul网关探秘http数据同步Admin通知前处理

引言

本篇开始钻研 Soul 网关 http 数据同步,将分为三篇进行剖析:

  • 《Admin 告诉前解决》
  • 《变更告诉机制》
  • 《Bootstrap 解决变更告诉》

心愿三篇完结后能对 Soul 的 http 数据同步策略有所播种。

本篇旨在探索 soul-admin 端在发动变更告诉前所做的解决。

不同数据变更的解决模式该当是统一的,故本篇以 selector 配置变更为切入点进行深刻。

一、配置变更入口

找到 SelectorController,这是 selector 配置变更的入口

其持有一个 SelectorService 援用,通过 SelectorService 实现 selector 配置变更。

二、配置变更服务

再来看看 SelectorService,实现了配置变更的具体解决。

其外部持有 5 个 mapper、1 个 eventPublisher 和 1 个 upstreamCheckService,对外提供一系列对 selector 的 crud 办法

留神 createOrUpdate 办法

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 上游服务

三、spring 事件告诉机制

此处波及 spring 的事件告诉机制,在此简要阐明:

ApplicationContext 通过 ApplicationEvent 类和 ApplicationListener 接口提供事件处理。

如果一个 bean 实现 ApplicationListener 接口在容器中, 每次一个 ApplicationEvent 被公布到 ApplicationContext 中, 这类 bean 就会收到这些告诉。

实现 Spring 事件机制次要有 4 个类:

  • ApplicationEvent:事件,每个实现类示意一类事件,可携带数据。
  • ApplicationListener:事件监听器,用于接管事件处理工夫。
  • ApplicationEventMulticaster:事件管理者,用于事件监听器的注册和事件的播送。
  • ApplicationEventPublisher:事件发布者,委托 ApplicationEventMulticaster 实现事件公布。

四、soul 实现事件告诉

上面咱们看看 Soul 是如何应用 spring 的工夫告诉机制。

事件定义

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

事件监听器

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 的注入。

五、响应数据变更事件

下面的事件监听解决用到 soul 的 DataChangedListener 接口

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

退出移动版