Spring Cloud Microservices Using webSocket

  • 2021-10-11 18:43:52
  • OfStack

webSocket

webSocket Long Connection is a protocol for full duplex communication over a single tcp connection, allowing bidirectional data push. 1 restful API provided by microservices only respond to front-end requests. Using webSocket, the back end can actively push messages to the front end.

Gateway configuration

The gateway components of spring cloud are zuul and getway

getway


base:
  config:
    nacos:
      nacoshost: localhost
      port: 8848

spring:
  application:
    name: gateway
  main:
    allow-bean-definition-overriding: true
  cloud:
    nacos:
      discovery:
        server-addr: ${base.config.nacos.nacoshost}:${base.config.nacos.port}
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        #  websocket
        - id: CLOUD-WEBSOCKET
          uri: lb:ws://cloud-websocket
          predicates:
            - Path=/cloud-websocket/**
server:
  port: 8888

Pay attention to adding ws protocol when configuring gateway.

zuul

zuul can only manage http requests. zuul is not recommended to manage websocket connections. Direct connection is recommended.

Server side

Add an maven dependency


<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>

Add webSocket configuration


@Configuration
@EnableWebSocket
public class WebsocketConfiguration implements WebSocketConfigurer {
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        // webSocket Channel 
        //  Specify the processor and path 
        registry.addHandler(new WebSocketHandler(), "/websocket")
                //  Specify a custom interceptor 
                .addInterceptors(new WebSocketInterceptor())
                //  Allow cross-domain 
                .setAllowedOrigins("*");
        // sockJs Channel 
        registry.addHandler(new WebSocketHandler(), "/sock-js")
                .addInterceptors(new WebSocketInterceptor())
                .setAllowedOrigins("*")
                //  Open sockJs Support 
                .withSockJS();
    }
}

Add processor


package com.auexpress.cloud.handler;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.StringUtils;
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description
 * @ClassName WebSocketHandler
 * @Author HYSong
 * @date 2020.04.14 10:08
 */
public class WebSocketHandler extends AbstractWebSocketHandler {
    /**
     *   Storage sessionId And webSocketSession
     *   It should be noted that, webSocketSession No parameterless construction is provided, so it cannot be serialized, and it cannot be passed through redis Storage 
     *   In a distributed system, if you want to find other ways to achieve it, webSocketSession Sharing 
     */
    private static Map<String, WebSocketSession> sessionMap = new ConcurrentHashMap<>();
    private static Map<String, String> userMap = new ConcurrentHashMap<>();

    /**
     * webSocket Called after the connection is created 
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        //  Get parameters 
        String user = String.valueOf(session.getAttributes().get("user"));
        userMap.put(user, session.getId());
        sessionMap.put(session.getId(), session);
    }

    /**
     *  When a message is received, it calls 
     */
    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        JSONObject jsonObject = JSONObject.parseObject(message.getPayload().toString());
        String content = jsonObject.getString("content");
        String targetAdminId = jsonObject.getString("targetId");
        if("0".equals(targetAdminId)){
            //   Push it to everyone 
            userMap.forEach((key,value)->{
                try {
                    this.sendMessage(key,content);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        }else{
            sendMessage("1", content);
        }
    }

    /**
     *  A connection error calls the 
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) {
        sessionMap.remove(session.getId());
    }

    /**
     *  Connection closure calls 
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        sessionMap.remove(session.getId());
    }

    @Override
    public boolean supportsPartialMessages() {
        return false;
    }

    /**
     *  Back-end sending messages 
     */
    public void sendMessage(String user, String message) throws IOException {
        String sessionId = userMap.get(user);
        if (StringUtils.isEmpty(sessionId)) {
            return;
        }
        WebSocketSession session = sessionMap.get(sessionId);
        if (session == null) {
            return;
        }
        session.sendMessage(new TextMessage(message));
    }
}

Add an interceptor


package com.auexpress.cloud.interceptor;

import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.HandshakeInterceptor;

import java.util.Map;

/**
 * @Description
 * @ClassName WebSocketInterceptor
 * @Author HYSong
 * @date 2020.04.14 10:09
 */
public class WebSocketInterceptor implements HandshakeInterceptor {
    /**
     * handler Called before processing ,attributes Property ends up in the WebSocketSession Li ,
     *  Possibly through webSocketSession.getAttributes().get(key Value ) Obtain 
     */
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) {
        if (request instanceof ServletServerHttpRequest) {
            ServletServerHttpRequest serverHttpRequest = (ServletServerHttpRequest) request;
            //  Gets the parameters carried by the request path 
            String user = serverHttpRequest.getServletRequest().getParameter("user");
            attributes.put("user", user);
            return true;
        } else {
            return false;
        }
    }

    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) {

    }
}

Related articles: