An overview of the

This is the first part of the third WebSocket story series, which will delve into the Spring source code step by step, and the dry stuff will be released in the next few articles. The WebSocket story series is planned as a five-part, step-by-step introduction to WebSocket and how to quickly build and use the capabilities WebSocket provides in Springboot. This series is planned to include the following articles:

Part 1: What WebSocket is and what it Does Part 2: How to Use STOMP to Quickly Build WebSocket broadcast message Patterns in Spring In Springboot, how to use WebSocket and STOMP to quickly build point-to-point messaging mode (2) in Springboot, how to implement custom WebSocket message proxy in web chat rooms

The main line of this article

The simplest pattern of STOMP implemented by Spring in the previous article, via the @sendto annotation, sends a message to a specified message broker, which will be received by any client that has subscribed to the broker. As part 3 of this series, I’ll cover the implementation details in three separate installments, starting with @sendto and @Sendtouser, and delving into Spring’s key code for sending WebSocket messages. Pave the way for the next article on peer-to-peer messaging.

This article is suitable for readers

Students who want to learn about STOMP, the details of Spring’s internal code, and how to build WebSocket services using Springboot.

High energy warning ahead

This is a relatively large amount of code, so I’ll try to explain it in detail.

The magic @sendto and @sendtouser

In this article, we’ll cover the story behind these two notes.

@SendTo

In the previous article, we used the @sendto annotation to push the return value of the method to the message broker, which broadcasts it to the subscription path. There is no detailed description of how messages are processed by the Spring framework and then broadcast out. The key code from the previous section:

    @MessageMapping("/hello") // Use MessageMapping to indicate that all messages sent to the destination "/hello" will be routed to this method for processing. @sendto ("/topic/greetings"// Use the SendTo annotation to indicate that the result returned by this method will be sent to its specified destination, "/topic/greetings". public Greeting greeting(HelloMessage message) throws Exception { Thread.sleep(1000); // Simulate processing delayreturn new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!"); // Based on the incoming information, a welcome message is returned.Copy the code

The return value from the above method is broadcast to the /topic/ Greetings subscription path, and the client receives the message whenever it subscribs to this path. Spring processing SimpleBrokerMessageHandler is the main classes of the news, when you need to send broadcast messages, eventually will invoke the sendMessageToSubscribers () method:

Broker
Session
destination
Broker
message

So if I just want to use WebSocket to send a query request to the server, and then the server you give me the query result, other users do not need you broadcast push, simple point, is I request, you push to me. What should I do about it? Yes, @sendtouser will fix that.

@SendToUser

Here is the code snippet:

    @MessageMapping("/hello") // Use MessageMapping to indicate that all messages sent to the destination "/hello" will be routed to this method for processing. @sendtouser ("/topic/greetings"// Use the SendToUser annotation to indicate that the result returned by this method will be sent to the destination of the user requesting it. // The passed parameter Message is the Message sent from the client and is automatically bound. public Greeting greeting(HelloMessage message) throws Exception { Thread.sleep(1000); // Simulate processing delayreturn new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!"); // Based on the incoming information, a welcome message is returned.Copy the code

As you can see, I’ve just modified the annotation. Based on our sample code from the previous section, we start the program and try it out. We don’t get a message back. Let’s dive into the key points of code implementation.

The implementation details behind @sendtouser

First, before we look at the details of the code, we should do a static analysis. Based on what we’ve covered before, it’s easy to think of:

1. The establishment of the Spring WebSocket channel starts from the first handshake of THE Http protocol. After the handshake is successful, the WebSocket channel between the client and the server is opened, that is, the client and the server maintain communication through a Session. It’s like building a pipeline, you have content, you send it to me, I send it to you. 2. The greeting method above is actually an opportunity for the framework to provide developers with an opportunity to process the client’s requests. Developers can process the information according to the business needs and then return the desired response results to the client. So when this method returns, that’s when the response message is sent back from the server to the client.

Based on the above two basic conclusions, we began to analysis code, the first is beginning after the return, look at the code run where: AbstractMethodMessageHandler. HandleMatch method in Java

destination
/hello
GreetingController
handleMatch
invoke
GreetingController
greeting
greeting
handleRetureValue

Along the way, here we are an important class, SendToMethodReturnValueHandler. Java

As the name of the class indicates, it is the class that handles sendto-related annotations. When the sendto-annotated method returns, the handleReturnValue method in this class is called for processing. The code flow is very clear, you can refer to the comments in the picture.

Continue tracing the send logic

Two points worth following:

1. In the SendToUser branch, messagingTemplate is used for both broadcast and unbroadcast messages. What is this messagingTemplate? 2. The same method, convertAndSendToUser, is called for both broadcast and non-broadcast message sending. The difference is that when it is not broadcast, there is an extra sessionId parameter. How to understand this method and this parameter?

Take this question to follow, or in SendToMethodReturnValueHandler. Java in this class:

Here we come to a new class, SimpMessagingTemplate. It implements the convertAndSendToUser method, which we need to cover in detail because it’s a small but critical piece of code:

public void convertAndSendToUser(String user, String destination, Object payload, @Nullable Map<String, Object> headers, @Nullable MessagePostProcessor postProcessor) throws MessagingException {
    Assert.notNull(user, "User must not be null");
    user = StringUtils.replace(user, "/"."%2F");
    destination = destination.startsWith("/")? destination :"/" + destination;
    super.convertAndSend(this.destinationPrefix + user + destination, payload, headers, postProcessor);
}

Copy the code

Introduce the input parameters:

Destination: Payload :Object type. It identifies the return value of a method defined in the Controller. Here is the return value of the greeting method in the GreetingController class: headers: the message header that returns the message postProcessor: Null\

First of all, into the parameter calibration and normalization, the key in the last line, into the office made a string concatenation, the original destination stitching for/user/userID/topic/’t userID is client SessionID. Stitching results destination = “/ user/au3ev44r/topic/’t”. Ok, next, let’s look at this method:

AbstractMessageSendingTemplate < D >. In Java:

public void convertAndSend(D destination, Object payload, @Nullable Map<String, Object> headers, @Nullable MessagePostProcessor postProcessor) throws MessagingException { Message<? > message = this.doConvert(payload, headers, postProcessor); this.send(destination, message); }Copy the code

It combines the Body information it will send with the Header information to get the Message Message. After that, the send method is called. After a series of processing methods of the circulation, and finally reached the UserDestinationMessageHandler handleMessage methods in class.

resolveDestination
/user
Here will besourceDestinationConverted into/topic/greetings-userau3ev44r.userau3ev44r,userIs the keyword,au3ev44risSessionIDThis uniquely matches the user with the subscription path

targetDestinations
SimpMessageTemplate
SimpleBrokerMessageHandler
SendTo
/topic/greetings-userau3ev44r
greeting

conclusion

In the example above, we explained in detail how a client message arrived at the server, through the code flow, the entire process of finding the following two key parameters.

  • Destination address of the message
  • I hope you can calm down and read this part of the code carefully. It will be very helpful for the understanding of the following articles, but also improve your understanding of Spring design concept. Learn more about Spring’s implementation details.

The code covered in this article

SpringWebSocket Github

Welcome to continue to follow

Xiaoming produced, must be a boutique

Welcome to pay attention to xNPE technology forum, more original dry goods daily push.