Soul网关源码解析(六):websocket同步数据

Soul网关源码解析(六):websocket同步数据

Soul网关源码解析(六):websocket同步数据Websocket同步数据数据同步配置启动时的同步admin的同步处理小结参考

通过上一节,我们知道在Bootstrap启动流程中,会与soul-admin进行数据同步,那么同步的方式采用的是什么呢?在哪里配置的?过程又是如何?这些都在本小节进行解答。

Websocket同步数据

数据同步配置

首先Bootstrap与soul-admin进行数据同步的方式可以配置,这意味着方式不止一种(Soul就是这么灵活),本小节介绍通过WebSocket的方式进行数据同步,配置的地方在Bootstrap的application.yml中配置,配置方式如下:

soul :
    sync:
        websocket :
             urls: ws://localhost:9095/websocket

启动时的同步

在Bootstrap启动初始化过程中,会构造SyncDataService这个Bean,这个Bean的构建过程会与admin进行数据同步,并专门开启定时任务线程检查连接。怎么找到SyncDataService类的,两种方式,一种是日志定位,在bootstrap启动时,会打印“you use websocket sync soul data.......”这条日志,通过整个项目搜索,能够定位到这个函数;另一种,同步配置定位,因为上面有介绍数据同步的配置是soul.sync.websocket.urls,这里可以使用它为搜索关键字进行搜索,如下所示

 

下面我们来看一下这个Bean构建时如何进行同步的,首先@Bean注释的方法,如果方法有参数,会先从Spring Bean容器里获取这些对象,如果没有,则尝试找到这些Bean的配置类中进行构造,这里WebsocketConfig对象就是将上面application.yml中配置的数据同步参数封装成对象,为后面连接使用。接着看这个函数体,就干一件事,创建了一个WebsocketSyncDataService对象,那我们进入其中瞧瞧。

#WebsocketSyncDataConfiguration
    @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));
    }
​
    @Bean
    @ConfigurationProperties(prefix = "soul.sync.websocket")
    public WebsocketConfig websocketConfig() {
        return new WebsocketConfig();
    }
​
@Data
public class WebsocketConfig {
    
    /**
     * if have more soul admin url,please config like this.
     * 127.0.0.1:8888,127.0.0.1:8889
     */
    private String urls;
}

WebsocketSyncDataService对象的初始化有点长,一段一段看,首先获取数据同步配置的urls数组,依据数据长度创建定时线程池,作用用于websocket连接,接着遍历这个urls数组,根据每个url创建SoulWebsocketClient,并将它添加到WebsocketSyncDataService类的list数组里,然后遍历这个list对象,依次连接,并开启一个检查任务放到定时线程池中,每3-秒检查一次连接是否被关闭,第一次时会延迟10秒。

    public WebsocketSyncDataService(final WebsocketConfig websocketConfig,
                                    final PluginDataSubscriber pluginDataSubscriber,
                                    final List<MetaDataSubscriber> metaDataSubscribers,
                                    final List<AuthDataSubscriber> authDataSubscribers) {
        # 获取数据同步配置urls                            
        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("webswebsocketConfig.urlsocket url({}) is error", url, e);
            }
        }
        try {
            for (WebSocketClient client : clients) {
                // 尝试连接并保持3秒
                boolean success = client.connectBlocking(3000, TimeUnit.MILLISECONDS);
                if (success) {
                    log.info("websocket connection is successful.....");
                } else {
                    log.error("websocket connection is error.....");
                }
                // 每30秒检查一次,初次延迟10秒
                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时,构建了WebsocketDataHandler对象,这个对象里保存了一个EnumMap<ConfigGroupEnum, DataHandler> ENUM_MAP,这里面保存了插件,选择器,规则,认证与元数据的处理器,处理器里存着各自的订阅对象,当订阅对象发生变化时,会触发对应的事件对象处理,目前是在CommonPluginDataSubscriber类中统一处理

#SoulWebsocketClient
    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);
    }
    
#WebsocketDataHandler
    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));
        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));
    }

admin的同步处理

上面是bootstrap端的流程,在发送同步信息到admin后,admin这边也有处理,它的处理在WebsocketCollector类里,这里说明一下WebsocketCollector类上有个@ServerEndpoint("/websocket"),这个注解的值需要和上面的Bootstrap配置的url一致。这里admin会先调用onOpen函数,连接成功,并记录这个session,然后同步消息,最后发送这个消息。如果发生异常失败了,则会调用onError函数,接着调onClose将这个session从set中移除。

#WebsocketCollector
admin#WebsocketCollector    
    @OnOpen
    public void onOpen(final Session session) {
        log.info("websocket on open successful....");
        SESSION_SET.add(session);
    }
    
    @OnMessage
    public void onMessage(final String message, final Session session) {
        if (message.equals(DataEventTypeEnum.MYSELF.name())) {
            try {
                ThreadLocalUtil.put(SESSION_KEY, session);
                SpringBeanUtils.getInstance().getBean(SyncDataService.class).syncAll(DataEventTypeEnum.MYSELF);
            } finally {
                ThreadLocalUtil.clear();
            }
        }
    }
    
    public static void send(final String message, final DataEventTypeEnum type) {
        if (StringUtils.isNotBlank(message)) {
            if (DataEventTypeEnum.MYSELF == type) {
                try {
                    Session session = (Session) ThreadLocalUtil.get(SESSION_KEY);
                    if (session != null) {
                        session.getBasicRemote().sendText(message);
                    }
                } catch (IOException e) {
                    log.error("websocket send result is exception: ", e);
                }
                return;
            }
            for (Session session : SESSION_SET) {
                try {
                    session.getBasicRemote().sendText(message);
                } catch (IOException e) {
                    log.error("websocket send result is exception: ", e);
                }
            }
        }
    }
    
    
    @OnError
    public void onError(final Session session, final Throwable error) {
        SESSION_SET.remove(session);
        ThreadLocalUtil.clear();
        log.error("websocket collection error: ", error);
    }
    
    @OnClose
    public void onClose(final Session session) {
        SESSION_SET.remove(session);
        ThreadLocalUtil.clear();
    }

小结

本小结以admin与bootstrap同步数据的方式入手,介绍了websock的同步方式,先从bootstrap侧讲述启动时的同步逻辑,主体以定时线程任务保持连接,然后介绍了admin端在收到连接请求后的处理,分正常和异常两种,自此本小节结束。希望能帮到你,初识soul这样一个极致性能的网关项目。

参考

Soul 官网

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值