Many times we need to get the latest data in real time, but in the traditional sense, HTTP requests must be initiated by the client to the server, and the server then returns the corresponding data.
If we need to obtain real-time data, we have to poll it through HTTP and the client continuously initiates requests to the server.
Such continuous requests not only seriously increase the pressure on the server, but may also affect the timeliness of data due to network delay.
The following are two methods that can meet business needs well.
1. WebSocket
WebSocket is a protocol that HTML5 began to provide for full duplex communication on a single TCP link.
- advantage: Duplex communication
- shortcoming: It is necessary to specifically define the data protocol to parse the data stream, and some servers support is incomplete. For example, Java spring boot 2.1.2 only supports websocket 1.0 (up to 1.3)
1. Client code
python 3+ code
#python 3+ import threading import websocket class Client: def __init__(self,url): = url = None = True def on_message(self,response): = False print(response) def on_error(self,error): # print(ws) print(error) def on_close(self): = True print(ws) def on_open(self): print('open') def start_func(self): while : (True) = (, on_message=self.on_message, # on_data=on_data, on_error=self.on_error, on_open=self.on_open, on_close=self.on_close, ) .run_forever(ping_interval=60, ping_timeout=5) if __name__ == "__main__": cli = Client(url = 'wss:///websocket' ) t1 = (target=cli.start_func_zb) ()
javascript code
var ws = new WebSocket("wss://"); = function(evt) { ("Connection open ..."); ("Hello WebSockets!"); }; = function(evt) { ( "Received Message: " + ); (); }; = function(evt) { ("Connection closed."); };
2. Server code
from websocket_server import WebsocketServer class WSSocketObj: def __init__(self,host=None,port = 8131): = host if host else '127.0.0.1' = port # When a new client connects, it will prompt def new_client(self,client, server,): print("When a new client connects, it will prompt: %s" % client['id']) dd = 122 server.send_message_to_all("Hey all, a new client has joined us") # When the old client leaves def client_left(self,client, server): print("Client %s disconnected" % client['id']) # Receive client information. def message_received(self,client, server, message): print("Client(%d) said: %s" % (client['id'], message)) # server.send_message_to_all(message) #Send messages to all clients server.send_message(client, 'hello,client') # Send a message to the specified client def run_server(self): server = WebsocketServer(, ) server.set_fn_new_client(self.new_client) server.set_fn_client_left(self.client_left) server.set_fn_message_received(self.message_received) server.run_forever() if __name__ == '__main__': WSSocketObj().run_server()
2. SSE (Server-Sent Events, server sends events)
SSE(Server-sent Events) In a common sense, it is a technology based on HTTP and continuously sending data from the server to the client in the form of a stream. It is a lightweight alternative to WebSocket.
- advantage: Simple development, almost no difference from traditional http development, simple client development, standard support (EventSource)
- shortcoming: Compared with websocket, it can only communicate with simplex. After establishing a connection, it can only be sent to the client from the server and occupy one connection. If the client needs to communicate with the server, an additional connection needs to be opened.
1. Client code
python
#The first methoddef sse_client(): """ pip install sseclient-py For return criteria onlySSEFormat requests available Format:event: {EVENT}\nretry: 10000\ndata: {DATA}\n\n :return: """ import requests # res = ('get', url, json=data, stream=True, headers={'Accept': 'text/event-stream'}) client = (url, json=data, stream=True, headers={'Accept': 'text/event-stream'}) client = (client) for i in (): print() # The second waydef sse_with_requests(): headers = {"Accept": "text/event-stream"} r = (url, headers=headers, json=data, stream=True) = 'utf-8' for chunk in r.iter_content(chunk_size=None, decode_unicode=True): # Process the received data blocks print("Received:", chunk)
javascript
The first method:
//Judge whether SSE is supportedif('EventSource' in window){ //Initialize SSEvar url="http:localhost:8000/stream"; var source=new EventSource(url); // Open event will be triggered after the connection is successful=(event)=>{ ("Open SSE"); } // If there is no event field when the server sends information to the client, the message event will be triggered by default.=(event)=>{ var data=; $("body").append($("<p>").text(data)); } // Listen to like events('like',function(event){ var data=; $("body").append($("<p>").text(data)); },false); // An error event will be triggered when the connection is abnormal and it will automatically reconnect.=(event)=>{ (event); }
The second method: use the addEventListener method to add the corresponding event handling method
if () { // Create EventSource object to connect to the server const source = new EventSource('http://localhost:2000'); // Open event will be triggered after the connection is successful ('open', () => { ('Connected'); }, false); // If there is no event field when the server sends information to the client, the message event will be triggered by default. ('message', e => { (`data: ${}`); }, false); // Custom EventHandler, triggered when receiving a message with the event field slide ('slide', e => { (`data: ${}`); // => data: 7 }, false); // An error event will be triggered when the connection is abnormal and it will automatically reconnect. ('error', e => { if ( === ) { ('Disconnected'); } else if ( === ) { ('Connecting...'); } }, false); } else { ('Your browser doesn\'t support SSE'); }
EventSourceFrom the parent interfaceEventTarget
Inherited attributes and methods, it has 3 built-inEventHandler
Properties, 2 read-only properties, and 1 method:
EventHandlerproperty
-
Called when the connection is opened.
-
Called when a message without an event attribute is received.
-
Called during connection exception. Read-only properties
-
An unsigned short value representing the connection status. Possible values are CONNECTING (0), OPEN (1), or CLOSED (2).
-
The URL to the connection. method
-
()
Close the connection
EventSource object'sonmessage
The function of attributes is similar toaddEventListener( ‘ message ’ )
2. Server-side code (based on Flask)
import json import time from flask import Flask, request from flask import Response from flask import render_template app = Flask(__name__) def get_message(): """this could be any function that blocks until data is ready""" (1) s = (()) return (['Current time:' + s , 'a'], ensure_ascii=False) @('/') def hello_world(): return render_template('') @('/stream') def stream(): user_id = ('user_id') print(user_id) def eventStream(): id = 0 while True: id +=1 # wait for source data to be available, then push it yield 'id: {}\nevent: add\ndata: {}\n\n'.format(id,get_message()) return Response(eventStream(), mimetype="text/event-stream") if __name__ == '__main__': ()
Because SSE is an http request, but it is limited to a long link, the MIME type must be set to text/event-stream. The returned string.
Message format
The SSE data sent by the server to the browser must be UTF-8 encoded text;
Each message is sent consists of several messages, and each message is separated by \n\n. Each message consists of several rows
- Format
[field]:value\n
Among them, 4 fields are defined for messages in the specification
- id indicates id
- event Indicates the event type of the message
- Data field of data message
- retry The time for the reconnection of the client. Only integers are accepted, in milliseconds. If this value is not an integer, it will be automatically ignored
It should be noted that the id field is not necessary, and the server may not include the id field in the message, so that the sub-client will not have the Last-Event-ID property. So in order to ensure the data is reliable, we need to bring the id field on each message.
A very interesting thing is that the specification stipulates that messages starting with a colon will be regarded as comments, a normal comment (:\n\n
) It only takes up 5 characters for the server, but no events will be triggered when sent to the client, which is very friendly to the client. Therefore, comments are generally used to maintain long connections between servers and clients.
Notes on using
1. How to ensure data integrity in SSE
Every time the client receives a message, it will store the message id field as the internal property Last-Event-ID.
SSE supports disconnection and reconnection mechanism by default. When the connection is disconnected, the error event of EventSource will be triggered, and the reconnection will be automatically reconnected. When the connection is successful again, EventSource will send the Last-Event-ID property to the server as a request header, so that the server can perform corresponding processing based on this Last-Event-ID.
It should be noted here that the id field is not necessary, and the server may not include the id field in the message, so that the sub-client will not have the Last-Event-ID property. So in order to ensure the data is reliable, we need to bring the id field on each message.
2. Reduce overhead
In the draft SSE, it is mentioned that the MIME type transmission of "text/event-stream" should be automatically disconnected after being left to stand for 15 seconds. This mechanism will also exist in actual projects, but the disconnection time is not included in the standard.
In order to reduce server overhead, we can also disconnect and reconnect for purpose.
The simple way is to send a close message and specify a reconnection timestamp. The client closes the current connection when the closing event is triggered and creates a timer, and destroys the timer when the reconnection is reconnected.
function connectSSE() { if () { const source = new EventSource('http://localhost:2000'); let reconnectTimeout; ('open', () => { ('Connected'); clearTimeout(reconnectTimeout); }, false); ('pause', e => { (); const reconnectTime = +; const currentTime = +new Date(); reconnectTimeout = setTimeout(() => { connectSSE(); }, reconnectTime - currentTime); }, false); } else { ('Your browser doesn\'t support SSE'); } } connectSSE();
Summarize
The above is personal experience. I hope you can give you a reference and I hope you can support me more.