配置
- 引入pom文件
<dependency>
<groupId>org.dromara</groupId>
<artifactId>soul-spring-boot-starter-sync-data-websocket</artifactId>
<version>${last.version}</version>
</dependency>
- yml文件配置
soul :
sync:
websocket :
urls: ws://localhost:9095/websocket
如果admin地址有多个使用逗号分割
weboscket原理
网关与 管理后台建立好 websocket 连接时,admin 会推送一次全量数据,后续如果配置数据发生变更,则将增量数据通过 websocket 主动推送给 soul-web
数据如何同步
日志
我们看下启动是否有相关的日志*()
2021-01-20 23:21:29.742 INFO 23508 --- [ main] o.d.s.b.SoulBootstrapApplication : Starting SoulBootstrapApplication on YS-20200423USJU with PID 23508 (E:\opensource\soul\soul-bootstrap\target\classes started by Tao.Huang in E:\opensource\soul)
2021-01-20 23:21:29.742 INFO 23508 --- [ main] o.d.s.b.SoulBootstrapApplication : The following profiles are active: local
2021-01-20 23:21:30.824 INFO 23508 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Multiple Spring Data modules found, entering strict repository configuration mode!
2021-01-20 23:21:30.828 INFO 23508 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data Redis repositories in DEFAULT mode.
2021-01-20 23:21:30.871 INFO 23508 --- [ main] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 17ms. Found 0 Redis repository interfaces.
2021-01-20 23:21:31.804 INFO 23508 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[global] [org.dromara.soul.plugin.global.GlobalPlugin]
2021-01-20 23:21:31.804 INFO 23508 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[sign] [org.dromara.soul.plugin.sign.SignPlugin]
2021-01-20 23:21:31.805 INFO 23508 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[waf] [org.dromara.soul.plugin.waf.WafPlugin]
2021-01-20 23:21:31.805 INFO 23508 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[rate_limiter] [org.dromara.soul.plugin.ratelimiter.RateLimiterPlugin]
2021-01-20 23:21:31.805 INFO 23508 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[hystrix] [org.dromara.soul.plugin.hystrix.HystrixPlugin]
2021-01-20 23:21:31.805 INFO 23508 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[resilience4j] [org.dromara.soul.plugin.resilience4j.Resilience4JPlugin]
2021-01-20 23:21:31.805 INFO 23508 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[divide] [org.dromara.soul.plugin.divide.DividePlugin]
2021-01-20 23:21:31.805 INFO 23508 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[webClient] [org.dromara.soul.plugin.httpclient.WebClientPlugin]
2021-01-20 23:21:31.805 INFO 23508 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[divide] [org.dromara.soul.plugin.divide.websocket.WebSocketPlugin]
2021-01-20 23:21:31.805 INFO 23508 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[alibaba-dubbo-body-param] [org.dromara.soul.plugin.alibaba.dubbo.param.BodyParamPlugin]
2021-01-20 23:21:31.805 INFO 23508 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[dubbo] [org.dromara.soul.plugin.alibaba.dubbo.AlibabaDubboPlugin]
2021-01-20 23:21:31.805 INFO 23508 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[monitor] [org.dromara.soul.plugin.monitor.MonitorPlugin]
2021-01-20 23:21:31.805 INFO 23508 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[response] [org.dromara.soul.plugin.httpclient.response.WebClientResponsePlugin]
2021-01-20 23:21:31.805 INFO 23508 --- [ main] o.d.s.w.configuration.SoulConfiguration : load plugin:[response] [org.dromara.soul.plugin.alibaba.dubbo.response.DubboResponsePlugin]
2021-01-20 23:21:32.025 INFO 23508 --- [ main] b.s.s.d.w.WebsocketSyncDataConfiguration : you use websocket sync soul data.......
2021-01-20 23:21:32.225 INFO 23508 --- [ main] o.d.s.p.s.d.w.WebsocketSyncDataService : websocket connection is successful.....
2021-01-20 23:21:32.306 INFO 23508 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoint(s) beneath base path '/actuator'
2021-01-20 23:21:33.457 INFO 23508 --- [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port(s): 9195
2021-01-20 23:21:33.460 INFO 23508 --- [ main] o.d.s.b.SoulBootstrapApplication : Started SoulBootstrapApplication in 4.287 seconds (JVM running for 5.599)
我们可以看到在启动日志中除了加载插件的日志还有2条是这样的
you use websocket sync soul data.......
websocket connection is successful.....
我们现在代码中找到相关的打个断点看下相关的代码
源码
soul-spring-boot-starter-sync-data-websocket
通过日志我们知道这个日志在 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));
}
我们可以看到实际上调用的是类中的方法WebsocketSyncDataService
public WebsocketSyncDataService(final WebsocketConfig websocketConfig,
final PluginDataSubscriber pluginDataSubscriber,
final List<MetaDataSubscriber> metaDataSubscribers,
final List<AuthDataSubscriber> authDataSubscribers) {
//获取你注册的客户端ruls
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("websocket url({}) is error", url, e);
}
}
//2.链接
try {
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);
}
}
soul-admin
在网关启动后我们可以看到管理后新增一条日志
2021-01-20 23:55:41.164 INFO 6440 --- [0.0-9095-exec-1] o.d.s.a.l.websocket.WebsocketCollector : websocket on open successful....
根据日志我们找到了类WebsocketCollector
然后再把几个方法打个断点WebsocketCollector
当网关启动后发现调用了SyncDataService的syncAll方法
@Override
public 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;
}
这个方法的主要就是在启动的时候加载数据并发送
通过在管理后台同步数据的时候会调用方法
@Override
public boolean syncPluginData(final String pluginId) {
PluginVO pluginVO = pluginService.findById(pluginId);
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.PLUGIN, DataEventTypeEnum.UPDATE,
Collections.singletonList(PluginTransfer.INSTANCE.mapDataTOVO(pluginVO))));
List<SelectorData> selectorDataList = selectorService.findByPluginId(pluginId);
if (CollectionUtils.isNotEmpty(selectorDataList)) {
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.SELECTOR, DataEventTypeEnum.REFRESH, selectorDataList));
List<RuleData> allRuleDataList = new ArrayList<>();
for (SelectorData selectData : selectorDataList) {
List<RuleData> ruleDataList = ruleService.findBySelectorId(selectData.getId());
allRuleDataList.addAll(ruleDataList);
}
eventPublisher.publishEvent(new DataChangedEvent(ConfigGroupEnum.RULE, DataEventTypeEnum.REFRESH, allRuleDataList));
}
return true;
}
然后就是进入类WebsocketDataChangedListener中相关的方法发送数据
例如同步插件列表数据的时候调用其中的一个发送方法
@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);
}
在上面的方法中我们可以知道soul是通过EventPublisher发出变更通知的.