The first week of the New Year is over, and it’s time to start making new work plans. The main project I am responsible for is the data visualization platform, and if the server can push the relevant notification to the page end, many functions can be optimized. Since the Node end of the project has been officially put into use, the front end has its own server, so it is naturally much easier to do things.

Technical selection: SSE (Server-sent Events) or WebSocket

A few years ago, servers did not have the ability to actively push, mainly through polling to achieve a similar ability to server push. Now there is no need for such trouble, polling can only be used as a backward compatibility scheme, the current mainstream server push is to use SSE or WebSocket to achieve. The comparison is as follows:

Whether based on the new protocol Two-way communication or not Whether cross-domain is supported Access to the cost
SSE No (Http) No (Server unidirectional) No (Firefox supports cross-domain) low
WebSocket Is (ws) is is high

What needs a little explanation is the cost of access. SSE is a relatively lightweight protocol, (Node) code implementation is relatively simple, and WebSocket is a more complex protocol, although there are class libraries can be used, maybe the page side of the two code amount, but the server side is a lot more complex. At the same time, to implement WebSocket, it is necessary to create another service, but SSE does not need.

After comparison, SSE and WebSocket have a general understanding. The requirement of the project is to send notifications to the server, and the real-time synchronization function may be needed in the future. Therefore, SSE is selected based on the actual situation of the project and the access cost.

Finally, take a look at browser support for reference:

IE let it go, daily does not support ~ other browsers or green, support is quite high.

The sample

The Node end

The project uses Egg as the framework and Koa2 as the underlying, so Koa2 is used as the example. The key code of Node side is as follows:

app.use(async (ctx) => { const { res, request: { url } } = ctx; Res.writehead (200, {' content-type ': 'text/event-stream', // server says next to send an event stream}); let stream = new PassThrough(); let i = 0; let timer = setInterval(() => { if (i === 5) { stream.write('event: pause\n'); // Event type} else {stream.write('event: test\n'); // Event type} stream.write(' id: ${+new Date()}\n '); // Message ID stream.write(' data: ${I}\n '); // Stream.write (' Retry: 10000\n'); // stream. Write ('\n\n'); I++; }, 1000); stream.on('close', function() { console.log('closed.') clearInterval(timer); }) ctx.body = stream; });Copy the code

The server tells the client that the type returned is text/event-stream. If you look at the MDN document, you can see that the event stream is just a simple text data stream, and the text should be encoded in UTF-8 format. Each message is followed by an empty line delimiter. Comment lines that begin with a colon are ignored.

After that comes the body of the message, and although the example uses setInterval to simulate sending a push over and over again, it’s ok to use any condition to trigger a push. Stream. write is called 5 times, corresponding to each field in the specification, as understood below:

  • eventIs the event type of the message. The clientEventSourceCan be passed throughaddEventListenerListen to the news. This field can be omitted and triggered by the clientmessageEvents.
  • idIs the event ID. As the attribute value of the last event ID inside the client, it is used for reconnection and cannot be omitted.
  • dataFor the data field of the message, simply put, the client listens to the time after passinge.dataI got the data.
  • retryYou can omit this parameter for reconnection time.
  • Finally, the notification was terminated\n\nCannot be omitted. All field names except those specified above are ignored.

A more detailed explanation can be found in the MDN documentation. One small detail to note is that the SSE draft states that the MIME type transmission of “text/event-stream” should be automatically disconnected after 15 seconds of rest. But when I tested it (only with Chrome), I found that the browser did not disconnect from the client, even after resting for more than 15 seconds. A number of articles have been consulted, all of which suggest maintaining a heartbeat mechanism for sending \n\n. I think this helps improve the robustness of the client application, but it is not necessary.

Finally, the close event of the listening event stream is used to close the link. After testing, it is found that the server’s close event can be triggered either by the client calling the close method (examples below ~) or by an abnormal end, including closing a window, closing a program, etc.

The client

The client code is simpler as shown below:

const source = new EventSource('http://localhost:3000/test');

source.addEventListener('open', () => {
  console.log('Connected');
}, false);

source.addEventListener('message', e => {
  console.log(e.data);
}, false);

source.addEventListener('pause', e => {
  source.close();
}, false);
Copy the code

The front end should be familiar with this kind of code, where everything is triggered by events and the corresponding code is executed according to different events. With a little explanation of the properties and methods that An EventSource has, you’ll be happy to use it.

The EventSource has three default events:

  • open: is called when the connection is opened.
  • message: Called when a message is received without the Event attribute.
  • error: is called when an error occurs.

Two read-only properties:

  • readyState: indicates the connection status. Possible values are CONNECTING (0), OPEN (1), or CLOSED (2).
  • url: indicates the URL of the connection.

One way:

  • close: Close the connection after the call (as mentioned above).

A more detailed explanation can be found in the MDN documentation

summary

So far is the brief introduction of server SSE, as can be seen, SSE development is relatively simple, access cost is very low. But this is not to say that WebSocket is bad, regardless of the actual situation to talk about business is rogue. In addition, the above code is just a demonstration and can be further optimized. For example, in order to reduce server overhead, you can establish a set of mechanisms for purposeful disconnection and reconnection, etc., we can achieve their own.

The relevant code has been thrown to Github, welcome to check.

Thank you to see the adult here, easy to know, I hope this article is helpful to you ~ thank you!

The resources

20 lines of code to write a data push service

Use the server to send events

EventSource