序
在上一篇文章中,我们已经知道了 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;@Overridepublic 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 {@OnMessagepublic 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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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);}@Overridepublic 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);}@Overridepublic 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 {@Overridepublic 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 是怎么完成代理的?
我们下篇文章再分析。
