序
在上一篇文章中,我们已经知道了 soul-bootstrap 会主动跟 soul-admin 建立连接,而且 soul-admin 也会推送更新的接口数据给连接上 web socket 服务的客户端(也就是 soul 网关)。
但是我们还并不知道数据是怎样被 soul-bootstrap 处理的,所以留下了 2 个问题:
- soul-bootstrap 是怎么处理接口数据的
- soul-bootstrap 如果挂掉了重启,接口数据会不会消失?
soul-bootstrap 建立 socket 连接的数据请求
我们先从第二个问题出发,因为挂掉重启和第一次启动的处理过程应该是一致的。
因为第一次启动是肯定不会有接口数据的,但是我们之前的测试的时候发现只要 soul-bootstrap 启动了就可以代理接口提供服务了。
也就是说,soul-bootstrap 启动并建立了 socket 连接之后,就应该会从 soul-admin 那里去取一次数据。
那么切入点也就找到了,当 socket 连接成功之后,肯定有我们想要的答案。
public final class SoulWebsocketClient extends WebSocketClient {
private volatile boolean alreadySync = Boolean.FALSE;
@Override
public void onOpen(final ServerHandshake serverHandshake) {
if (!alreadySync) {
send(DataEventTypeEnum.MYSELF.name());
alreadySync = true;
}
}
}
看到上面的代码了么,简单解读一下:
- 当 socket 成功建立连接就会执行这个 onOpen 方法
- 然后给 soul-admin 去发送一条 MYSELF 消息
- 然后更新 alreadySync 状态
接下来就需要回头去看 soul-admin 接收到 MYSELF 消息之后会做出什么样的响应了。
@ServerEndpoint("/websocket")
public class WebsocketCollector {
@OnMessage
public void onMessage(final String message, final Session session) {
if (message.equals(DataEventTypeEnum.MYSELF.name())) {
WebsocketCollector.session = session;
SpringBeanUtils.getInstance().getBean(SyncDataService.class).syncAll(DataEventTypeEnum.MYSELF);
}
}
}
嗯,再来简答解读下:
- 判断是否是 MYSELF 消息
- _调用 _SyncDataService 类的 syncAll 方法,这个方法的名字一看就是同步所有数据
那么同步哪些数据呢?结合之前的文章,肯定会有「选择器」和「规则」的数据吧,毕竟这两块就是接口相关的数据。
让我们继续看下去:
@Service("syncDataService")
public class SyncDataServiceImpl implements SyncDataService {
@Override
public boolean syncAll(final DataEventTypeEnum type) {
appAuthService.syncData();
List<PluginData> pluginDataList = pluginService.listAll();
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, type, pluginDataList));
List<SelectorData> selectorDataList = selectorService.listAll();
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, type, selectorDataList));
List<RuleData> ruleDataList = ruleService.listAll();
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.RULE, type, ruleDataList));
metaDataService.syncData();
return true;
}
}
嗯,果然不出我们所料,「选择器」和「规则」的数据都在这里,但是还多了一个「插件」的数据,不清楚「插件」的概念也不用慌,继续看下去。
这里是获取到了 3 种数据,然后通通用事件发布模式给发布了出去,老规矩去找到监听的方法。
public class DataChangedEventDispatcher implements ApplicationListener<DataChangedEvent>, InitializingBean {
@Override
public void onApplicationEvent(final DataChangedEvent event) {
for (DataChangedListener listener : listeners) {
switch (event.getGroupKey()) {
// 省略其他的 case...
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;
default:
throw new IllegalStateException("Unexpected value: " + event.getGroupKey());
}
}
}
}
这几个事件的监听处理都是同一个地方,根据数据的类型不同再去执行不同的方法,我们继续看下去。
public class WebsocketDataChangedListener implements DataChangedListener {
@Override
public void onPluginChanged(final List<PluginData> pluginDataList, final DataEventTypeEnum eventType) {
WebsocketData<PluginData> websocketData =
new WebsocketData<>(ConfigGroupEnum.PLUGIN.name(), eventType.name(), pluginDataList);
WebsocketCollector.send(GsonUtils.getInstance().toJson(websocketData), eventType);
}
@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);
}
@Override
public void onRuleChanged(final List<RuleData> ruleDataList, final DataEventTypeEnum eventType) {
WebsocketData<RuleData> configData =
new WebsocketData<>(ConfigGroupEnum.RULE.name(), eventType.name(), ruleDataList);
WebsocketCollector.send(GsonUtils.getInstance().toJson(configData), eventType);
}
}
看到了这里应该就明白了,分别把需要同步的「插件」、「选择器」和「规则」数据用 socket 再发送出去。那么这时候的接收方是谁呢?没错,又回到了 soul-bootstrap,不过这一次带上了 3 种数据。
我们依葫芦画瓢,继续找到 soul-bootstrap 处理的地方,也就是上面 SoulWebsocketClient 类里面的 onMessage 方法(不仅回到了 soul-bootstrap,还回到了同一个类里面):
public final class SoulWebsocketClient extends WebSocketClient {
@Override
public void onMessage(final String result) {
handleResult(result);
}
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);
}
}
简单解读下这段代码:
- 从参数里解析出数据、事件类型等需要的数据
- 交给 websocketDataHandler 去处理
又到了我们一步一猜的时候了,猜测会怎么去处理数据?因为数据的种类不一样,肯定需要分开单独去处理吧。
这里一气化三清,数据分别交给了 PluginDataHandler、SelectorDataHandler、RuleDataHandler 刷新数据(其实就是删除数据)之后,又会走到同一个方法里面去处理。
public class CommonPluginDataSubscriber implements PluginDataSubscriber {
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));
}
} else if (data instanceof SelectorData) {
SelectorData selectorData = (SelectorData) data;
if (dataType == DataEventTypeEnum.UPDATE) {
BaseDataCache.getInstance().cacheSelectData(selectorData);
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));
}
} else if (data instanceof RuleData) {
RuleData ruleData = (RuleData) data;
if (dataType == DataEventTypeEnum.UPDATE) {
BaseDataCache.getInstance().cacheRuleData(ruleData);
Optional.ofNullable(handlerMap.get(ruleData.getPluginName())).ifPresent(handler -> handler.handlerRule(ruleData));
} else if (dataType == DataEventTypeEnum.DELETE) {
BaseDataCache.getInstance().removeRuleData(ruleData);
Optional.ofNullable(handlerMap.get(ruleData.getPluginName())).ifPresent(handler -> handler.removeRule(ruleData));
}
}
});
}
}
这段代码就是判断数据的类型——区分「插件」、「选择器」和「规则」,另外判断数据操作的类型——更新还是删除。
最后把这些数据写入到对应的内存里面。
总结
第一,整个代码分析的过程就是 soul-bootstrap 处理接口数据的过程;第二,因为数据存储在内存里面,soul-bootstrap 一旦挂掉数据就会消失,然而每次启动就会从 soul-admin 那里同步数据。
另外,soul-admin 还会定时同步和更新接口数据,推送给 soul-bootstrap,这个过程跟上面 soul-bootstrap 接收到数据的过程是一样的。
到此为止,我们已经从自己的服务开始,把整个 http 接口数据注册到 soul-admin,然后通过 socket 同步到了 soul-bootstrap,也就是说 soul-bootstrap 已经存在了代理我们真实的 http 服务的数据。
但是,新的问题来了—— soul-bootstrap 是怎么完成代理的?
我们下篇文章再分析。