Soul网关源码解析(六):websocket同步数据
Soul网关源码解析(六):websocket同步数据Websocket同步数据数据同步配置启动时的同步admin的同步处理小结参考
通过上一节,我们知道在Bootstrap启动流程中,会与soul-admin进行数据同步,那么同步的方式采用的是什么呢?在哪里配置的?过程又是如何?这些都在本小节进行解答。
Websocket同步数据
数据同步配置
首先Bootstrap与soul-admin进行数据同步的方式可以配置,这意味着方式不止一种(Soul就是这么灵活),本小节介绍通过WebSocket的方式进行数据同步,配置的地方在Bootstrap的application.yml中配置,配置方式如下:
soul : sync: websocket : urls: ws://localhost:9095/websocket
启动时的同步
在Bootstrap启动初始化过程中,会构造SyncDataService这个Bean,这个Bean的构建过程会与admin进行数据同步,并专门开启定时任务线程检查连接。怎么找到SyncDataService类的,两种方式,一种是日志定位,在bootstrap启动时,会打印“you use websocket sync soul data.......”这条日志,通过整个项目搜索,能够定位到这个函数;另一种,同步配置定位,因为上面有介绍数据同步的配置是soul.sync.websocket.urls,这里可以使用它为搜索关键字进行搜索,如下所示

