4.1.1.1.1. Spring WebSocket

4.1.1.1.1.1. SockJS

http://host:port/myApp/myEndpoint/{server-id}/{session-id}/{transport}

  • {server-id} - useful for routing requests in a cluster but not used otherwise.

  • {session-id} - correlates HTTP requests belonging to a SockJS session.

  • {transport} - indicates the transport type, e.g. “websocket”, “xhr-streaming”, etc.

4.1.1.1.1.1.1. Enable SockJS

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(myHandler(), "/myHandler").withSockJS();
    }

    @Bean
    public WebSocketHandler myHandler() {
        return new MyHandler();
    }

}

关键代码:withSockJS()

4.1.1.1.1.2. STOMP

spring-messaging和spring-websocket模块中提供了STOMP over WebSocket支持。 一旦有了这些依赖关系,就可以使用SockJS Fallback通过WebSocket公开STOMP端点。

import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        // /portfolio是WebSocket(或SockJS)所指向的终结点点HTTP URL,客户端需要连接此地址才能进行WebSocket握手。
        registry.addEndpoint("/portfolio").withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        //  STOMP messages whose destination header begins with /app are routed to @MessageMapping methods in @Controller classes.
        // 其目标标头以/app开头的STOMP消息被路由到@Controller类中的@MessageMapping方法。
        config.setApplicationDestinationPrefixes("/app");
        // Use the built-in message broker for subscriptions and broadcasting and route messages whose destination header begins with /topic `or `/queue to the broker.
        // 使用内置的消息代理进行订阅和广播,以及将目标标头以/topic或/queue开头的消息路由到代理。
        config.enableSimpleBroker("/topic", "/queue");
    }
}

@Controller
public class GreetingController {

    @MessageMapping("/greeting") {
    public String handle(String greeting) {
        return "[" + getTimestamp() + ": " + greeting;
    }
}

以上代码可以在客户端使用SockJS连接。

var socket = new SockJS("/spring-websocket-portfolio/portfolio");
var stompClient = webstomp.over(socket);

stompClient.connect({}, function(frame) {
}
var socket = new WebSocket("/spring-websocket-portfolio/portfolio");
var stompClient = Stomp.over(socket);

stompClient.connect({}, function(frame) {
}

对于内置的简单代理,/topic和/queue前缀没有任何特殊含义。 它们仅是区分发布订阅消息传递和点对点消息传递的约定(即,许多订户与一个消费者)。 使用外部代理时,请检查代理的STOMP页面以了解其支持哪种STOMP目标和前缀。

4.1.1.1.1.2.1. Flow of Messages

无外部Message Broker的情况:

https://docs.spring.io/spring/docs/current/spring-framework-reference/images/message-flow-simple-broker.png

有外部Message Broker的情况:

https://docs.spring.io/spring/docs/current/spring-framework-reference/images/message-flow-broker-relay.png

前面两个图之间的主要区别是使用“代理中继”将消息通过TCP传递到外部STOMP代理,以及将消息从代理传递到订阅的客户端。

当从WebSocket连接接收到消息时,它们被解码为STOMP帧,转换为Spring消息表示形式,并发送到clientInboundChannel进行进一步处理。 例如,目标标头以/ app开头的STOMP消息可以路由到带注释的控制器中的@MessageMapping方法,而/ topic和/ queue消息可以直接路由到消息代理。

处理来自客户端的STOMP消息的带注释的@Controller可以通过brokerChannel将消息发送到消息代理,并且代理通过clientOutboundChannel将消息广播给匹配的订户。 相同的控制器还可以响应HTTP请求执行相同的操作,因此客户端可以执行HTTP POST,然后@PostMapping方法可以将消息发送到消息代理,以广播到订阅的客户端。

4.1.1.1.1.3. Enable a STOMP broker relay

enableStompBrokerRelay

@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
    registry.enableStompBrokerRelay("/topic", "/queue")
            .setRelayHost("localhost")      // rabbitmq-host服务器地址
            .setRelayPort(61613)     // rabbitmq-stomp 服务器服务端口
            .setClientLogin("guest")   // 登陆账户
            .setClientPasscode("guest");  // 登陆密码
    //定义一对一推送的时候前缀
    registry.setUserDestinationPrefix("/user/");
    //客户端需要把消息发送到/message/xxx地址
    registry.setApplicationDestinationPrefixes("/message");
    LOGGER.info("init rabbitmq websocket MessageBroker complated.");
}

STOMP的消息根据前缀的不同分为三种。

  • 以/app开头的消息会被路由到带有@MessageMapping或@SubscribeMapping注解的方法中。

  • 以/topic或/queue开头的消息都会被发送到STOMP代理中。

根据你所选择都STOMP代理不同,目的地都可选前缀也会有所限制;以/usr开头都消息会将消息重路由到某个用户独有的目的地商。

https://images2018.cnblogs.com/blog/1153954/201805/1153954-20180506222500876-639399590.png