一、目标
数据同步是Soul网关非常重要的一块内容,之前我们一起了解了Soul网关四种数据同步方式的运作原理,本节我们就从整体的层面来看一下Soul数据同步的思想,算是对之前学习内容的一个总结。
二、内容
2.1 背景
Soul网关目前支持四种数据同步方式:webSocket、zookeeper、http长轮询、nacos。Soul网关支持插件化配置、目前支持的插件有13个。每个插件要处理的数据类型包括:AuthData、PluginData、SelectorData、RuleData、MetaData。每种数据类型又包括三种操作:update、delete、refresh。这样一组合下来,它的组合方式是非常多的,13 x 4 x 5 x 3,算下来有780种之多。如果不能厘清他们之间的关系,那代码的结构就会看起来非常乱,而且同时还要考虑可扩展的问题。
我们在分析源码的过程中,可以看到Soul网关,这一块还是梳理的非常清楚的,这其中的设计思想和模式是非常值得我们学习的。接下来我们就从整体的角度来一起看一下Soul网关数据同步模块的设计。
2.2 接口设计
Soul在数据同步这一块,大体上有四类接口:
-
ApplicationEventPublisher -> EventPublisher 发出配置变更通知;
-
DataChangedEventDispatcher -> 根据配置的同步策略(http、weboscket、zookeeper),将配置发送给对应的事件处理器;
-
DataHandler接口 -> 事件处理器;
-
Subscriber接口 -> 事件订阅者接收数据;
2.2.1 ApplicationEventPublisher
这个接口是Spring里面的接口,是封装事件发布功能的接口,Soul网关通过接口里面的publishEvent方法来发布事件;
2.2.3 DataChangedEventDispatcher
事件转发器,将更改的事件转发到每个ConfigEventListener。DataChangedEventDispatcher 实现了Spring里面的ApplicationListener接口。数据通过DataChangedEvent类进行传递;
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()); } } }
2.2.4 DataHandler
这个是数据处理的接口,接口里面定义一个handle方法接口,参数是json格式的数据和eventType。AbstractDataHandler是一个抽象方法,实现了DataHandler接口,重写了接口里面的handle方法,同时他也定义了很多很多自己的方法。这里用到了模板设计模式,学习设计模式的同学可以参考一下这里的用法;
2.2.5 Subscriber接口
事件订阅者接收数据。Subscriber接口有三类:
-
PluginDataSubscriber接口,主要处理插件相关的不同类型的数据;
CommonPluginDataSubscriber类实现了PluginDataSubscriber接口,重写了接口里面的方法,自定义了subscribeDataHandler方法,这个方法是用来根据事件类型来调用不同类型的handler方法。
private <T> void subscribeDataHandler(final T classData, final DataEventTypeEnum dataType) { Optional.ofNullable(classData).ifPresent(data -> { if (data instanceof PluginData) { PluginData pluginData = (PluginData) data; if (dataType == DataEventTypeEnum.UPDATE) { BaseDataCache.getInstance().cachePluginData(pluginData); Optional.ofNullable(handlerMap.get(pluginData.getName())).ifPresent(handler -> handler.handlerPlugin(pluginData)); } else if (dataType == DataEventTypeEnum.DELETE) { BaseDataCache.getInstance().removePluginData(pluginData); Optional.ofNullable(handlerMap.get(pluginData.getName())).ifPresent(handler -> handler.removePlugin(pluginData)); } } //省略 ... } } }); }
-
MetaDataSubscriber接口,主要处理原数据;
-
AuthDataSubscriber接口,主要处理用户及权限相关数据;
2.3 各数据同步中心
经过DataHandler和Subscribe接口的处理,数据到了各个数据同步中心进行处理
以soul-sync-data-websocket同步中心为例,看一下数据到了数据同步中心的处理
数据到达之后,调用onMessage方法,onMessage调用handleResult方法,找到websocketDataHandler,websocketDataHandler调用具体的处理类AbstractDataHandler,再根据DataEventType选择对应的类进行处理。
public void handle(final String json, final String eventType) {
List<T> dataList = convert(json);
if (CollectionUtils.isNotEmpty(dataList)) {
DataEventTypeEnum eventTypeEnum = DataEventTypeEnum.acquireByName(eventType);
switch (eventTypeEnum) {
case REFRESH:
case MYSELF:
doRefresh(dataList);
break;
case UPDATE:
case CREATE:
doUpdate(dataList);
break;
case DELETE:
doDelete(dataList);
break;
default:
break;
}
}
}
三、总结
今天从整体上对Soul网关的数据同步进行了粗略的回顾,看懂总体再结合具体的实例去看一遍,整个结构和思路会更加清楚。这其中的设计思路、设计模式及代码的实现方式都值得我们去好好进一步深究。