A Preliminary Study of WebSocket-Practical Experience Sharing

Posted May 27, 20205 min read

background

The author has always done ordinary CRUD business. Recently, the product manager suddenly had a whimsy and wanted to integrate a small game of answering questions in our current product. I still put forward some challenges to CRUD for many years. Today, the project is nearing completion of development. So far, we have summarized some of the gains from the project.

This article is mainly to summarize my actual combat experience in this project, so each module is based on the business side. After all, the technical design that is out of business is nonsense. If you want to find the code that can be used here, obviously there is no. And I assume that everyone already knows the basics. But at the end of the article, I added some good articles and blogs that I referred to in the process of doing projects. Welcome to read them.

Communication mode selection

This answer game can be regarded as an online game. In an online game, an important point is to maintain synchronization between the server and the client, and between the client and the client. The game industry already has two commonly used synchronization models:frame synchronization and state synchronization.

Frame synchronization

The King Glory, which we are all familiar with, uses frame synchronization. To put it simply, the server specifies a sequence. In a sequence, all clients must pass the actions sent in this sequence to the server. After receiving all the actions of the client, the server transfers these actions. Forward to all clients in the current battle. The client reproduces locally according to the received action signal.

It can be seen that frame synchronization has a very complete time axis control. When the number of clients is relatively small, the amount of message data that needs to be sent between each other is very small(only the actions of each client within this timing need to be transferred).

State synchronization

State synchronization means that the client sends operations to the server, and the server performs calculations and passes the results to other clients. Since the timing control is not strict, it is more suitable for turn-based games.

to sum up

The implementation strategies of frame synchronization and state synchronization are different, which makes them suitable for different game types, and this article does not go into details. We are going to do a small game of answering questions, which is a turn-based game, so we chose state synchronization.

Communication method

No matter which back-end design solution is adopted, we need to consider a question, how to send messages to the client through the server. The HTTP interface I was familiar with before, the request can only be initiated by the client, and the server can only respond to the client's request. So this time must introduce WebSocket.

In Java, the commonly used WebSocket frameworks are Spring and Netty. Considering that I have a better understanding of Spring, I adopted Spring.

Spring provides two different schemes to use WebSocket, one is STOMP, which is a specific message protocol, and it is very refreshing to use; the other is native WebSocket, although it is more complicated to use, but more flexible. I chose the latter because it is more convenient for him to interface with the front end of our applet.

Although WebSocket access may be more complicated, in fact, it provides a few functions:

  • Connection establishment
  • Message monitoring
  • Message sending

In other words, no matter what the business is, we need to summarize them into the above three functions.

Authentication

We need to authenticate any request on the network. Before WebSocket establishes a connection, there is a handshake process through HTTP request. In Spring, we can intercept the handshake request by setting org.springframework.web.socket.server.HandshakeInterceptor.

Therefore, we can bring authentication parameters when establishing a connection request, such as the generated Token or the user's account password. We can also do other things during authentication, such as checking the number of currently established connections and controlling the number of server connections to avoid exhausting the public network bandwidth.

Since this link is an HTTP request, it can respond to a specific HTTP status code to the front end.

Receive message

Listening to messages is relatively simple. Org.springframework.web.socket.WebSocketHandler # handleMessage provides a method for listening to messages. We can bring the corresponding action in the message, and call different business methods according to different actions.

Message sending

In the use of Spring's native WebSocket, if you want to send a message to the client, you must use org.springframework.web.socket.WebSocketSession. This is not a problem in itself, but it is troublesome in a distributed environment.

For example, I have two servers. Server 1 receives the message from client 1, and then responds. It needs to forward the message to client 2, but client 2 is currently connected to server 2, so how does server 1 reach the client Which server does terminal 2 stay connected to? Even if server 2 is known, how does server 1 forward the message to server 2?

WebSocket cluster solution in distributed environment

Now any business needs to consider the use and implementation of the distributed environment, a single point is always unreliable. On the HTTP server, we can persist the HTTP Session into Redis to implement a distributed Session, or completely abandon the Session and adopt a Token solution. But WebSocket Session cannot be saved to Redis. This is easy to understand. The WebSocket connection is actually stateful. The client and the server are long connections established, so even if I persist the session, it is impossible to obtain the session on another server. The corresponding client sends a message.

So how should it be solved?

The easiest way is to use broadcast messages. When the server finds that a message needs to be sent to the client, it directly sends a broadcast message. All servers can receive the broadcast message. When the server receives this message, it checks whether the client to be pushed keeps the connection with itself, if it is not maintained, it is directly discarded, and if it is maintained, it pushes the message to the corresponding client. The implementation of broadcast messages is also very simple, you can directly use MQ, the current mainstream MQ provides the function of broadcast messages, I use RocketMQ. You can also use Redis's subscription publishing function, the principle is actually the same.

Of course, broadcasting messages will obviously waste performance. A more aggressive strategy is to use Hash routing to allow clients with certain characteristics to connect to the specified server. When the server needs to send a message, it only needs to find the server that is connected to this client according to the routing table . However, such implementation requires the support and cooperation of operation and maintenance. If the request volume is not large or there are not many server nodes themselves, it is sufficient to use broadcast messages.

Further reading

Learn the basics of WebSocket: WebSocket Tutorial-Ruan Yifeng

If the WebSocket implementation is added to the Spring project: All implementation methods of springmvc + websocket

Distributed WebSocket implementation: distributed WebSocket cluster solution

Understand some game backend design ideas: Play Cards:Explore general game backend solutions