Soul网关源码学习02
数据同步
soul网关中路由规则、插件管理、负载均衡策略、限流熔断等配置,都是支持动态变更,数据的同步做到秒级同步。配置变更导致数据的同步支持 websocket、http、zookeeper、nacos 等多种方式,实现soul-admin和soul-web的数据同步。
websocket 同步
soul-web 启动时与 soul-admin 建立 websocket 连接,之后soul-admin 会推送一次全量数据,后续如果配置数据发生变更,则将增量数据通过 websocket 主动推送给 soul-web。soul-admin基于 tomcat 的 websocket 实现服务端,soul-web 使用 java-websocket 第三方库实现客户端建立连接。
soul-web 客户端
YML中配置使用 websocket 同步
soul :
sync:
websocket :
urls: ws://localhost:9095/websocket
WebsocketSyncDataConfiguration
@Bean
public SyncDataService websocketSyncDataService(@NotNull final ObjectProvider<WebsocketConfig> websocketConfig,
final ObjectProvider<PluginDataSubscriber> pluginSubscriber,
final ObjectProvider<List<MetaDataSubscriber>> metaSubscribers,
final ObjectProvider<List<AuthDataSubscriber>> authSubscribers) {
//创建Websocket客户端
return new WebsocketSyncDataService(websocketConfig.getIfAvailable(WebsocketConfig::new), pluginSubscriber.getIfAvailable(),metaSubscribers.getIfAvailable(Collections::emptyList),authSubscribers.getIfAvailable(Collections::emptyList));
}
@Bean
@ConfigurationProperties(prefix = "soul.sync.websocket")
//websocket配置
public WebsocketConfig websocketConfig() {
return new WebsocketConfig();
}
1. 使用spring.factories的方式加载,WebsocketSyncDataConfiguration。
2. @ConfigurationProperties 将 websocket 的配置信息封装成实体类。
3. 创建Websocket客户端。
WebsocketSyncDataService
private List<WebSocketClient> clients = new ArrayList<>();
private final ScheduledThreadPoolExecutor executor;
public WebsocketSyncDataService(final WebsocketConfig websocketConfig,
final PluginDataSubscriber pluginDataSubscriber,
final List<MetaDataSubscriber> metaDataSubscribers,
final List<AuthDataSubscriber> authDataSubscribers) {
//多个websocket地址
String[] urls = StringUtils.split(websocketConfig.getUrls(), ",");
//创建定时任务的线程
executor = new ScheduledThreadPoolExecutor(urls.length, SoulThreadFactory.create("websocket-connect", true));
//每个url创建Websocket客户端
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);
}
}
try {
for (WebSocketClient client : clients) {
//每个url创建Websocket客户端
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);
}
} catch (InterruptedException e) {
log.info("websocket connection...exception....", e);
}
}
//资源关闭
@Override
public void close() {
//关闭socket连接
for (WebSocketClient client : clients) {
if (!client.isClosed()) {
client.close();
}
}
if (Objects.nonNull(executor)) {
executor.shutdown();
}
}
1. 每个url创建websocket客户端与服务端建立连接,因为是阻塞的所以设置超时时间,防止被卡住。
2. 状态isClosed表示关闭或未建立连接,定时任务的线程进行重试,10秒后执行,间隔周期为30秒。
3. 实现AutoCloseable接口对资源的关闭,关闭socket连接。
SoulWebsocketClient
private volatile boolean alreadySync = Boolean.FALSE;
private final WebsocketDataHandler websocketDataHandler;
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 onOpen(final ServerHandshake serverHandshake) {
if (!alreadySync) {
//发送MYSELF到服务端,获取全量数据
send(DataEventTypeEnum.MYSELF.name());
alreadySync = true;
}
}
@Override
public void onMessage(final String result) {
//接受消息
handleResult(result);
}
@Override
public void onClose(final int i, final String s, final boolean b) {
this.close();
}
@Override
public void onError(final Exception e) {
this.close();
}
@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);
}
1. 建立连接后发送消息到服务端,服务端会发送全量的配置信息给客户端。
2. 接收服务端发送的消息并进行处理,json数据序列化成对象,从EnumMap中获取处理的Handler。
3. 事件对应的Handler。
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));
ENUM_MAP.put(ConfigGroupEnum.PLUGIN, new PluginDataHandler(pluginDataSubscriber));
ENUM_MAP.put(ConfigGroupEnum.SELECTOR, new SelectorDataHandler(pluginDataSubscriber));
AbstractDataHandler
protected abstract List<T> convert(String json);
protected abstract void doRefresh(List<T> dataList);
protected abstract void doUpdate(List<T> dataList);
protected abstract void doDelete(List<T> dataList);
@Override
public void handle(final String json, final String eventType) {
List<T> dataList = convert(json);
if (CollectionUtils.isNotEmpty(dataList)) {
DataEventTypeEnum eventTypeEnum = DataEventTypeEnum.acquireByName(eventType);
switch (eventTypeEnum) {
case REFRESH:
case MYSELF:
doRefresh(dataList);
break;
case UPDATE:
case CREATE:
doUpdate(dataList);
break;
case DELETE:
doDelete(dataList);
break;
default:
break;
}
}
}
定义模板方法,不同事件类型调用各自实现类进行更新。