1. 后端主动推送

主动推送时随机选择一台服务器A发起推送,如果用户甲是和服务器B建立连接的,那么服务器A发出的推送就到不了前端。

解决方案

Nginx配置负载均衡,后台微服务主动推送时不做负载分发,而是所有的服务器都推送

2. 前端心跳轮询

由于前端和后端是长连接,由于前端隔一分钟会发送一次请求,来确认长连接有没有断,断了的话会进行重连,如果请求被分发到了另一台服务器上的话,这样消息就会发送失败,前台就不能确定是请求分发的问题还是真的是长连接断了。

解决方案

Nginx固定IP路由

3. 代码示例

  1. Nginx配置

    web.conf
    upstream ms-websocket {
        server  192.168.1.1:8091;
        server  192.168.1.2:8091;
        ip_hash;
    }
    location = /notification/socket {
            proxy_pass              http://ms-websocket;
            proxy_redirect          off;
            proxy_set_header        Host $server_name:$server_port;
            proxy_set_header        X-Real-IP $remote_addr;
            proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
            add_header              X-Frame-Options "SAMEORIGIN";
            proxy_http_version      1.1;
            proxy_set_header        Upgrade $http_upgrade;
            proxy_set_header        Connection "upgrade";
            proxy_next_upstream     error timeout invalid_header http_500 http_502 http_503 http_504;
            proxy_connect_timeout   60;
            proxy_send_timeout      60;
            proxy_read_timeout      60;
            access_log              off;
        }
  2. java代码

    pom.xml
    <dependency>
        <groupId>org.springframework.kafka</groupId>
        <artifactId>spring-kafka</artifactId>
    </dependency>
    producer.java
    @Component("webSocketKafkaProducer")
    @Slf4j
    public class WebSocketKafkaProducer {
    
        @Autowired
        private KafkaTemplate<String, Object> kafkaTemplate;
    
        public void sendMessage(String content) {
            log.debug("发送消息:{}",content);
            kafkaTemplate.send("websocket_push", content);
        }
    }
    cosumer.java
    @Component
    @Slf4j
    public class WebSocketKafkaConsumer {
        @Autowired
        private SimpMessageSendingOperations messagingTemplate;
    
        @KafkaListener(
                topics = {"websocket_push"}
                , groupId = "${spring.cloud.client.ip-address}.${server.port}" //websocket每个实例使用不同的group-id,防止漏掉非当前实例的连接用户
        )
        public void websocket(String content){
            log.debug("监听到消息:{}", content);
            //具体发送websocket
            String destination = "/websocket/listener" + "/" +"username";
            log.info("向监听地址为[{}]发送消息:{}", destination, content);
            this.messagingTemplate.convertAndSend(destination, content);
        }
    
    }