Soul网关源码阅读12-使用websocket同步数据

使用websocket同步数据到网关

一、配置环境

1、soul-admin

soul-admin/src/main/resources/application.yml

soul:
 sync:
    websocket:
      enabled: true

2、soul-bootstrap

soul-bootstrap/src/main/resources/application-local.yml

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

soul-bootstrap/pom.xml

<!--soul data sync start use websocket-->
<dependency>
    <groupId>org.dromara</groupId>
    <artifactId>soul-spring-boot-starter-sync-data-websocket</artifactId>
    <version>${project.version}</version>
</dependency>

3、启动服务

1、soul-admin,端口9095
2、soul-admin,端口9096
3、soul-bootstrap,端口9195
4、soul-bootstrap,端口9196

二、soul-admin 端

1、soul-admin 启动会注入配置类 DataSyncConfiguration,像容器中注入 WebsocketListener。

org.dromara.soul.admin.config.DataSyncConfiguration

@Configuration
@ConditionalOnProperty(name = "soul.sync.websocket.enabled", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(WebsocketSyncProperties.class)
static class WebsocketListener {
    // 负责监听数据变化
    @Bean
    @ConditionalOnMissingBean(WebsocketDataChangedListener.class)
    public DataChangedListener websocketDataChangedListener() {
        return new WebsocketDataChangedListener();
    }
    // 负责维护websocket连接通信
    @Bean
    @ConditionalOnMissingBean(WebsocketCollector.class)
    public WebsocketCollector websocketCollector() {
        return new WebsocketCollector();
    }
    @Bean
    @ConditionalOnMissingBean(ServerEndpointExporter.class)
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}

WebsocketDataChangedListener 负责监听网关数据变化的处理

org.dromara.soul.admin.listener.websocket.WebsocketDataChangedListener

通过 WebsocketCollector.send 发送给websocket客户端(soul-bootstrap)

public class WebsocketDataChangedListener implements DataChangedListener {
    @Override
    public void onPluginChanged(final List<PluginData> pluginDataList, final DataEventTypeEnum eventType) {
        WebsocketData<PluginData> websocketData =
                new WebsocketData<>(ConfigGroupEnum.PLUGIN.name(), eventType.name(), pluginDataList);
        WebsocketCollector.send(GsonUtils.getInstance().toJson(websocketData), eventType);
    }
    ......
}

WebsocketCollector 负责打开websocket等待客户端连接

org.dromara.soul.admin.listener.websocket.WebsocketCollector

Set SESSION_SET 存放客户端session,用来与多个soul-bootstrap同步网关数据

@ServerEndpoint("/websocket")
public class WebsocketCollector {
    private static final Set<Session> SESSION_SET = new CopyOnWriteArraySet<>();
    private static final String SESSION_KEY = "sessionKey";
    // 打开websocket连接
    @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();
            }
        }
    }
    ......
    // 发送数据,会遍历所有的session发送消息
    public static void send(final String message, final DataEventTypeEnum type) {
        if (StringUtils.isNotBlank(message)) {
            if (DataEventTypeEnum.MYSELF == type) {
                Session session = (Session) ThreadLocalUtil.get(SESSION_KEY);
                if (session != null) {
                    sendMessageBySession(session, message);
                }
            } else {
                SESSION_SET.forEach(session -> sendMessageBySession(session, message));
            }
        }
    }
    ......
}

2、soul-admin 监听到soul-bootstrap连接会打印如下信息

并存入 SESSION_SET.add(session);

