Soul网关-websocket同步数据原理讲解 第一章
在上一章中已经看到,针对于接口注册的网关的流程,大体的通用逻辑都是先进行业务逻辑的判断(是否重复、是否符合),在判断完后进行了一系列的修改数据库表的操作,最后通过Spring的事件发布机制,发布了一个类型为DataChangedEvent的事件。
为什么要发布事件呢?在soul的官方文档已经很明确的说明了,网关要做到高性能,要尽快的进行规定路径的寻址,如果每次都从库里取注册的地址,毫无疑问会增加网络IO的消耗,所以在soul设计的时候将所有网关请求转发所需要的东西,全部都加载到了JVM的内存中,而为了避免对地址的一些修改、删除、新增等操作,soul目前采用了websocket、zookeeper(3.0版本)、http长轮询、nacos(3.0版本)等解决方案,来实现了一个近实时的操作。
- 这个 DataChangedEvent的事件中到底包含了哪些东西呢?
// 事件的操作类型
private DataEventTypeEnum eventType;
// 配置组(我更习惯理解为各个模块)
private ConfigGroupEnum groupKey;
//还有一个 List<?> source 属性 这个属性 继承于ApplicationEvent
- 在DataEventTypeEnum中定义了DELETE、CREATE、UPDATE、REFRESH、MYSELF五种操作类型,按照字面意思除了最后一个MYSELF有些歧义外,其他都好理解(MYSELF暂时先放过,后期在找)。
- 在ConfigGroupEnum中定义了APP_AUTH、PLUGIN、RULE、SELECTOR、META_DATA五种类型,而在我们的基于SpringMvc的接口注册中,最后只发布了RULE和SELECTOR的事件,从这里也能大体看出soul把用户的权限、插件的使用、接口规则·、选择器、元数据都放到了内存中(暂时大部分都不知道干啥用的)。
接下来进入正题让我们来看一下针对于websocket,soul是如何实现近实时更新的。
- 找到订阅事件的地址,最后找到了DataChangedEventDispatcher(可以全局搜索implements ApplicationListener<DataChangedEvent找到对应的类),
@Component
public class DataChangedEventDispatcher implements ApplicationListener<DataChangedEvent>, InitializingBean {
private ApplicationContext applicationContext;
private List<DataChangedListener> listeners;
public DataChangedEventDispatcher(final ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@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接口的所有实现全部加载到list中
@Override
public void afterPropertiesSet() {
Collection<DataChangedListener> listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values();
this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans));
}
}
- 在上面的代码中我们可以看到DataChangedEventDispatcher类在初始化的时候实现了InitializingBean通过Spring的上下文获取到了属于DataChangedListener这个类型的所有对象并且加载到了当前类的list中,在接收到事件后又通过模块类型选择执行不同listener的方法。那DataChangedListener对象又是干什么的呢?
public interface DataChangedListener {
//收到AppAuth时调用此方法。
default void onAppAuthChanged(List<AppAuthData> changed, DataEventTypeEnum eventType) {}
//收到插件时调用此方法。
default void onPluginChanged(List<PluginData> changed, DataEventTypeEnum eventType) {}
//收到选择器时调用此方法。
default void onSelectorChanged(List<SelectorData> changed, DataEventTypeEnum eventType) {}
//收到元数据时调用此方法。
default void onMetaDataChanged(List<MetaData> changed, DataEventTypeEnum eventType) {}
//收到Rule时调用此方法。
default void onRuleChanged(List<RuleData> changed, DataEventTypeEnum eventType) {}
}
-
通过上述的接口方法,大概能明确DataChangedListener接口提供了ConfigGroupEnum下类型的数据处理,而它有哪些实现类呢?

-
可以看到一共提供了5种实现,大概扫了一下结构,其实实际只有4种,http长轮询、nacos、zookeeper、websocket,而多余的那个抽象实现其实是和http长轮询联用的,暂时忽略,后期测试http长轮询的同步再逐步跟进。
-
由于本篇文章 主要针对于websocket同步数据,所以主要看对应的websocket实现类的逻辑,大体如下
public class WebsocketDataChangedListener implements DataChangedListener {
//省略其余实现 其余实现的代码逻辑 与这个一致
@Override
public void onSelectorChanged(final List<SelectorData> selectorDataList, final DataEventTypeEnum eventType) {
System.out.println("进入 websocket selector chanage");
WebsocketData<SelectorData> websocketData =
new WebsocketData<>(ConfigGroupEnum.SELECTOR.name(), eventType.name(), selectorDataList);
WebsocketCollector.send(GsonUtils.getInstance().toJson(websocketData), eventType);
}
}
- 可以看到 在websocket的监听实现类里,在接收到传递的数据后,又进行封装了一次自己对应的DTO,然后发送给了对应的websocket
本篇文章大概先到期为止,后续的流程大概跟了一下,有些许的长,下篇文章接着讲