Which of these 7 solutions to realize web real-time message push have you used?

I have a friend~

I made a small broken website, and now I want to implement a function of pushing web messages in the station. Yes, it is the little red dot in the picture below, a very commonly used function.

d0f0da20a37076322505a78fb9e79cbc.png

But he hasn’t figured out how to do it yet. Here I helped him sort out several solutions and implemented them simply.

2b8a67b52c770552267e7456827091c2.png

What is message push (push)

There are many push scenarios. For example, if someone pays attention to my official account, I will receive a push message to attract me to click to open the app.

Message push (push) usually refers to the active message push of the user’s current web page or mobile device APP by the operator of the website through some tool.

Message push is generally divided into web message push and mobile message push.

ea9ac1a1d2e073ddcd823d041a5e1980.png

The one above belongs to the mobile message push, and the common message push on the web, such as internal messages, the number of unread emails, the number of monitoring alarms, etc., is also widely used.

8ce807f16cb488346e7fcc50adc8ee65.png

Before the specific implementation, let’s analyze the previous requirements again. In fact, the function is very simple. As long as an event is triggered (actively sharing resources or actively pushing messages in the background), the notification red dot on the web page will be displayed in real time + 1 will do.

Usually there are several message push tables on the server side, which are used to record different types of messages pushed by users triggering different events, and the front end actively queries (pulls) or passively receives (pushes) the number of all unread messages from users.

2c921d0ca4d076c465c40aa503cc018b.png

Message push is nothing more than push (push) and pull (pull), let’s understand one by one below.

Short Polling

Polling (polling) should be the easiest way to implement message push schemes. Here we divide polling into short polling and long polling .

Short polling is easy to understand. At a specified time interval, the browser sends a HTTP request to the server, and the server returns unread message data to the client in real time, and the browser renders and displays it.

A simple JS timer can do it, request the unread message count interface every second, and display the returned data.

setInterval(() => {
  // method request
  messageCount().then((res) => {
      if (res.code === 200) {
          this.messageCount = res.data
      }
  })
}, 1000);

The effect is still possible. Although the implementation of short polling is simple, the disadvantages are also obvious. Since the push data does not change frequently, no matter whether there is a new message at the back end at this time, the client will make a request, which will inevitably cause great damage to the server. High stress, waste of bandwidth and server resources.

7e76369eee04a58c73d72dddcd9a86ee.gif

Long polling

Long polling is an improved version of the above short polling. While minimizing the waste of server resources, it ensures the relative real-time performance of messages. Long polling is widely used in middleware, such as Nacos and apollo configuration center, message queue kafka, RocketMQ.

This time I used the apollo configuration center to implement long polling, and applied a class DeferredResult, which passed Spring after servelet3.0 An asynchronous request mechanism provided by encapsulation means to delay the result.

d0b3f23d94ba48da400bf0c2e7b0501d.png

DeferredResult can allow the container thread to quickly release the occupied resources without blocking the request thread, so as to accept more requests to improve the throughput of the system, and then start an asynchronous worker thread to process the real business logic and complete the call DeferredResult.setResult(200) submits the response result.

Below we use long polling to implement message push.

Because an ID may be monitored by multiple long polling requests, I use the Multimap structure provided by the guava package to store long polling. One key can correspond to multiple values . Once a key change is detected, all corresponding long polls will respond. The front end gets the status code of non-request timeout, knows the data change, actively queries the unread message count interface, and updates the page data.

@Controller
@RequestMapping("/polling")
public class PollingController {

    // Store the long polling collection that monitors a certain Id
    // thread synchronization structure
    public static Multimap<String, DeferredResult<String>> watchRequests = Multimaps.synchronizedMultimap(HashMultimap.create());

    /**
     * Official account: Programmer Xiaofu
     * Set up monitoring
     */
    @GetMapping(path = "watch/{id}")
    @ResponseBody
    public DeferredResult<String> watch(@PathVariable String id) {
        // delay object setting timeout
        DeferredResult<String> deferredResult = new DeferredResult<>(TIME_OUT);
        // Remove the key when the asynchronous request is completed to prevent memory overflow
        deferredResult.onCompletion(() -> {
            watchRequests. remove(id, deferredResult);
        });
        // register long polling request
        watchRequests. put(id, deferredResult);
        return deferredResult;
    }

    /**
     * Official account: Programmer Xiaofu
     * change data
     */
    @GetMapping(path = "publish/{id}")
    @ResponseBody
    public String publish(@PathVariable String id) {
        // Data change Take out all the long polling requests of the monitoring ID and respond to them one by one
        if (watchRequests. containsKey(id)) {
            Collection<DeferredResult<String>> deferredResults = watchRequests. get(id);
            for (DeferredResult<String> deferredResult : deferredResults) {
                deferredResult.setResult("I updated" + new Date());
            }
        }
        return "success";
    }

When the request exceeds the set timeout period, an AsyncRequestTimeoutException exception will be thrown. Here, you can directly use @ControllerAdvice to capture and return it globally. The front end will start again after obtaining the agreed status code Long polling request, repeated calls like this.

@ControllerAdvice
public class AsyncRequestTimeoutHandler {

    @ResponseStatus(HttpStatus.NOT_MODIFIED)
    @ResponseBody
    @ExceptionHandler(AsyncRequestTimeoutException. class)
    public String asyncRequestTimeoutHandler(AsyncRequestTimeoutException e) {
        System.out.println("Asynchronous request timed out");
        return "304";
    }
}

Let’s test it. First, the page initiates a long polling request /polling/watch/10086 to monitor the message change, the request is suspended, and the data is not changed until it times out, and the long polling request is initiated again; Then manually change the data /polling/publish/10086, the long polling gets a response, and the front-end processing business logic is completed and the request is initiated again, and so on.

7f35377902d3443e9df6685552647993.gif

Compared with short polling, long polling has improved a lot in performance, but it still generates more requests, which is its imperfection.

iframe stream

The iframe flow is to insert a hidden