Soul网关源码阅读(四)—— sofa插件详解
概述
经过divide插件和dubbo插件篇的讲解,soul网关的核心概念和大体运行机制已经熟悉的差不多了。我们知道了,更换插件对于soul网关来说无非不就是更换soul网关到后端服务的通信协议,剩下的逻辑都是共同的。
因此这篇文章在介绍sofa插件使用方式的同时,我们还会接着上一篇dubbo篇,续集往下深入,梳理一下soul-admin与soul-bootstrap服务进行数据同步的具体细节。
实践操作
-
启动admin服务。
-
在bootstrap服务pom.xml中添加对sofa插件的依赖,并启动bootstrap服务。
<dependency> <groupId>com.alipay.sofa</groupId> <artifactId>sofa-rpc-all</artifactId> <version>5.7.6</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-client</artifactId> <version>4.0.1</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-framework</artifactId> <version>4.0.1</version> </dependency> <dependency> <groupId>org.apache.curator</groupId> <artifactId>curator-recipes</artifactId> <version>4.0.1</version> </dependency> <dependency> <groupId>org.dromara</groupId> <artifactId>soul-spring-boot-starter-plugin-sofa</artifactId> <version>${last.version}</version> </dependency>
-
打开admin服务对sofa插件的支持开关。
-
启动soul-examples/soul-examples-sofa服务。
-
观测添加@SoulSofaClient注解的api是否在admin注册成功
-
使用postman请求网关进行sofa的转发测试。
源码阅读
今天阅读源码的思路,从上一篇Soul网关源码学习(三)——Dubbo插件详解 接着往下分析。
上一篇分析了如果soul网关使用websocket与admin进行通信,admin服务中WebsocketCollector类的send方法负责将数据发送给soul网关。那么网关服务是如何接收admin传过来的数据并且如何使用的呢?
查看项目结构可以发现,有一个子模块soul-sync-data-center,可以判断这个模块负责与admin的数据同步。该模块下面有几个子模块对应不同协议的数据同步方式,正好与DataSyncConfiguration类中配置的一致,这里我们着重分析soul-sync-data-websocket模块。
先查看一下目录结构:
先分析WebsocketSyncDataService类,该类的两个成员变量,clients和executor。
-
executor是一个定时任务类型的线程执行器,每30秒检测soul与admin的websocket连接是否正常,如果断掉则重连。
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);
-
clients用来持有与admin的连接,client的实现类是SoulWebsocketClient,它扩展了WebSocketClient。
public final class SoulWebsocketClient extends WebSocketClient { //... 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); } //...监听到消息执行逻辑。 @Override public void onMessage(final String result) { handleResult(result); } //执行具体的handler @SuppressWarnings("ALL") private void handleResult(final String result) { WebsocketData websocketData = GsonUtils.getInstance().fromJson(result, WebsocketData.class); ConfigGroupEnum groupEnum = ConfigGroupEnum.acquireByName(websocketData.getGroupType()); String eventType = websocketData.getEventType(); String json = GsonUtils.getInstance().toJson(websocketData.getData()); websocketDataHandler.executor(groupEnum, json, eventType); } }
该类的主要任务是,通过构造参数将不同类型的业务处理handler通过构造函数封装到websocketDataHandler中,然后onMessage方法接收admin的消息,并执行对应的handle方法,我们可以看到之前handle目录下的handler就是来负责处理具体逻辑的。
我们选择PluginDataHandler来分析一下:
可以看到真正执行处理的PluginDataSubscriber,它是插件进行与admin同步数据的接口,可以看到其定义了很多数据交互的方法。PluginDataSubscriber的具体实现类是CommonPluginDataSubscriber。CommonPluginDataSubscriber维护了一个key为插件名称,value 为handler的Map集合。
public CommonPluginDataSubscriber(final List<PluginDataHandler> pluginDataHandlerList) { this.handlerMap = pluginDataHandlerList.stream().collect(Collectors.toConcurrentMap(PluginDataHandler::pluginNamed, e -> e)); }
其核心方法是subscribeDataHandler:
这里不同的事件类型调用相应的处理器去做数据处理,真正存储admin同步来的数据的类是BaseDataCache。
打开BaseDataCache类:
好了,终于找到真正缓存数据的地方了,可以看到分别这里使用了一个ConcurrentMap用来缓存admin同步来的数据。
思考总结
今天简单的学习了sofa插件的使用,另一方面接着上一篇继续分析了数据同步的源码,了解到了如何使用一个hashmap来做数据同步,了解了如何维持一个connection连接(定时任务检测重连,类似心跳检测)。