soul-admin 可以使用websocket与soul-bootstrap进行通信
1.配置
引入 jar 包

soul-admin中配置 websocket

soul-bootstrap中配置websokcet的地址

2. soul-admin 配置加载
soul-admin 端会加载一个 WebsocketDataChangedListener , 该类中使用 WebsocketCollector 连接 websocket 来发送数据给 soul-bootstrap


admin端使用spring的事件注册机制在更改配置时发送事件,将修改的配置发送出去
每个service都会注入 eventPublisher 用来发布配置修改的事件 DataChangedEvent。


DataChangedEventDispatcher 用来接收 DataChangedEvent 事件,该类会加载容器内配置的 DataChangedListener

public class DataChangedEventDispatcher implements ApplicationListener<DataChangedEvent>, InitializingBean {
private ApplicationContext applicationContext;
private List<DataChangedListener> listeners;
public DataChangedEventDispatcher(final ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
// 接收 DataChangedEvent事件后使用循环DataChangedListener处理事件
@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));
}
}
因为我们使用的是 websocket, 我们以 RULE 为例,当接收到DataChangedEvent事件时就会调用 WebsocketDataChangedListener.onRuleChanged()
@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);
}
在这里使用 WebsocketCollector发送数据
3.soul-boostrap配置加载
在项目启动时会在 WebsocketSyncDataConfiguration 中注册
websocketConfig websocket 配置
pluginSubscriber 用于广播plugin的改动
metaSubscribers 用于广播 metaData的改动
authSubscribers 用于改动 auth的改动

看下 WebsocketSyncDataService 构造方法
public WebsocketSyncDataService(final WebsocketConfig websocketConfig,
final PluginDataSubscriber pluginDataSubscriber,
final List<MetaDataSubscriber> metaDataSubscribers,
final List<AuthDataSubscriber> authDataSubscribers) {
// 配置的 soul-admin url,可能为多个
String[] urls = StringUtils.split(websocketConfig.getUrls(), ",");
// 创建一个定时线程池,用于定时重连 soul-admin
executor = new ScheduledThreadPoolExecutor(urls.length, SoulThreadFactory.create("websocket-connect", true));
for (String url : urls) {
try {
// 为 每一个 soul-admin url 创建一个 SoulWebsocketClient
clients.add(new SoulWebsocketClient(new URI(url), Objects.requireNonNull(pluginDataSubscriber), metaDataSubscribers, authDataSubscribers));
} catch (URISyntaxException e) {
log.error("websocket url({}) is error", url, e);
}
}
try {
// 遍历创建的 WebSocketClient 判断是否与 websocket 客户端相连
for (WebSocketClient client : clients) {
boolean success = client.connectBlocking(3000, TimeUnit.MILLISECONDS);
if (success) {
log.info("websocket connection is successful.....");
} else {
log.error("websocket connection is error.....");
}
// 线程池 定时连接客户端,避免掉线
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);
}
/* client.setProxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxyaddress", 80)));*/
} catch (InterruptedException e) {
log.info("websocket connection...exception....", e);
}
}
在
SoulWebsocketClient 中会初始化 WebsocketDataHandler , 内部缓存 5中数据的DataHandler用来更新客户端修改的数据。

3.使用websocket同步数据
当soul-admin修改数据时就会调用 SoulWebsocketClient.onMessage() 方法


在 WebsocketDataHandler 中根据启动加载缓存的具体 handler 处理数据。我们这里修改的是 rule 类型,所以会调用 RuleDataHandler.handle() 方法

因为是 ruleDate的update 最后调用 CommonPluginDataSubscriber.onRuleSubscribe() 来更新 ruleData

执行 subscribeDataHandler 更新缓存中的 ruleData
private <T> void subscribeDataHandler(final T classData, final DataEventTypeEnum dataType) {
Optional.ofNullable(classData).ifPresent(data -> {
if (data instanceof PluginData) {
// 省略
} else if (data instanceof SelectorData) {
// 省略
} else if (data instanceof RuleData) {
RuleData ruleData = (RuleData) data;
if (dataType == DataEventTypeEnum.UPDATE) {
// 更新本地缓存的 ruleData
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-admin 通过websocket 更新ruleData的流程就结束啦。
本文详细介绍了如何通过soul-admin利用WebSocket与soul-bootstrap进行配置数据交换,包括配置设置、事件触发与处理,以及在soul-bootstrap中的接收与同步机制。重点展示了规则数据的实时更新流程。
701

被折叠的 条评论
为什么被折叠?



