- 启动admin,与网关。 admin操作,使用websocket同步数据到网关
说明
- 数据同步是指将
soul-admin
配置的数据,同步到soul
集群中的JVM内存里面,是网关高性能的关键。 - 将网关与
admin
建立好websocket
连接时,admin
会推送一次全量数据,后续如果配置数据发生变更,则将增量数据通过websocket
主动推送给soul-web
- 使用websocket同步的时候,特别要注意断线重连,也叫保持心跳。
soul
使用java-websocket
这个第三方库来进行websocket
连接。
数据同步开关如何开启
-
在
soul-admin
的application.yml
指定同步策略,可以和网关的数据同步方式有websocket、zk、http、nacos 。websocket
同步(默认方式,推荐)soul: database: dialect: mysql init_script: "META-INF/schema.sql" init_enable: true sync: websocket: enabled: true # zookeeper: # url: localhost:2181 # sessionTimeout: 5000 # connectionTimeout: 2000 # http: # enabled: true # nacos: # url: localhost:8848 # namespace: 1c10d748-af86-43b9-8265-75f487d20c6c # acm: # enabled: false # endpoint: acm.aliyun.com # namespace: # accessKey: # secretKey:
-
在
soul-admin
的pom.xml
文件中 引入以下依赖:<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
-
在
soul-bootstrap
的配置文件application-local.yml
中配置soul-admin
的地址,#urls:是指 soul-admin的地址,如果有多个,请使用(,)分割.soul : sync: websocket : urls: ws://localhost:9095/websocket
优点
- 当建立连接以后会
全量
获取一次数据,以后的数据都是增量的更新与新增
,性能好。 - 支持
断线重连
(默认30秒)。
soul-admin如何同步数据到
soul-web`
-
soul-admin
在用户发生配置变更之后,会通过EventPublisher
发出配置变更通知,由EventDispatcher
处理该变更通知,然后根据配置的同步策略(http
、weboscket
、zookeeper
),将配置发送给对应的事件处理器。 -
将变更后的数据主动推送给
soul-web
,并且在网关层,会有对应的WebSocketDataChangedListener
处理器处理admin
的数据推送。
源码简析
-
soul-admin
数据发生变更时推送通知。(以Selector为例)
-
接收到ApplicationEventPublisher发送来的事件之后触发onApplicationEvent执行,使用了策略模式,根据event.getGroupKey从listener中找到匹配的事件处理函数,再执行afterPropertiesSet()获取listeners。
public void onApplicationEvent(final DataChangedEvent event) { for (DataChangedListener listener : listeners) { switch (event.getGroupKey()) { case APP_AUTH: ...... case SELECTOR: listener.onSelectorChanged((List<SelectorData>) event.getSource(), event.getEventType()); break; .... default: throw new IllegalStateException("Unexpected value: " + event.getGroupKey()); } } } @Override public void afterPropertiesSet() { Collection<DataChangedListener> listenerBeans = applicationContext.getBeansOfType(DataChangedListener.class).values(); this.listeners = Collections.unmodifiableList(new ArrayList<>(listenerBeans)); }
-
WebsocketDataChangedListener
实现了DataChangedListener
接口,监听到配置的变更之后,便会通过建立好的websocket
连接将变更的数据发送给soul-web
public class WebsocketDataChangedListener implements DataChangedListener { ...... @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); } ...... }
-
监听到配置的变更之后,便会通过建立好的
websocket
连接将变更的数据发送给soul-web
,调用WebsocketCollector类的send()。WebsocketCollector WebsocketCollector.send(GsonUtils.getInstance().toJson(websocketData), eventType);
-
soul-bootstrap 引入依赖创建
websocket
客户端,WebsocketSyncDataConfiguration
的WebsocketSyncDataConfiguration
来同步数据,这里面应该是创造了一个SoulWebsocketClient
,猜测这个才是真正的同步数据。<!--soul data sync start use websocket--> <dependency> <groupId>org.dromara</groupId> <artifactId>soul-spring-boot-starter-sync-data-websocket</artifactId> <version>${project.version}</version> </dependency> ......
@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)); }
-
看到引入了依赖
soul-sync-data-websocket
,WebsocketDataHandler
用来处理websocket
协议推送过来的数据。<dependency> <groupId>org.dromara</groupId> <artifactId>soul-sync-data-websocket</artifactId> <version>${project.version}</version> </dependency>
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)); ...... }