下面我们来看一下这个Bean构建时如何进行同步的,首先@Bean注释的方法,如果方法有参数,会先从Spring Bean容器里获取这些对象,如果没有,则尝试找到这些Bean的配置类中进行构造,这里WebsocketConfig对象就是将上面application.yml中配置的数据同步参数封装成对象,为后面连接使用。接着看这个函数体,就干一件事,创建了一个WebsocketSyncDataService对象,那我们进入其中瞧瞧。
#WebsocketSyncDataConfiguration
@Bean
public SyncDataService websocketSyncDataService(
final ObjectProvider<WebsocketConfig> websocketConfig,
final ObjectProvider<PluginDataSubscriber> pluginSubscriber,
final ObjectProvider<List<MetaDataSubscriber>> metaSubscribers,
final ObjectProvider<List<AuthDataSubscriber>> authSubscribers) {
log.info("you use websocket sync soul data.......");
return new WebsocketSyncDataService(websocketConfig.getIfAvailable(WebsocketConfig::new),
pluginSubscriber.getIfAvailable(),
metaSubscribers.getIfAvailable(Collections::emptyList),
authSubscribers.getIfAvailable(Collections::emptyList));
}
@Bean
@ConfigurationProperties(prefix = "soul.sync.websocket")
public WebsocketConfig websocketConfig() {
return new WebsocketConfig();
}
@Data
public class WebsocketConfig {
/**
* if have more soul admin url,please config like this.
* 127.0.0.1:8888,127.0.0.1:8889
*/
private String urls;
}
WebsocketSyncDataService对象的初始化有点长,一段一段看,首先获取数据同步配置的urls数组,依据数据长度创建定时线程池,作用用于websocket连接,接着遍历这个urls数组,根据每个url创建SoulWebsocketClient,并将它添加到WebsocketSyncDataService类的list数组里,然后遍历这个list对象,依次连接,并开启一个检查任务放到定时线程池中,每3-秒检查一次连接是否被关闭,第一次时会延迟10秒。
public WebsocketSyncDataService(final WebsocketConfig websocketConfig,
final PluginDataSubscriber pluginDataSubscriber,
final List<MetaDataSubscriber> metaDataSubscribers,
final List<AuthDataSubscriber> authDataSubscribers) {
# 获取数据同步配置urls
String[] urls = StringUtils.split(websocketConfig.getUrls(), ",");
executor = new ScheduledThreadPoolExecutor(urls.length, SoulThreadFactory.create("websocket-connect", true));
for (String url : urls) {
try {
clients.add(new SoulWebsocketClient(new URI(url), Objects.requireNonNull(pluginDataSubscriber), metaDataSubscribers, authDataSubscribers));
} catch (URISyntaxException e) {
log.error("webswebsocketConfig.urlsocket url({}) is error", url, e);
}
}
try {
for (WebSocketClient client : clients) {
// 尝试连接并保持3秒
boolean success = client.connectBlocking(3000, TimeUnit.MILLISECONDS);
if (success) {
log.info("websocket connection is successful.....");
} else {
log.error("websocket connection is error.....");
}
// 每30秒检查一次,初次延迟10秒
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对象,这个对象里保存了一个EnumMap<ConfigGroupEnum, DataHandler> ENUM_MAP,这里面保存了插件,选择器,规则,认证与元数据的处理器,处理器里存着各自的订阅对象,当订阅对象发生变化时,会触发对应的事件对象处理,目前是在CommonPluginDataSubscriber类中统一处理
#SoulWebsocketClient
public SoulWebsocketClient(final URI serverUri, final PluginDataSubscriber pluginDataSubscriber,
final List<MetaDataSubscriber> metaDataSubscribers, final List<AuthDataSubscriber> authDataSubscribers) {
super(serverUri);
this.websocketDataHandler = new WebsocketDataHandler(pluginDataSubscriber, metaDataSubscribers, authDataSubscribers);
}
#WebsocketDataHandler
public WebsocketDataHandler(final PluginDataSubscriber pluginDataSubscriber,
final List<MetaDataSubscriber> metaDataSubscribers,
final List<AuthDataSubscriber> authDataSubscribers) {
ENUM_MAP.put(ConfigGroupEnum.PLUGIN, new PluginDataHandler(pluginDataSubscriber));
ENUM_MAP.put(ConfigGroupEnum.SELECTOR, new SelectorDataHandler(pluginDataSubscriber));
ENUM_MAP.put(ConfigGroupEnum.RULE, new RuleDataHandler(pluginDataSubscriber));
ENUM_MAP.put(ConfigGroupEnum.APP_AUTH, new AuthDataHandler(authDataSubscribers));
ENUM_MAP.put(ConfigGroupEnum.META_DATA, new MetaDataHandler(metaDataSubscribers));
}
admin的同步处理
上面是bootstrap端的流程,在发送同步信息到admin后,admin这边也有处理,它的处理在WebsocketCollector类里,这里说明一下WebsocketCollector类上有个@ServerEndpoint("/websocket"),这个注解的值需要和上面的Bootstrap配置的url一致。这里admin会先调用onOpen函数,连接成功,并记录这个session,然后同步消息,最后发送这个消息。如果发生异常失败了,则会调用onError函数,接着调onClose将这个session从set中移除。
#WebsocketCollector
admin#WebsocketCollector
@OnOpen
public void onOpen(final Session session) {
log.info("websocket on open successful....");
SESSION_SET.add(session);
}
@OnMessage
public void onMessage(final String message, final Session session) {
if (message.equals(DataEventTypeEnum.MYSELF.name())) {
try {
ThreadLocalUtil.put(SESSION_KEY, session);
SpringBeanUtils.getInstance().getBean(SyncDataService.class).syncAll(DataEventTypeEnum.MYSELF);
} finally {
ThreadLocalUtil.clear();
}
}
}
public static void send(final String message, final DataEventTypeEnum type) {
if (StringUtils.isNotBlank(message)) {
if (DataEventTypeEnum.MYSELF == type) {
try {
Session session = (Session) ThreadLocalUtil.get(SESSION_KEY);
if (session != null) {
session.getBasicRemote().sendText(message);
}
} catch (IOException e) {
log.error("websocket send result is exception: ", e);
}
return;
}
for (Session session : SESSION_SET) {
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
log.error("websocket send result is exception: ", e);
}
}
}
}
@OnError
public void onError(final Session session, final Throwable error) {
SESSION_SET.remove(session);
ThreadLocalUtil.clear();
log.error("websocket collection error: ", error);
}
@OnClose
public void onClose(final Session session) {
SESSION_SET.remove(session);
ThreadLocalUtil.clear();
}
小结
本小结以admin与bootstrap同步数据的方式入手,介绍了websock的同步方式,先从bootstrap侧讲述启动时的同步逻辑,主体以定时线程任务保持连接,然后介绍了admin端在收到连接请求后的处理,分正常和异常两种,自此本小节结束。希望能帮到你,初识soul这样一个极致性能的网关项目。
本文详细解析了Soul网关如何利用WebSocket实现数据同步。包括配置方式、启动时同步逻辑、定时任务维持连接及Admin端处理流程等内容。
:websocket同步数据&spm=1001.2101.3001.5002&articleId=113133309&d=1&t=3&u=9d6adcfced9544409f61b72671a3bd61)
973

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