2021-01-27 17:45:55.878  INFO 15932 --- [0.0-9096-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 86 ms
2021-01-27 17:45:59.361  INFO 15932 --- [0.0-9096-exec-1] o.d.s.a.l.websocket.WebsocketCollector   : websocket on open successful....
三、soul-bootstrap

1、soul-bootstrap 启动

加入了依赖 soul-spring-boot-starter-sync-data-websocket,启动后自动注入WebsocketSyncDataConfiguration

org.dromara.soul.spring.boot.starter.sync.data.websocket.WebsocketSyncDataConfiguration

向容器中注入 WebsocketSyncDataService

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

2、WebsocketSyncDataService

org.dromara.soul.plugin.sync.data.websocket.WebsocketSyncDataService

初始化会根据配置的websocket urls,建立websocket连接
1、如果配置多个url,也就是soul-admin 有多个服务,会循环建立多个SoulWebsocketClient
2、client.connectBlocking(3000, TimeUnit.MILLISECONDS) 可以链接超时判断
3、executor.scheduleAtFixedRate 连接失败后的,负责定时重连

public WebsocketSyncDataService(final WebsocketConfig websocketConfig,
                                final PluginDataSubscriber pluginDataSubscriber,
                                final List<MetaDataSubscriber> metaDataSubscribers,
                                final List<AuthDataSubscriber> authDataSubscribers) {
    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);
    }
}

3、SoulWebsocketClient

org.dromara.soul.plugin.sync.data.websocket.client.SoulWebsocketClient

建立连接后,发送DataEventTypeEnum.MYSELF消息,用来初始化网关数据

@Override
public void onOpen(final ServerHandshake serverHandshake) {
    if (!alreadySync) {
        send(DataEventTypeEnum.MYSELF.name());
        alreadySync = true;
    }
}

4、soul-bootstrap 启动成功后,如果多个soul-admin中有连接失败的情况,会定时重连


2021-01-27 17:45:50.085  INFO 15959 --- [           main] b.s.s.d.w.WebsocketSyncDataConfiguration : you use websocket sync soul data.......
2021-01-27 17:45:55.687 ERROR 15959 --- [           main] o.d.s.p.s.d.w.WebsocketSyncDataService   : websocket connection is error.....
2021-01-27 18:00:53.430 ERROR 15959 --- [           main] o.d.s.p.s.d.w.WebsocketSyncDataService   : websocket connection is error.....
2021-01-27 18:00:54.950  INFO 15959 --- [ocket-connect-1] o.d.s.p.s.d.w.WebsocketSyncDataService   : websocket reconnect is successful.....
2021-01-27 18:00:55.164  INFO 15959 --- [           main] o.s.b.a.e.web.EndpointLinksResolver      : Exposing 2 endpoint(s) beneath base path '/actuator'
2021-01-27 18:00:56.691  INFO 15959 --- [           main] o.s.b.web.embedded.netty.NettyWebServer  : Netty started on port(s): 9196
2021-01-27 18:00:56.695  INFO 15959 --- [           main] o.d.s.b.SoulBootstrapApplication         : Started SoulBootstrapApplication in 909.641 seconds (JVM running for 910.725)
2021-01-27 18:01:05.709  INFO 15959 --- [ocket-connect-2] o.d.s.p.s.d.w.WebsocketSyncDataService   : websocket reconnect is successful.....
2021-01-27 18:02:03.454  INFO 15959 --- [ocket-connect-1] o.d.s.p.s.d.w.WebsocketSyncDataService   : websocket reconnect is successful.....
2021-01-27 18:02:05.705 ERROR 15959 --- [ocket-connect-2] o.d.s.p.s.d.w.WebsocketSyncDataService   : websocket reconnection is error.....
2021-01-27 18:02:35.704 ERROR 15959 --- [ocket-connect-2] o.d.s.p.s.d.w.WebsocketSyncDataService   : websocket reconnection is error.....
四、总结

1、soul-admin 启动后通过 WebsocketCollector --> onOpen 监听 soul-bootstrap 连接,并保存 Session
2、soul-bootstrap 启动后初始化 WebsocketSyncDataService 创建 SoulWebsocketClient,如果有多个 soul-admin ,会创建多个SoulWebsocketClient,并创建定时任务进行失败重连
3、SoulWebsocketClient --> onOpen 打开连接后会发送消息 DataEventTypeEnum.MYSEL,用来初始化网关数据
4、WebsocketCollector --> onMessage 接收判断并判断消息 DataEventTypeEnum.MYSEL
5、SyncDataServiceImpl --> syncAll 负责处理 DataEventTypeEnum.MYSEL 向soul-admin同步全部的网关数据

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值