Soul源码分析 —— 同步配置数据的方式汇总

本文分析了Soul框架中四种配置数据同步方式:long polling、zookeeper、nacos和websocket。其中,long polling简单但占资源;zookeeper时效性高但可能引入额外依赖;nacos功能强大但可能过重;而websocket则是默认选项,节省资源且减少依赖。总结了每种方式的优缺点及其在Soul中的应用。
摘要由CSDN通过智能技术生成

数据同步方式

  • long polling同步配置数据:
    代码实现比较简单,易于维护,long polling会大量占用连接池的资源,影响系统的并发数

  • zookeeper同步配置数据
    使用zookeeper同步机制也是非常好的,时效性也高,但是也要处理zk环境不稳定,集群脑裂等问题. 如果zookeeper已经在当前系统中广泛使用,那用它作为同步机制是个很好的选择。否则增加一个新的外部依赖也是一个需要考量的点。

  • nacos同步配置数据
    阿里的nacos功能非常强大,可以同时作为注册中心和配置中心, 但是使用它来同步配置数据有点太heavy了。和zookeeper一样,如果nacos已经在当前系统中使用,那用它作为同步的机制是个很好的选择。

  • websocket同步配置数据
    特点: WebSocket对比http long poll节省系统资源,对比使用zookeeper少了一个系统依赖。所以它是soul的默认选项

配置

配置上需要加上不同的同步机制的starter

<dependency>
    <groupId>org.dromara</groupId>
    <artifactId>soul-spring-boot-starter-sync-data-xxx</artifactId>
    <version>${last.version}</version>
</dependency>

在网关和soul-admin上需要增加对应的参数,这里就不再赘述了

使用配置数据的订阅

websocket

以websocket为例,WebsocketSyncDataService建立和client的连接和断开重连

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);
            }
        }
        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);
        }

SoulWebsocketClient在服务启动时全量的同步数据,或者在每次收到消息时更新对应的配置数据。

 @Override
    public void onOpen(final ServerHandshake serverHandshake) {
        if (!alreadySync) {
            send(DataEventTypeEnum.MYSELF.name());
            alreadySync = true;
        }
    }
@Override
    public void onMessage(final String result) {
        handleResult(result);
    }
    
    @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);
    }
nacos
    public void start() {
        watcherData(PLUGIN_DATA_ID, this::updatePluginMap);
        watcherData(SELECTOR_DATA_ID, this::updateSelectorMap);
        watcherData(RULE_DATA_ID, this::updateRuleMap);
        watcherData(META_DATA_ID, this::updateMetaDataMap);
        watcherData(AUTH_DATA_ID, this::updateAuthMap);
    }
long polling
@Override
public void run() {
     while (RUNNING.get()) {
         for (int time = 1; time <= retryTimes; time++) {
             try {
                 doLongPolling(server);
             } catch (Exception e) {
                 // print warnning log.
                 if (time < retryTimes) {
                     log.warn("Long polling failed, tried {} times, {} times left, will be suspended for a while! {}",
                             time, retryTimes - time, e.getMessage());
                     ThreadUtils.sleep(TimeUnit.SECONDS, 5);
                     continue;
                 }
                 // print error, then suspended for a while.
                 log.error("Long polling failed, try again after 5 minutes!", e);
                 ThreadUtils.sleep(TimeUnit.MINUTES, 5);
             }
         }
     }
     log.warn("Stop http long polling.");
 }
zookeeper
kClient.subscribeDataChanges(pluginPath, new IZkDataListener() {
@Override
 public void handleDataChange(final String dataPath, final Object data) {
     Optional.ofNullable(data)
             .ifPresent(d -> Optional.ofNullable(pluginDataSubscriber).ifPresent(e -> e.onSubscribe((PluginData) d)));
 }

 @Override
 public void handleDataDeleted(final String dataPath) {
     final PluginData data = new PluginData();
     data.setName(pluginName);
     Optional.ofNullable(pluginDataSubscriber).ifPresent(e -> e.unSubscribe(data));
 }
});
小结

这里对soul采用的4种配置数据同步方式进行了对比和源码的分析

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值