本文主要目标:
本篇是对admin后台操作数据以后,同步到网关的流程介绍
打开浏览器的检查选项查看请求,然后访问admin控制台的插件列表,我们可以看到选择器的相关请求都包含 /selector ,到源码项目中全局搜索可以发现,具体的实现为SelectorController.java
类作为入口。在此查看该类所在的目录,我们可以发现admin项目的所有controller的,这里作为crud的入口。
这里主要简单介绍下和 网关服务soul-bootstrap 相关的几个配置项
MetaDataController、PluginController、SelectorController、RuleController、SoulClientController等进行更新和创建之后,都会发送一个数据变更事件DataChangedEvent
事件。例如:
public class DataChangedEvent extends ApplicationEvent {
// 指定数据变更类型:是插件、选择器、规则还是其他类型数据变更
private ConfigGroupEnum groupKey;
// 指定数据变更的动作:新增还是更新还是删除
private DataEventTypeEnum eventType;
// 父类EventObject中,用来传输具体的变更数据
protected transient Object source;
}
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.RULE, DataEventTypeEnum.UPDATE,
Collections.singletonList(RuleDO.transFrom(ruleDO, pluginDO.getName(), conditionDataList))));
全局搜索 ApplicationListener
我们可以发现,具体的实现类为DataChangedEventDispatcher
public class DataChangedEventDispatcher implements ApplicationListener<DataChangedEvent>, InitializingBean {
private List<DataChangedListener> listeners;
@Override
@SuppressWarnings("unchecked")
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());
}
}
}
}
其中DataChangedListener
的实现类有四种,默认情况下是使用的WebsocketDataChangedListener进行数据同步
soul
sync:
websocket:
enabled: true
# zookeeper:
# url: localhost:2181
# sessionTimeout: 5000
# connectionTimeout: 2000
# http:
# enabled: true
# nacos:
# url: localhost:8848
# namespace: 1c10d748-af86-43b9-8265-75f487d20c6c
# acm:
# enabled: false
# endpoint: acm.aliyun.com
# namespace:
# accessKey:
# secretKey:
下面对于默认的Websocket为例,简单介绍下
WebsocketData
的内部封装数据,然后调用 WebsocketCollector
发送更新消息SESSION_SET
中的 Websocket 的 session,然后send消息,同步到 soul-bootstraplistener.onSelectorChanged((List<SelectorData>) event.getSource(), event.getEventType());
@Override
public void onSelectorChanged(final List<SelectorData> selectorDataList, final DataEventTypeEnum eventType) {
WebsocketData<SelectorData> websocketData =
new WebsocketData<>(ConfigGroupEnum.SELECTOR.name(), eventType.name(), selectorDataList);
WebsocketCollector.send(GsonUtils.getInstance().toJson(websocketData), eventType);
}
public class WebsocketCollector {
if (StringUtils.isNotBlank(message)) {
if (DataEventTypeEnum.MYSELF == type) {
try {
Session session = (Session) ThreadLocalUtil.get(SESSION_KEY);
if (session != null) {
session.getBasicRemote().sendText(message);
}
} catch (IOException e) {
log.error("websocket send result is exception: ", e);
}
return;
}
for (Session session : SESSION_SET) {
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
log.error("websocket send result is exception: ", e);
}
}
}
}
WebsocketSyncDataService
来处理数据同步的服务
executor.scheduleAtFixedRate(() -> {
try {
if (client.isClosed()) {
boolean reconnectSuccess = client.reconnectBlocking();
if (reconnectSuccess) {
log.info("websocket reconnect is successful.....");
} else {
log.error("websocket reconnection is error.....");
}
}
} catch (InterruptedException e) {
log.error("websocket connect is error :{}", e.getMessage());
}
}, 10, 30, TimeUnit.SECONDS);
}
soul-bootstrap端的 其中 client 类型为 SoulWebsocketClient
,实际上收到event时就是进行反序列化,然后让websocketDataHandler进行处理
DataHandler
@Override
public void onMessage(final String result) {
handleResult(result);
}
@SuppressWarnings("ALL")
private void handleResult(final String result) {
WebsocketData websocketData = GsonUtils.getInstance().fromJson(result, WebsocketData.class);
ConfigGroupEnum groupEnum = ConfigGroupEnum.acquireByName(websocketData.getGroupType());
String eventType = websocketData.getEventType();
String json = GsonUtils.getInstance().toJson(websocketData.getData());
websocketDataHandler.executor(groupEnum, json, eventType);
}
public class WebsocketDataHandler {
private static final EnumMap<ConfigGroupEnum, DataHandler> ENUM_MAP = new EnumMap<>(ConfigGroupEnum.class);
public WebsocketDataHandler(final PluginDataSubscriber pluginDataSubscriber,
final List<MetaDataSubscriber> metaDataSubscribers,
final List<AuthDataSubscriber> authDataSubscribers) {
ENUM_MAP.put(ConfigGroupEnum.PLUGIN, new PluginDataHandler(pluginDataSubscriber));
ENUM_MAP.put(ConfigGroupEnum.SELECTOR, new SelectorDataHandler(pluginDataSubscriber));
ENUM_MAP.put(ConfigGroupEnum.RULE, new RuleDataHandler(pluginDataSubscriber));
ENUM_MAP.put(ConfigGroupEnum.APP_AUTH, new AuthDataHandler(authDataSubscribers));
ENUM_MAP.put(ConfigGroupEnum.META_DATA, new MetaDataHandler(metaDataSubscribers));
}
public void executor(final ConfigGroupEnum type, final String json, final String eventType) {
ENUM_MAP.get(type).handle(json, eventType);
}
}
Datahandler 这里使用了模板方法模式,抽象类AbstractDataHandler定义了根据事件类型处理的框架,然后 PluginDataHandler、SelectorDataHandler、RuleDataHandler、AuthDataHandler、MetaDataHandler 只需要是实现 doRefresh、doUpdate、doDelette等方法即可
public abstract class AbstractDataHandler<T> implements DataHandler {
protected abstract List<T> convert(String json);
protected abstract void doRefresh(List<T> dataList);
protected abstract void doUpdate(List<T> dataList);
protected abstract void doDelete(List<T> dataList);
@Override
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;
}
}
}
}
以SelectorDataHandler的都doUpdate为例继续分析,进入到SelectorDataHandler
,可以看到该类主要就是对 pluginDataSubscriber
进行的回调操作
@Override
protected void doUpdate(final List<SelectorData> dataList) {
dataList.forEach(pluginDataSubscriber::onSelectorSubscribe);
}
这里进入到 PluginDataSubscriber,查看源码可以看到实际上是在CommonPluginDataSubscriber
中处理的
// CommonPluginDataSubscriber.java
private final Map<String, PluginDataHandler> handlerMap;
private <T> void subscribeDataHandler(final T classData, final DataEventTypeEnum dataType) {
Optional.ofNullable(classData).ifPresent(data -> {
// 先忽略别的逻辑,只看选择器数据的操作
if (data instanceof SelectorData) {
SelectorData selectorData = (SelectorData) data;
if (dataType == DataEventTypeEnum.UPDATE) {
// 修改了缓存 BaseDataCache
BaseDataCache.getInstance().cacheSelectData(selectorData);
// 然后根据插件名称,找到插件处理器 PluginDataHandler 处理选择器的更新数据
Optional.ofNullable(handlerMap.get(selectorData.getPluginName())).ifPresent(handler -> handler.handlerSelector(selectorData));
} else if (dataType == DataEventTypeEnum.DELETE) {
BaseDataCache.getInstance().removeSelectData(selectorData);
Optional.ofNullable(handlerMap.get(selectorData.getPluginName())).ifPresent(handler -> handler.removeSelector(selectorData));
}
}
});
}
本文简单介绍了以下几点: