这一篇主要介绍soul-admin的配置/注册数据如何通过websocket同步到soul-bootstrap
一、概述
- websocket和zookeeper同步方式,提供增量更新的方式,生产实践中也基本是这两种方式
- http长连接和nacos的同步方式,都是提供全量更新的方式,效率上存在一定的影响。
按上一篇的数据流分析,soul-admin会将配置信息同步至soul getway,如图所示,这一篇主要是讲解websocket同步方式。
二、相关配置说明
websocket的数据同步方式也是soul网关默认的同步方式,相应的配置有如下
- soul-admin application-local配置文件
- soul-admin/bootstrap pom.xml中引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
- soul-bootstrap的配置文件application-local.yml中配置soul-admin的ws地址
注意路径中的websocket这个可以理解为ws的topic,与之对应的是soul-admin中的
三、soul-admin源码解析
昨天客户端注册至soul-admin之后,在入库之后调用了publishEvent()方法,以这个为数据传输起点分析
其中publishEvent已经是Spring框架中ApplicationEventPublisher的方法了,搜一下
- ApplicationEventPublisher
notify all matching listeners registered with this application of an application event.Events may be framework events
(such as ContextRefreshedEvent) or application-specific events.
通知所有注册至这个应用的监听者,通知事件可能为框架事件或者是特定应用程序的事件。
与之对应的类为ApplicationListener,肯定有一个地方实现了这个接口
- ApplicationListener
ApplicationListener接口是由 Spring 提供的事件订阅者必须实现的接口,我们一般把该 Service 关心的事件类型作为泛型传入。处理事件,通过 event.getSource() 即可拿到事件的具体内容
publish和listener中间这里传递的是DataChangedEvent,查看DataChangedEvent的调用者猜测为DataChangedEventDispatcher,这个也的确实现了ApplicationListener接口
DataChangedEventDispatcher负责两件事情
- 得到所有实现了DataChangedListener的监听器
- 循环监听器们,进行相应的方法调用(这里可以优化,因为会存在多个同步方式)
@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的监听器
@Override
public void afterPropertiesSet() {
Collection<DataChangedListener> listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values();
this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans));
}
}
那所有的DataChangeListener是怎么注入的呢,搜索DataChangeListener的所有实现方法
发现这里是根据配置文件里的配置项实现的自动注入bean,因为我们在yml配置中开启了soul.sync.websocket.enabled,所以监听器可以初始化,而后DataChangedEventDispatcher根据listener触发相应的更新操作。
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);
}
@Override
public void onAppAuthChanged(final List<AppAuthData> appAuthDataList, final DataEventTypeEnum eventType) {
WebsocketData<AppAuthData> configData =
new WebsocketData<>(ConfigGroupEnum.APP_AUTH.name(), eventType.name(), appAuthDataList);
WebsocketCollector.send(GsonUtils.getInstance().toJson(configData), eventType);
}
@Override
public void onMetaDataChanged(final List<MetaData> metaDataList, final DataEventTypeEnum eventType) {
WebsocketData<MetaData> configData =
new WebsocketData<>(ConfigGroupEnum.META_DATA.name(), eventType.name(), metaDataList);
WebsocketCollector.send(GsonUtils.getInstance().toJson(configData), eventType);
}
}
至此soul-admin的ws流程完毕
四、soul-bootstrap处理流程
找到依赖包里的soul-sync-data-websocket
找到WebSocketClient的具体实现,看具体的处理过程,debug一下。
一步步走下去
后面就是具体的更新缓存的逻辑了,这一块设计层层进行,后面单独一篇讲。