Last time we looked at how Soul uses WebSocket for data synchronization, today we’ll look at when webSocket connections are set up.

As mentioned in the previous article, when soul-Admin is started, all three classes are loaded because YML is configured to synchronize using WebSocket. The WebsocketCollector class starts a WebSocket service.

The SOUl-Bootstrap POM file introduces the starter.

<! --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>Copy the code

The resources/ meta-INF /spring.factories package is used to search for the soul-spring-boot-starter-sync-data-websocket package. Then load the specified module according to the configuration in the file.

// Spring. factories file contents
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.dromara.soul.spring.boot.starter.sync.data.websocket.WebsocketSyncDataConfiguration
Copy the code

The configuration file is WebsocketSyncDataConfiguration classes.

Seeing the code for this class, I first looked up ObjectProvider knowledge.

Before spring4.3, when we needed to inject another class B into A class A, we would use the @autowired annotation and raise an exception if we didn’t add it. After 4.3, we introduced A new feature, we just need to add A constructor to class A, B is passed as A constructor parameter, we can not add @autoWired, but B must be in the Spring container, otherwise there will be an exception, we need to introduce ObjectProvider.

/ / 4.3 before
@Service
public class A {
    private final B b;
    @Autowired
    public A (B b) {
        this.b = b
    }
}
After 4.3, the @autowired annotation is not required, but if B is not in the Spring container, an exception will be raised
@Service
public class A {
    private final B b;
    public A (B b) {
        this.b = b
    }
}
/ / introduce ObjectProvider
@Service
public class A {
    private final B b;
    public A(ObjectProvider<B> bProvider) {
    	// Return null if not available or unique (primary not specified). Otherwise, the object is returned.
        this.b = bProvider.getIfUnique(); }}Copy the code

In the following class, we use the getIfAvailable(Supplier

defaultSupplier) method of the ObjectProvider. This method returns if the object exists and calls back if it does not. The callback object is provided by defaultSupplier.

@Configuration
@ConditionalOnClass(WebsocketSyncDataService.class)
@ConditionalOnProperty(prefix = "soul.sync.websocket", name = "urls")
@Slf4j
public class WebsocketSyncDataConfiguration {

    /** * Websocket sync data service. */
    @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));
    }

    /**
     * Config websocket config.
     *
     * @return the websocket config
     */
    @Bean
    @ConfigurationProperties(prefix = "soul.sync.websocket")
    public WebsocketConfig websocketConfig(a) {
        return newWebsocketConfig(); }}Copy the code

WebsocketSyncDataConfiguration loading depends on the soul of this class. The sync. Websocket, and we are in the soul – the bootstrap configuration as follows, so they will load the class.

As you can see, this class creates a WebsocketSyncDataService object. In this service, it creates a webSocket client that connects to the webSocket service we created in soul-admin. There is also a thread pool ScheduledThreadPoolExecutor, if the client connection is closed, regularly try to reconnect.

    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); }}Copy the code

The soul-admin server sends a message to the client to synchronize the data to the memory in a timely manner.

The resources

Spring ObjectProvider source code analysis