Springboot erstellt einen WebSocket-Cluster

Es sind Probleme aufgetreten

Als die offizielle Website von Yideng (Personal WeChat Login Solution) die Anmeldefunktion zum ersten Mal implementierte, wurde sie über HTTP-Abfragen implementiert. Später, als die Anzahl der Benutzer allmählich zunahm, traten nach und nach die Nachteile dieser Lösung zutage, und es kam zu häufigen Anfragen nach dem Back- Endschnittstelle

Das System wurde zunächst in einem eigenständigen Zustand betrieben und die Implementierung von WebSocket war problemlos möglich. Mit dem Betrieb des Systems wurde später ein Dienst hinzugefügt, um einen Dual-Service-Cluster zu realisieren. Zu diesem Zeitpunkt stieß ich jedoch beim Anmelden am System auf ein Problem mit dem Websocket in einer Clusterumgebung. Manchmal ist die Anmeldung durch Scannen des QR-Codes erfolgreich, aber das System erhält den Anmeldestatus nicht, was dazu führt, dass die Anmeldung am System nicht möglich ist.

Ursache des Problems

Wenn das Front-End eine WebSocket-Verbindung mit Dienst A herstellt, zeichnet Dienst A die Sitzungsinformationen des WebSockets auf, Dienst B stellt jedoch keine WebSocket-Verbindung mit dem Front-End her. Wenn zu diesem Zeitpunkt eine Nachricht über Dienst B an das Front-End gesendet wird, kann die WebSocket-Nachricht nicht an das Front-End gesendet werden, da Dienst B keine WebSocket-Verbindung mit dem Front-End hergestellt hat.

Dies ähnelt der Situation, in der Benutzerinformationen zwischen mehreren Diensten verloren gehen, wenn die HTTP-Sitzung nicht in einer Clusterumgebung gemeinsam genutzt wird.

Problem gelöst

Nachdem das Problem nun gefunden wurde, ist es einfach zu lösen. Sie können sowohl Dienst A als auch Dienst B über die zu sendende Nachricht benachrichtigen. Reicht es nicht aus, dass jeder, der über die Sitzungsinformationen verfügt, die Nachricht senden kann?

Sind Sie etwas verwirrt, wenn Sie diesen kleinen Freund sehen? Ist das nicht ein typisches Publish-Subscribe- Modell?

Beide Dienste abonnieren denselben Kanal. Solange sich eine Nachricht in diesem Kanal befindet, senden beide Dienste die Nachricht. Dadurch wird sichergestellt, dass die Nachricht versendet werden kann.

 

Lösung

Es gibt viele Lösungen auf dem Markt, die das Publish-Subscribe- Modell implementiert haben , wie zum Beispiel das MQ-Framework, Redis usw. Da das Yideng- System Redis integriert hat, ist es nicht erforderlich, das MQ-Framework einzuführen, um dieses Problem zu lösen.

Lassen Sie uns darüber sprechen, wie Sie die Veröffentlichungs- und Abonnementfunktion von Redis verwenden können , um dieses Problem zu lösen.

Lösungsumsetzung

  • JedisAbhängigkeiten einführen 

    <dependency>
    	<groupId>redis.clients</groupId>
    	<artifactId>jedis</artifactId>
    	<version>3.7.0</version>
    </dependency>
    
  • Erstellen Sie einen Redis-Verbindungspool

    @Slf4j
    @Configuration
    @Data
    public class JedisConfig {
    
        @Value("${redis.host}")
        private String host;
        @Value("${redis.port}")
        private Integer port;
        @Value("${redis.user}")
        private String user;
        @Value("${redis.password}")
        private String password;
    
        @Bean
        public JedisPool jedisPool() {
            JedisPool jedisPool = new JedisPool(this.host, this.port, StringUtils.isEmpty(user) ? null : this.user, StringUtils.isEmpty(this.password) ? null : this.password);
            log.info("jedis init success.");
            return jedisPool;
        }
    }
    
  • Passen Sie Ihre eigene Verarbeitungslogik für Redis-Abonnements an

    @Slf4j
    public class WsSubscriber extends JedisPubSub {
    
        // 当有消息发布到名称为 ws-channel 的渠道时会被该方法会监听到,服务A和服务B都是可以将听到这个方法内容的,我们需要在这里实现自己的逻辑
        @Override
        public void onMessage(String channel, String message) {
            log.info("jedis Subscriber channel={}, message={}", channel, message);
            // do sth... 这里可以调用websocket发送消息的方法就可以了,这时服务A和服务B都会去发送ws消息
        }
    
        // 当有订阅操作时会被该方法监听到
        @Override
        public void onSubscribe(String channel, int subscribedChannels) {
            log.info("jedis Subscriber channel={}, subscribedChannels={}", channel, subscribedChannels);
            super.onSubscribe(channel, subscribedChannels);
        }
    }
    
  • Abonnieren Sie zunächst einen Kanal, wenn der Dienst startet

@Component
@Slf4j
public class RedisSubscribeConfig {

    @Resource
    private ExecutorService executorService;

    @PostConstruct
    public void config() {
	// 这里另起一个线程来完成订阅操作是为了不影响服务的其他配置的初始化
        executorService.execute(() -> {
	    // 订阅 ws-channel 渠道,该渠道有发布消息时,用我们自定义的订阅类处理
            jedisPool.getResource().subscribe(new WsSubscriber(), "ws-channel");
        });
    }
}

Nach der obigen Verarbeitung ist das ursprüngliche Problem gelöst

 Ursprünglicher Link: Memory Journey

Supongo que te gusta

Origin blog.csdn.net/wuchenxiwalter/article/details/127342593
Recomendado
Clasificación