1.4.1 數(shù)據(jù)同步方式
我們知道Soul數(shù)據(jù)同步可以配置為 Websocket咙鞍,Http 長(zhǎng)輪詢房官,Zookeeper 和 Nacos 這四種方式,我們從更新Selector的接口作為切入點(diǎn)续滋,根據(jù)調(diào)用鏈一步步分析各種數(shù)據(jù)同步方式翰守。
1.4.1 通用處理鏈路
在決定使用那種方式發(fā)送前都會(huì)經(jīng)過(guò)一個(gè)通用的分配流程,我們從新Selector的接口出發(fā)蜡峰,SelectorController 的 updateSelector 方法會(huì)被端調(diào)用進(jìn)行服務(wù)更新湿颅。這里調(diào)用了 SelectorServiceImpl服務(wù)進(jìn)行更新油航。
這里先執(zhí)行數(shù)據(jù)庫(kù)操作谊囚,將對(duì)應(yīng)的Selector更新镰踏,然后將舊的rule刪除余境,然后再逐條插入新的規(guī)則灌诅,最終調(diào)用到了eventPubliser的publishEvent方法含末,這里使用了SpringBoot的消息發(fā)布機(jī)制佣盒,最終發(fā)布了一個(gè)DataChangedEvent類型的消息,我們接著看消息消費(fèi)者 DataChangedEventDispatcher 叶撒。
DataChangedEventDispatcher 實(shí)現(xiàn)了 ApplicationListener<DataChangedEvent> 這個(gè)接口,它重寫(xiě)了OnApplicationEvent方法耀石,該方法就是接收消息的方法滞伟。同時(shí) DataChangedEventDispatcher 也實(shí)現(xiàn)了InitializingBean 接口梆奈,他會(huì)在所有的Bean都準(zhǔn)備就緒后觸發(fā) afterPropertiesSet 方法亩钟。
我們看它主要就是查詢里面所有實(shí)現(xiàn)了 DataChangedListener 方法的實(shí)現(xiàn)類径荔,其主要實(shí)現(xiàn)類有如下幾個(gè)总处。其實(shí)就是我們接下來(lái)要說(shuō)的幾種數(shù)據(jù)同步方式鹦马。
我們回過(guò)頭來(lái)看一下 OnApplicationEvent 做了什么荸频,我們可以看到旭从,它主要是輪詢所有注冊(cè)了的Listener和悦,然后根據(jù)不同的Group Key 調(diào)用對(duì)應(yīng)的方法鸽素。listener的方法馍忽。
我們看這里的listener是如何注冊(cè)進(jìn)ApplicationContext的呢遭笋。我們看它被引用的地方 DataSyncConfiguration 瓦呼,這里根據(jù)我們的配置進(jìn)行對(duì)應(yīng)的加載,我們看一下websocket的流程:但開(kāi)啟soul.sync.websocket.enabled 的時(shí)候會(huì)進(jìn)行bean的加載谎替。這里包括三部分钱贯,WebsocketDataChangedListener 即我們剛才被調(diào)用的listener秩命;WebsocketCollector 這是個(gè)WebSocket 的客戶端弃锐,主要負(fù)責(zé)收發(fā)數(shù)據(jù)霹菊;ServerEndpointExporter 這個(gè)是Spring-WebSocket的一個(gè)類旋廷,他也實(shí)現(xiàn)了SmartInitializingSingleton 接口饶碘,他會(huì)在每個(gè)單例bean注冊(cè)完成后對(duì)其進(jìn)行掃描是否使用了 ServerEndpoint 注解扎运,然后將該類注入到serverContainer中豪治。
1.4.2 websocket Server 端更新流程
由于我們是更新selector 鬼吵, 所以會(huì)調(diào)用listener 的 onSelectorChanged 方法篮赢,這里最終還是調(diào)用了WebsocketCollector 的send 方法启泣。
我們可以看出來(lái)寥茫,在Spring中使用WebSocket非常簡(jiǎn)單纱耻,我們只需要使用ServerEndpoint 這個(gè)注解定義這個(gè)是websocket處理類弄喘,OnOpen 這個(gè)注解類定義在websocket鏈接建立后生產(chǎn)對(duì)應(yīng)的session然后回調(diào)這個(gè)服務(wù),這里將session作為靜態(tài)屬性持有累奈,OnMessage 接收消息處理方法澎媒,onClose 鏈接關(guān)閉處理方法戒努。
我們可以看到Souladmin這里會(huì)持有所有的WebSocket鏈接柏卤。
在發(fā)送的時(shí)候會(huì)給所有的客戶端進(jìn)行發(fā)送缘缚,這里還有對(duì)自定義類型的處理桥滨。
1.4.2 websocket Client 端
我們?cè)賮?lái)看看接收端,我們可以看到使用websocket同步數(shù)據(jù)纷跛,BootStrap需要使用以下依賴
<dependency>
<groupId>org.dromara</groupId>
<artifactId>soul-spring-boot-starter-sync-data-websocket</artifactId>
<version>${project.version}</version>
</dependency>
我們找到該項(xiàng)目贫奠,這是一個(gè)標(biāo)準(zhǔn)的springboot-starter,這里定義個(gè)一個(gè)自動(dòng)配置類拷恨。
我們來(lái)看一下配置類 WebsocketSyncDataConfiguration小泉, 它主要是初始化了兩個(gè)Bean 一個(gè)是 websocketSyncDataService 即為數(shù)據(jù)同步服務(wù)冕杠, 一個(gè)是 websocketConfig 即websocket的一些配置項(xiàng)分预。這里會(huì)將以soul.sync.websocket 開(kāi)頭的配置注入到這個(gè)類中噪舀,這里只有一個(gè)屬性 urls。
websocketSyncDataService 這里使用到了 ObjectProvider 這個(gè)是SpringFactory到一個(gè)擴(kuò)展點(diǎn),我們知道在一個(gè)方法前Autowrite也可以實(shí)現(xiàn)注入纺座,但是假如存在多個(gè)參數(shù)類是以這里到常見(jiàn)到話Autowrite就懵,
- 如果注入實(shí)例為空時(shí)少欺,使用ObjectProvider則避免了強(qiáng)依賴導(dǎo)致的依賴對(duì)象不存在異常馋贤;主要是 getIfAvailable 這個(gè)方法配乓。
- 如果有多個(gè)實(shí)例,ObjectProvider的方法會(huì)根據(jù)Bean實(shí)現(xiàn)的Ordered接口或@Order注解指定的先后順序獲取一個(gè)Bean崎页。從而了提供了一個(gè)更加寬松的依賴注入方式飒焦。主要通過(guò) orderedStream 方法返回一個(gè)根據(jù)order注解的一個(gè)bean 的 stream屿笼。
@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));
}
我們根據(jù)debug數(shù)據(jù)看一下主要注入的參數(shù),PluginDataSubscriber 這個(gè)主要是各個(gè)插件的Handler 主要負(fù)責(zé)插件內(nèi)的數(shù)據(jù)更新。metaDataSubscribers 元數(shù)據(jù)訂閱者挑辆,authDataSubscribers 認(rèn)證數(shù)據(jù)訂閱鱼蝉。
這里會(huì)根據(jù)websocket的配置生成一個(gè)SoulWebsocketClient 客戶端箫荡,然后嘗試鏈接服務(wù)端羔挡。
這里會(huì)觸發(fā)服務(wù)端的OnMessage方法绞灼,然后觸發(fā)SysnAll方法,向客戶端發(fā)送所有數(shù)據(jù)印叁。我們看調(diào)用棧轮蜕,這里會(huì)包含所有信息同步給客戶端跃洛。
這里包括了插件數(shù)據(jù)税课,selector 數(shù)據(jù)和 rule 數(shù)據(jù)韩玩。
@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;
}
我們?cè)倏纯碨oulWebsocketClient 做了什么陆馁,其主要是在接收到OnMessge的時(shí)候調(diào)用websocketDataHandler 進(jìn)行消息處理叮贩。
websocketDataHandler 里面再注冊(cè)了各種數(shù)據(jù)的處理器,如圖 包括plugin selector rule app_auth meta 這幾種數(shù)據(jù)的更新寸莫。
我們看handle 是一個(gè)default方法膘茎,主要是調(diào)用各個(gè)實(shí)現(xiàn)類的refresh方法
我們更新selector主要是做以下操作,更新內(nèi)存數(shù)據(jù)盐数,然后通知各個(gè)插件數(shù)據(jù)更新。
1.4.4 總結(jié)
在這期的學(xué)習(xí)過(guò)程中,我學(xué)到很一些Spring的新特性如 ObjectProvider漾峡,還知道了在SpringBoot中如何寫(xiě)一個(gè)WebSocket的客戶端灰殴。